Ответ 1
Когерентные системы кэша делают все возможное, чтобы скрыть такие вещи от вас. Я думаю, вам придется наблюдать это косвенно, либо используя регистры подсчета производительности, чтобы обнаруживать промахи в кэше, либо тщательно измеряя время для чтения ячейки памяти с помощью таймера с высоким разрешением.
Эта программа работает в моем окне x86_64, чтобы продемонстрировать эффекты clflush
. Время, затрачиваемое на чтение глобальной переменной с помощью rdtsc
. Будучи единственной инструкцией, привязанной непосредственно к ЦП, прямое использование rdtsc
идеально подходит для этого.
Вот результат:
took 81 ticks took 81 ticks flush: took 387 ticks took 72 ticks
Вы видите 3 испытания: первое гарантирует, что i
находится в кеше (это он, потому что он был просто обнулен как часть BSS), второй - это чтение i
, которое должно быть в кеше. Затем clflush
удаляет i
из кеша (вместе со своими соседями) и показывает, что повторное чтение занимает значительно больше времени. Окончательное чтение проверяет, что оно вернулось в кеш. Результаты очень воспроизводимы, и разница достаточно существенна, чтобы легко увидеть промахи в кэше. Если вы решили откалибровать накладные расходы rdtsc()
, вы могли бы сделать разницу еще более выраженной.
Если вы не можете прочитать адрес памяти, который хотите протестировать (хотя даже mmap
of /dev/mem
должен работать для этих целей), вы можете сделать вывод о том, что хотите, если знаете размер кешины и ассоциативность кэш. Затем вы можете использовать доступные ячейки памяти для проверки активности в интересующем вас наборе.
Исходный код:
#include <stdio.h>
#include <stdint.h>
inline void
clflush(volatile void *p)
{
asm volatile ("clflush (%0)" :: "r"(p));
}
inline uint64_t
rdtsc()
{
unsigned long a, d;
asm volatile ("rdtsc" : "=a" (a), "=d" (d));
return a | ((uint64_t)d << 32);
}
volatile int i;
inline void
test()
{
uint64_t start, end;
volatile int j;
start = rdtsc();
j = i;
end = rdtsc();
printf("took %lu ticks\n", end - start);
}
int
main(int ac, char **av)
{
test();
test();
printf("flush: ");
clflush(&i);
test();
test();
return 0;
}