Ваш первый код (ведущий к заглавному вопросу) содержит ошибки, поскольку он перезаписывает результаты rdtsc
и rdtscp
с результатами cpuid
в EAX, EBX, ECX и EDX.
Используйте lfence
вместо cpuid
;на Intel с тех пор, как навсегда, и AMD с включенным смягчением Spectre, lfence
будет сериализовать поток команд и, таким образом, делать то, что вы хотите с rdtsc
.
Помните, что RDTSC считает контрольные циклы, а нетактовые частоты ядра. Получить счетчик тактов ЦП? для этого и больше о RDTSC.
В вашем интервале измерений нет cpuid
или lfence
.Но вы делаете сами rdtscp
в интервале измерений.Спина к спине rdtscp
не является быстрой, 64 эталонных циклов звучат вполне разумно, если вы работали без прогрева процессора.Частота холостого хода обычно намного ниже, чем у эталонного цикла ;1 эталонный цикл равен или близок к частоте "стикера", например, максимальная частота без поддержки турбо, на процессорах Intel.например, 4008 МГц на процессоре Skylake "4 ГГц".
Это не то, как вы рассчитываете одну инструкцию
Имеет значение задержка, прежде чем другая команда сможет использовать результат,не задержка до тех пор, пока он полностью не выйдет из внешнего сервера. RDTSC может быть полезен для синхронизации относительных отклонений того, сколько времени занимает одна загрузка или одна инструкция сохранения, но накладные расходы означают, что выне получится хорошего абсолютного времени.
Однако вы можете попытаться вычесть накладные расходы на измерения.например, clflush для аннулирования строки кэша с помощью функции C .И смотрите также следующие действия: Использование счетчика меток времени и clock_gettime для пропуска кэша и Измерение задержки памяти со счетчиком меток времени .
Эточто я обычно использую, чтобы профилировать задержку или пропускную способность (и uops слитый и неиспользуемый домен) инструкции короткого блока .Отрегулируйте, как вы используете его, чтобы ограничить задержку, как здесь, или нет, если вы хотите просто проверить пропускную способность.например, с блоком %rep
с достаточным количеством различных регистров, чтобы скрыть задержку, или разорвать цепочки зависимостей с помощью pxor xmm3, xmm3
после короткого блока и позволить exec-of-order exec работать своим волшебством.(До тех пор, пока вы не станете узким местом в интерфейсе.)
Возможно, вы захотите использовать пакет smartalign NASM или YASM, чтобы избежать стены однобайтовых инструкций NOP для директивы ALIGN.NASM по умолчанию настроен на действительно глупые NOP даже в 64-битном режиме, где всегда поддерживается long-NOP.
global _start
_start:
mov ecx, 1000000000
; linux static executables start with XMM0..15 already zeroed
align 32 ; just for good measure to avoid uop-cache effects
.loop:
;; LOOP BODY, put whatever you want to time in here
times 4 addsd xmm4, xmm3
dec ecx
jnz .loop
mov eax, 231
xor edi, edi
syscall ; x86-64 Linux sys_exit_group(0)
Запустите его с чем-то вроде этого с одним вкладышем, который связывает его в статический исполняемый файл и профилирует его с помощью perf stat
, , который вы можете стрелять вверх и перезапускать каждый раз, когда вы меняете источник :
(я фактически помещаю опциональную дизассемблирование nasm + ld + в сценарий оболочки под названием asm-link
, чтобы сохранить ввод, когда я не профилирую. Разборка гарантирует, что в вашем цикле находится то, что вы имели в виду для профилирования, особенно если в вашем коде есть что-то %if
.на вашем терминале прямо перед профилем, если вы хотите прокрутить назад при тестировании теорий в своей голове.)
t=testloop; nasm -felf64 -g "$t.asm" && ld "$t.o" -o "$t" && objdump -drwC -Mintel "$t" &&
taskset -c 3 perf stat -etask-clock,context-switches,cpu-migrations,page-faults,cycles,branches,instructions,uops_issued.any,uops_executed.thread -r4 ./"$t"
Результат от i7-6700k при 3,9 ГГц (текущий perf
имеет ошибку отображения масштаба модуля для вторичного столбца. Она исправлена в восходящем направлении, но Arch Linux еще не обновился.):
Performance counter stats for './testloop' (4 runs):
4,106.09 msec task-clock # 1.000 CPUs utilized ( +- 0.01% )
17 context-switches # 4.080 M/sec ( +- 5.65% )
0 cpu-migrations # 0.000 K/sec
2 page-faults # 0.487 M/sec
16,012,778,144 cycles # 3900323.504 GHz ( +- 0.01% )
1,001,537,894 branches # 243950284.862 M/sec ( +- 0.00% )
6,008,071,198 instructions # 0.38 insn per cycle ( +- 0.00% )
5,013,366,769 uops_issued.any # 1221134275.667 M/sec ( +- 0.01% )
5,013,217,655 uops_executed.thread # 1221097955.182 M/sec ( +- 0.01% )
4.106283 +- 0.000536 seconds time elapsed ( +- 0.01% )
На моем i7-6700k (Skylake), addsd
имеет 4 циклазадержка, пропускная способность 0,5 с.(т.е. 2 за час, если задержка не была узким местом).См. https://agner.org/optimize/, https://uops.info/, и http://instlatx64.atw.hu/.
16 циклов на ветвь = 16 циклов на цепочку 4 addsd
= задержка 4 цикла для addsd
, воспроизведениеAgner Fog измеряет 4 цикла с точностью до 1 части на 100 даже для этого теста, который включает незначительные накладные расходы при запуске и издержки прерывания.
Выберите различные счетчики для записи.При добавлении :u
, например instructions:u
, к perf даже будут учитываться только инструкции пользовательского пространства, исключая те, которые выполнялись во время обработчиков прерываний.Обычно я этого не делаю, поэтому я могу рассматривать эти накладные расходы как часть объяснения времени настенных часов.Но если вы это сделаете, cycles:u
может сопоставить очень близко с instructions:u
.
-r4
запускает его 4 раза и усредняет, что может быть полезно, чтобы увидеть, если есть многовариация между циклами вместо простого получения среднего значения из более высокого значения в ECX.
Настройте исходное значение ECX так, чтобы общее время составляло от 0,1 до 1 секунды, что обычно достаточно, особенно если ваш процессор ускоряетсядо макс. турбо очень быстро (например, Skylake с аппаратными P-состояниями и довольно агрессивной energy_performance_preference).Или макс. Без турбо с отключенным турбо.
Но это учитывается в тактах ядра, а не в эталонных циклах, поэтому он все равно дает тот же результат независимо от изменений частоты процессора .(+ - некоторый шум от остановки часов во время перехода.)