Неожиданно большое количество пропусков 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).
2 ответа
На основании комментариев:
- ~ 90 промахов, если
malloc()
используется. - 32 пропускает, если
calloc()
используется или, если массив итерируется перед рукой.
Причина в ленивом распределении. ОС на самом деле не дает вам память, пока вы не коснетесь ее.
Когда вы впервые коснетесь страницы, это приведет к сбою страницы. ОС будет ловить эту ошибку страницы и правильно распределять ее на лету ( что включает в себя обнуление среди прочего). Это накладные расходы, которые приводят ко всем этим дополнительным пропускам TLB.
Но если вы используете calloc()
или вы касаетесь всей памяти раньше времени, вы перемещаете эти накладные расходы до того, как запустите счетчики. Отсюда и меньший результат.
Что касается 32 оставшихся промахов... Понятия не имею.
(Или, как упоминалось в комментариях, это, вероятно, помехи PAPI.)
Причина этого, возможно, в том, что вы прыгаете на 4096 в каждой итерации цикла:
for(int i=0; i < 32; ++i){
databuf[4096 * i] = 'a';
}
Существует большая вероятность того, что вы получите пропуск кэша для каждого доступа.