У меня есть следующий цикл в asm:
.LBB5_5:
vaddpd ymm0, ymm0, ymmword, ptr, [rdi, +, 8*rcx]
vaddpd ymm1, ymm1, ymmword, ptr, [rdi, +, 8*rcx, +, 32]
vaddpd ymm2, ymm2, ymmword, ptr, [rdi, +, 8*rcx, +, 64]
vaddpd ymm3, ymm3, ymmword, ptr, [rdi, +, 8*rcx, +, 96]
add rcx, 16
cmp rax, rcx
jne .LBB5_5
Это часть более крупной функции, которая вычисляет сумму массива [f64]
в Rust.
Я сравнил этот код с набором критериев и обнаружил, что 1 000 000 000
двойные операции занимают 200 000 000
циклов на моем процессоре Rocket Lake (i7 11700K).
В различных источниках я обнаружил, что задержка сложения чисел с плавающей запятой на этом процессоре составляет 4 цикла.
Это означало бы, что каждый из vaddpd
может выполняться только каждый четвертый цикл, поскольку они несут зависимость от предыдущей суммы. Это означало бы, что я могу сделать максимум 4 двойных сложения за цикл.
Мои измерения показывают, что он делает 5 прибавок за цикл. (Для измерения используется инструкция RDTSC
, я не уверен, что это может быть проблематично)
В основном я хочу понять, что происходит, и проверить, насколько хорошо я понимаю конвейер ЦП.
@AndreySemachev Это всего лишь представление cargo asm
, это не петля ручной работы. Я знаю, что эти 4 инструкции можно выполнять параллельно, но это должно дать мне только 4 раза по 4 удвоения за 4 такта. Я наблюдаю более быстрое исполнение
Я думаю, вы наблюдаете 5 прибавок за цикл, потому что используете RDTSC
для измерения.
В течение последнего десятилетия или около того инструкция RDTSC не учитывает циклы ЦП. Вместо этого он измеряет время настенных часов, используя базовую частоту процессора.
Ваш процессор имеет базовую частоту 3,6 ГГц и максимальную турбо-частоту 5,00 ГГц. Если вы проведете короткий тест, ваш процессор будет работать на турбо-частоте, однако счетчик, измеренный с помощью RDTSC, все равно будет работать на базовой частоте.
Это действительно была проблема. Когда я отключаю турбо, я получаю 260 000 000 циклов, что совместимо с 4 добавлениями за цикл.
RDTSC по-прежнему работает на базовой частоте. - Или какая-то близкая к ней частота, напр. 4008 МГц на моем i7-6700k (базовая частота 4 ГГц, повышение 4,2 ГГц). Или на некоторых процессорах, даже близких к базовой частоте, например. некоторые мобильные процессоры Ice Lake, поддерживающие TDP-up и TDP-down. Как получить количество циклов процессора в x86_64 из C++?. Хм, интересно, как это работает на Alder Lake с E-ядрами и P-ядрами? Предположительно, TSC по-прежнему синхронизируются по всем ядрам, как типичные многоядерные процессоры, поэтому один набор ядер будет иметь TSC, который практически не связан с тактовой частотой его ядра.
Выложенный вами код не совсем корректен из-за лишних запятых. В инструкциях 4
vpaddpd
используются отдельные аккумуляторы, что означает, что они могут выполняться параллельно. Учитывая, что в Rocket Lakevpaddpd
обратная пропускная способность равна 0,5, это означает, что за такт может запускаться до двух таких дополнений. В зависимости от продолжительности цикла и от того, находятся ли исходные данные в кэше, пропускная способность памяти может быть здесь ограничивающим фактором.