2016-06-14 4 views
1

Я пытаюсь сделать точное измерение доступа к памяти для различных уровней кэш-памяти, и придумал этот код для исследования:Точное определение времени доступа к памяти с помощью 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 и не должен быть отправлен во время работы. Может ли кто-нибудь сказать мне, могу ли я улучшить код и тем самым результаты в любом случае?

+1

Правильные номера для загрузки кэша -> использование латентности для Intel Haswell - 4c для L1, 12c для L2, согласно [PDF] (http://agner.org/optimize/). Отличный способ измерить это (особенно для L1) - это стрельба. Для L1 просто установите указатель для указания на себя и запустите 'mov (% rax),% rax' в цикле. Для L2 вам нужен большой связанный список, который не подходит для L1. –

ответ

1

Чтобы исправить ваш измерительный код, вы правы, что вам нужно измерить пустую установку в качестве базовой линии, чтобы вычесть издержки измерения.

Также имейте в виду, что TSC подсчитывает контрольные циклы, а не циклы тактовых импульсов, поэтому для этого вам необходимо убедиться, что ваш процессор всегда работает с одинаковой скоростью. (например, отключите турбонаддув и используйте цикл разогрева, чтобы получить CPU до максимальной скорости, тогда количество TSC должно соответствовать циклам ядра, если вы не разгоняете.)

Это, вероятно, объясняет колебания.


Обычно я измеряю материал с помощью счетчиков перфораций, а не RDTSC.

Но я думаю, что вы должны использовать инструкцию для сериализации (например, CPUID) перед первым RDTSC. Использование CPUID после второго RDTSC, вероятно, не является полезным. rdstcp for the second measurement is useful, так как это означает, что временная метка возникает после выполнения загрузки. (Руководство говорит: «выполнено», ИДК, если это означает, что «в отставку» или просто буквально исполняются портировать нагрузки.)

Так IIRC, вам лучше всего:

# maybe set eax to something before CPUID 
cpuid 
rdtsc 
shl $32, %%rdx 
lea (%%rax, %%rdx), %%rsi 

... code under test 

# CPUID here, too, if you can only use rdtsc instead of rdtscp 
rdtscp 
shl $32, %%rdx 
or %%rdx, %%rax 
sub %%rsi, %%rax 
# time difference in RAX  

Если код тестируемой конкурирует за те же порты ALU, что и shift/LEA, вы можете просто mov низкий 32 из первого результата RDTSC в другой регистр. Вместо того, чтобы иметь дело с высоким 32 вообще. Если вы считаете, что разница в метках времени намного меньше 2^32, вам не нужны 32 бита любого из них.


Я читал, что измерения крошечные последовательности, как это на современных процессорах может быть сделано лучше счетчиков производительности, чем с TSC. Agner Fog's test programs включает код для использования перфорированных счетчиков изнутри программы для измерения чего-либо. Это может позволить вам измерять основные циклы независимо от турбонаддува или без турбонаддува, так как счетчик тактовых импульсов основного тактового генератора фактически рассчитывается на один за физический такт.

+0

Обновление: 'lfence; rdtsc' сериализует его и более эффективен, чем CPUID. –