Когерентные системы кэширования делают все возможное, чтобы скрыть от вас такие вещи. Я думаю, вам придется наблюдать это косвенно, либо используя регистры подсчета производительности для обнаружения промахов кэша, либо тщательно измеряя время чтения ячейки памяти с помощью таймера с высоким разрешением.
Эта программа работает на моей машине 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
из /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;
}
person
Ben Jackson
schedule
05.07.2011
WBINVD
сбрасывает все кеши данных (неясно, сбрасывал ли он также кеш инструкций L1, и его реализация зависит от ЦП), но использовать эту инструкцию довольно необычно (самоизменяющийся код - единственный пример что приходит на ум) и даже в этом случае кеши немедленно начнут снова заполняться, и ЦП не имеет прямых сведений о текущем состоянии любого из кешей. Вы должны объяснить, чего вы на самом деле пытаетесь достичь, то есть мотивацию вашего вопроса. - person Paul R   schedule 18.05.2011opcontrol --list-events
какое-нибудь интересное событие? - person ninjalj   schedule 02.07.2011