Неожиданно большое количество пропусков TLB при простом профилировании PAPI на x86

Я использую высокоуровневый API PAPI для проверки промахов TLB в простой программе, проходящей через массив, но вижу большие числа, чем ожидалось.

В других простых тестовых случаях результаты кажутся вполне разумными, что заставляет меня думать, что результаты реальны, а дополнительные промахи связаны с аппаратной предварительной выборкой или чем-то подобным.

Может ли кто-нибудь объяснить цифры или указать мне на ошибку в использовании PAPI?

int events[] = {PAPI_TLB_TL};
long long values[1];
char * databuf = (char *) malloc(4096 * 32);

if (PAPI_start_counters(events, 1) != PAPI_OK) exit(-1);
if (PAPI_read_counters(values, 1) != PAPI_OK) exit(-1); //Zeros the counters

for(int i=0; i < 32; ++i){
    databuf[4096 * i] = 'a';
}

if (PAPI_read_counters(values, 1) != PAPI_OK) exit(-1); //Extracts the counters

printf("%llu\n", values[0]);

Я ожидал, что напечатанное число будет в районе 32 или, по крайней мере, несколько, но постоянно получаю результат 93 или выше (не всегда выше 96, т.е. не просто 3 промаха на каждую итерацию). Я работаю привязанным к ядру, на котором больше ничего нет (кроме прерываний таймера).

Я использую Nehalem и не использую огромные страницы, поэтому в DTLB 64 записи (512 в L2).


person jmetcalfe    schedule 19.02.2013    source источник
comment
Любопытство, а почему вас беспокоят промахи TLB?   -  person Tony The Lion    schedule 19.02.2013
comment
@Tony Работает над приложением, где их потенциально можно избежать с помощью предварительной выборки (если предварительная выборка между страницами доступна в конкретной архитектуре), избегая накладных расходов, связанных с промахом на горячем пути. Я подозреваю, что стоимость промаха tlb меньше, чем промах кэша (чего нельзя избежать), но я хотел проверить это предположение и при этом столкнулся с этой проблемой.   -  person jmetcalfe    schedule 19.02.2013
comment
@jmetcalfe Попробуйте использовать calloc() вместо malloc().   -  person Mysticial    schedule 19.02.2013
comment
@Mysticial Это сокращает количество зарегистрированных промахов до 32. Точно так же, если я вручную просматриваю массив заранее, чтобы эффективно выполнить предварительную настройку. Я не понимаю, почему все еще 32 промаха (похоже, все они должны успешно вписаться в tlb и оставаться там до второго цикла).   -  person jmetcalfe    schedule 19.02.2013
comment
Ха! Я угадал. Но я все еще не могу объяснить 32 оставшихся промаха.   -  person Mysticial    schedule 19.02.2013
comment
@Mysticial Оказывается, если я помещаю свой предаварийный цикл после вызова PAPI_start_counters (перед чтением, которое их обнуляет), он падает до нуля. Так кажется особенность PAPI. Однако меня все еще интересует разница между calloc / malloc - до сих пор не понимаю, почему исходные числа были такими высокими.   -  person jmetcalfe    schedule 19.02.2013


Ответы (2)


На основании комментариев:

  • ~ 90 промахов, если используется malloc().
  • 32 промаха, если используется calloc() или если массив перед этим повторяется.

Причина в ленивом распределении. Операционная система фактически не предоставляет вам память, пока вы ее не коснетесь.

Когда вы впервые прикоснетесь к странице, это приведет к ошибке страницы. ОС перехватит эту ошибку страницы и правильно распределяет ее на лету (, который помимо прочего включает обнуление). Это накладные расходы, которые приводят ко всем этим лишним промахам TLB.

Но если вы используете calloc() или заранее касаетесь всей памяти, вы переносите эти накладные расходы на перед запуском счетчиков. Отсюда меньший результат.

Что касается 32 оставшихся промахов ... Понятия не имею.
(Или, как упоминалось в комментариях, вероятно, это вмешательство PAPI.)

person Mysticial    schedule 19.02.2013
comment
Согласно моему комментарию, лишние 32 промаха кажутся помехой PAPI. На самом деле я думал об этом раньше, но думал, что избегаю ошибки страницы, используя mallopt(M_MMAP_MAX, 0), чтобы заставить его использовать sbrk (Не включался в вопрос, так как я не хотел включать неактуальные вещи!). По-видимому, это не работает. - person jmetcalfe; 19.02.2013
comment
В вашем ответе нет кода сборки, позвольте мне помочь вам: lea ebx, [2*eax + 1] - person fredoverflow; 19.02.2013
comment
1) malloc вообще не обнуляет память. Этого никогда не было. Это сделало бы его каллоком. 2) calloc использует оптимизацию для назначения всех страниц памяти, которая установлена ​​на 0. Затем он помечает эти страницы как копии при записи, поэтому, когда вы их пишете, запускается ошибка страницы. При их чтении будут прочитаны 0. - person J.D.; 20.05.2021

Причина этого, возможно, в том, что вы перепрыгиваете на 4096 при каждой итерации цикла:

for(int i=0; i < 32; ++i){
    databuf[4096 * i] = 'a';
}

Существует большая вероятность того, что вы получите промах кеша при каждом доступе.

person junix    schedule 19.02.2013
comment
Весь смысл теста в том, чтобы каждый раз пропускать DTLB. Я ищу объяснение того, почему это число так велико (т.е. более одного промаха DTLB на итерацию). - person jmetcalfe; 19.02.2013