Оптимизация производительности сборки x86-64 - выравнивание и прогноз ветвления
В настоящее время я пишу высокооптимизированные версии некоторых строковых функций стандартной библиотеки C99, таких какstrlen()
, memset()
и т. д. с использованием сборки x86-64 с инструкциями SSE-2.
До сих пор мне удавалось добиться отличных результатов с точки зрения производительности, но иногда я получаю странное поведение, когда пытаюсь оптимизировать больше.
Например, добавление или даже удаление некоторых простых инструкций или просто реорганизация некоторых локальных меток, используемых с переходами, полностью ухудшает общие характеристики. И нет абсолютно никаких причин с точки зрения кода.
Таким образом, я предполагаю, что есть некоторые проблемы с выравниванием кода и / или с ветвями, которые были ошибочно предсказаны.
Я знаю, что даже при одинаковой архитектуре (x86-64) разные процессоры имеют разные алгоритмы прогнозирования ветвлений.
Но есть ли какие-то общие советы при разработке для высокой производительности на x86-64, о выравнивании кода и предсказании ветвлений?
В частности, о выравнивании, я должен гарантировать, что все метки, используемые инструкциями перехода, выровнены по DWORD?
_func:
; ... Some code ...
test rax, rax
jz .label
; ... Some code ...
ret
.label:
; ... Some code ...
ret
В предыдущем коде я должен использовать директиву выравнивания перед.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
Конечно, это не так просто, как выравнивание каждой цели ветки. Если я это сделаю, выступления, как правило, будут ухудшаться, если не считать каких-либо конкретных случаев, подобных описанным выше.