Прежде всего, dec/jnz
слится в одном макросе в семействе Intel Sandybridge.Вы можете победить это, поместив инструкцию без установки флага между dec и jnz.
.for_loop: ; do {
inc rax
dec rcx
lea rbx, [rbx+1] ; doesn't touch flags, defeats macro-fusion
jnz .for_loop ; } while (--rcx)
Это все равно будет выполняться по 1 итеру за цикл в Haswell и более поздних версиях и в Ryzen, поскольку они имеют 4 целочисленных порта выполнениячтобы не отставать от 4 мопов за итерацию.(Ваш цикл с макро-слиянием - это всего 3 мопа с плавким доменом на процессорах Intel, поэтому SnB / IvB также может запускать его по 1 за такт.)
См. Руководство по оптимизации Agner Fog *.и особенно его гид по микроарху.Также другие ссылки в https://stackoverflow.com/tags/x86/info.
Управляющие зависимости скрыты предсказанием ветвлений + спекулятивным выполнением, в отличие от зависимостей данных.
Внеочередное выполнение ипредсказание ветвлений + умозрительное выполнение скрывают «задержку» управляющей зависимости.то есть следующая итерация может начаться до того, как процессор подтвердит, что jnz
действительно должен быть взят.
Таким образом, каждый jnz
имеет входную зависимость от предыдущего dec rcx
, прежде чем он сможет проверить прогноз, но позжеинструкции не должны ждать, пока они будут проверены, прежде чем они смогут выполнить. Выход на пенсию гарантирует, что ложная спекуляция обнаруживается, прежде чем что-либо может "увидеть", что это происходит (за исключением микроархитектурных эффектов, ведущих к атаке Призрака ...)
10Mитераций не много.Я обычно использовал бы по крайней мере 100M для чего-то, что работает только в 1c на iter.Простой прогон микробенчмарка в течение 0,1-1 секунды обычно хорош для получения очень высокой точности и скрытия накладных расходов при запуске.
И кстати, вам не нужно sudo perf
, если вы установите kernel.perf_event_paranoid = 0
с помощью sysctl.Это почти наверняка лучше сделать, чем использовать sudo
все время.