Я пытаюсь сделать точное измерение доступа к памяти для различных уровней кэш-памяти, и придумал этот код для исследования:Точное определение времени доступа к памяти с помощью RDTSC и RDTSCP?
__asm__ __volatile__(
"xor %%eax, %%eax \n"
"xor %%edi, %%edi \n"
"xor %%edx, %%edx \n"
/* time measurement */
"lfence \n"
"rdtsc \n"
"shl $32, %%rdx \n"
"or %%rdx, %%rax \n"
"movq %%rax, %%rdi \n"
/* memory access */
"movq (%%rsi), %%rbx\n"
/* time measurement */
"rdtscp \n"
"shl $32, %%rdx \n"
"or %%rdx, %%rax \n"
"movq %%rax, %%rsi \n"
"cpuid \n"
: /* output operands */
"=S"(t2), "=D"(t1)
: /* input operands */
"S" (mem)
: /* clobber description */
"ebx", "ecx", "edx", "cc", "memory"
);
Однако доступ к кэш-памяти L1 и L2 просто отличаются друг от 8 циклов и результаты колеблются много, поэтому я решил проверить, насколько влияние на окружающий код (за исключением фактического доступа к памяти) имеет на сроки:
__asm__ __volatile__(
"xor %%eax, %%eax \n"
"xor %%edi, %%edi \n"
"xor %%edx, %%edx \n"
/* time measurement */
"lfence \n"
"rdtsc \n"
"shl $32, %%rdx \n"
"or %%rdx, %%rax \n"
"movq %%rax, %%rdi \n"
/* memory access */
//"movq (%%rsi), %%rbx\n"
/* time measurement */
"rdtscp \n"
"shl $32, %%rdx \n"
"or %%rdx, %%rax \n"
"movq %%rax, %%rsi \n"
"cpuid \n"
: /* output operands */
"=S"(t2), "=D"(t1)
: /* input operands */
"S" (mem)
: /* clobber description */
"ebx", "ecx", "edx", "cc", "memory"
);
результаты выглядели так:
./cache_testing
From Memory: 42
From L3: 46
From L2: 40
From L1: 38
./cache_testing
From Memory: 40
From L3: 38
From L2: 36
From L1: 40
Я знаю, что на данный момент я не ударяю по разным уровням кэша по назначению, но мне интересно, почему время в случае отсутствия доступа к памяти так сильно колеблется. Код работает как SCHED_FIFO с наивысшим приоритетом, прикрепленным к одному CPU и не должен быть отправлен во время работы. Может ли кто-нибудь сказать мне, могу ли я улучшить код и тем самым результаты в любом случае?
Правильные номера для загрузки кэша -> использование латентности для Intel Haswell - 4c для L1, 12c для L2, согласно [PDF] (http://agner.org/optimize/). Отличный способ измерить это (особенно для L1) - это стрельба. Для L1 просто установите указатель для указания на себя и запустите 'mov (% rax),% rax' в цикле. Для L2 вам нужен большой связанный список, который не подходит для L1. –