почему результаты считывания аппаратных счетчиков с помощью papi зависят от позиции PAPI_library_init?

#c #caching #x86 #papi

#c #кэширование #x86 #papi

Вопрос:

Я использую библиотеку PAPI для считывания аппаратных счетчиков. Я заметил, что порядок вызова инициализации PAPI_library_init (PAPI_VER_CURRENT) влияет на результаты, которые я получаю. Моя инициализация и чтение массива выглядят следующим образом:

 int retval;

/*
     retval = PAPI_library_init(PAPI_VER_CURRENT);

     if (retval != PAPI_VER_CURRENT) {
       fprintf(stderr, "PAPI library init error!n");
       exit(1);
     }
*/

      for(int i=0; i < arr_size; i  ){
        array[i].value = 1;
        //_mm_clflush(amp;array[i]); flushing does not make difference. 
      }
      _mm_mfence();


      for(int i=0; i < arr_size; i  ){
        temp = array[i].value ;
      }
      _mm_mfence();



      retval = PAPI_library_init(PAPI_VER_CURRENT);

      if (retval != PAPI_VER_CURRENT) {
        fprintf(stderr, "PAPI library init error!n");
        exit(1);
      }
  

Я полагаю, что необходимость второго цикла для чтения массива связана с протоколом coherence, но здесь это не должно иметь большого значения. После этого я добавляю собственные события MEM_LOAD_RETIRED в набор событий, который я хочу прочитать, и я использую PAPI_read в этом третьем цикле (я читаю его до и после цикла и в конце печатаю разницу) :

 for(int i=0; i < arr_size; i  ){
       temp = array[i].value ;
     } 
  

где arr_size равен 1000, а размер каждого элемента массива равен 64 байтам (равен строке кэша). Я отключил все средства предварительной выборки . Я компилирую с флагом gcc -O3 для оптимизации и библиотекой -lpapi. с помощью этого кода для третьего цикла я получаю:

L1_HIT: 64, L1_MISS: 1011, L2_HIT: 15, L2_MISS: 996.

Однако, если я раскомментирую PAPI_library_init перед инициализацией массива и прокомментирую его после, я получу следующие результаты:

L1_HIT: 73, L1_MISS: 1004, L2_HIT: 990, L2_MISS: 14.

Я тестирую это на сервере skylake, размеры кэша составляют:

 L1d cache:             32K
L1i cache:             32K
L2 cache:              1024K
L3 cache:              22528K
  

Теперь я немного смущен, почему инициализация papi влияет на эти результаты. это L2 попадает и пропускает это изменение. Все, что мне нужно, это третий цикл, и влияние первых двух циклов на счетчики, я полагаю, не учитывается.

Итак, любой намек на это был бы полезен, поскольку во всей документации говорится следующее: «PAPI_library_init() инициализирует библиотеку PAPI. Это должно быть вызвано перед использованием любых низкоуровневых функций PAPI. Если ваше приложение использует потоки, PAPI_thread_init (3) также должен быть вызван перед выполнением любых вызовов библиотеки, отличной от PAPI_library_init().»

Комментарии:

1. Вы можете проверить без _mm_clflush(amp;array[i]); ? Можете ли вы проверить меньшие размеры массива, такие как 500 и 300 элементов вместо 1000? Вы уточнили объявление массива с помощью volatile , чтобы компилятор не оптимизировал нагрузку на -O3 ?

2. @HadiBrais Да, у меня энергозависимый массив, поэтому чтение во временном режиме не будет оптимизировано. Я проверю без промывки. теперь я уверен, что получу тот же результат, но я постараюсь

3. @HadiBrais да, у меня такое же поведение без clflush. Просто чтобы убедиться, в этом первом цикле, когда я инициализирую array, он записывает первый раз в элемент, а затем удаляет эту строку кэша, верно? Я не подозревал об этом раньше, но я протестировал это сегодня, и, к моему удивлению, это наблюдение, которое я получил. Я читал об обнулении по требованию в другом посте, который, как я понял, был причиной случая RFO, верно? Так это тоже как-то связано с удалением строки кэша?

4. для 500 инициализация PAPI после дает результат: L1_HIT: 62, L1_MISS: 513, L2_HIT: 16, L2_MISS: 497, а инициализация до: L1_HIT: 67, L1_MISS: 510, L2_HIT: 377, L2_MISS: 133. похоже, поведение такое же. То же самое и для 300. После инициализации: L1_HIT: 83, L1_MISS: 304, L2_HIT: 6, L2_MISS: 298. инициализация перед массивом: L1_HIT: 82, L1_MISS: 302, L2_HIT: 117, L2_MISS: 185,

5. Это как если бы PAPI_library_init вызывал удаление всех строк L2. Глядя на исходный код, я не понимаю, почему это могло произойти. Как насчет MEM_LOAD_RETIRED. L3_MISS и MEM_LOAD_RETIRED. L3_HIT?