Im в настоящее время кодирует высоко оптимизированные версии некоторых стандартных функций библиотеки библиотеки C99, таких как strlen()
, memset()
и т.д., используя сборку x86-64 с инструкциями SSE-2.
Пока мне удалось добиться отличных результатов с точки зрения производительности, но иногда я получаю странное поведение, когда пытаюсь оптимизировать больше.
Например, добавление или даже удаление некоторых простых инструкций или просто реорганизация некоторых локальных меток, используемых при скачках, полностью ухудшает общие характеристики. И theres абсолютно никакой причины с точки зрения кода.
Итак, я предполагаю, что есть некоторые проблемы с выравниванием кода и/или с ветвями, которые становятся неверно предсказанными.
Я знаю, что даже с той же архитектурой (x86-64) разные процессоры имеют разные алгоритмы предсказания ветвей.
Но есть ли общие рекомендации при разработке для высоких характеристик на x86-64, о выравнивании кода и предсказании ветвей?
В частности, о выравнивании, следует ли гарантировать, что все метки, используемые инструкциями перехода, выровнены по DWORD?
_func:
; ... Some code ...
test rax, rax
jz .label
; ... Some code ...
ret
.label:
; ... Some code ...
ret
В предыдущем коде следует использовать директиву align перед .label:
, например:
align 4
.label:
Если это так, достаточно ли выровнять по DWORD при использовании SSE-2?
И о предсказании ветвей, есть ли "предпочтительный" способ организовать метки, используемые инструкциями перехода, чтобы помочь процессору, или сегодня являются процессорами, достаточно умными, чтобы определить, что во время выполнения подсчитывается количество раз, когда ветка берется?
ИЗМЕНИТЬ
Хорошо, здесь конкретный пример - здесь начало strlen()
с SSE-2:
_strlen64_sse2:
mov rsi, rdi
and rdi, -16
pxor xmm0, xmm0
pcmpeqb xmm0, [ rdi ]
pmovmskb rdx, xmm0
; ...
Запуск его 10'000'000 раз с 1000 символьной строкой дает около 0,48 секунды, что отлично.
Но он не проверяет ввод строки NULL. Поэтому, очевидно, я добавлю простую проверку:
_strlen64_sse2:
test rdi, rdi
jz .null
; ...
Тот же тест, он работает сейчас через 0,59 секунды. Но если я выровняю код после этой проверки:
_strlen64_sse2:
test rdi, rdi
jz .null
align 8
; ...
Оригинальные выступления вернулись. Я использовал 8 для выравнивания, поскольку 4 ничего не меняет.
Может кто-нибудь объяснить это и дать некоторые советы о том, когда выровнять или не выравнивать разделы кода?
РЕДАКТИРОВАТЬ 2
Конечно, это не так просто, как выравнивание каждой цели ветки. Если я это сделаю, спектакли обычно ухудшатся, если только некоторые конкретные случаи, как указано выше.