#assembly #x86
#сборка #x86
Вопрос:
Я пытаюсь написать (очень) короткую процедуру сборки, которая проверяет равенство двух dw-слов и возвращает логическое значение (1 = true, 0 = false). На данный момент я разработал три метода, один из которых использует LAHF, который, по-видимому, не поддерживается на некоторых процессорах x86_64, так что, к сожалению, об одном из них не может быть и речи.
Первая версия:
mov eax, [esp 8]
cmp b, [esp 4]
mov eax, 1
jnz jpt
mov eax, 0
jpt: ret
и вторая версия — это:
mov eax, [ebp 8]
cmp b, [ebp 4]
pushf ; Get lowest word of the flags register
pop ax
and eax, 0x0040 ; Extract the zero flag
shr eax, 6 ; eax is now true(1) if arg1 == arg2
ret
В первой версии есть дополнительная инструкция перехода, но во второй версии есть дополнительная инструкция push и дополнительная pop. Какая из них, по вашим ожиданиям, будет самой быстрой и почему? Зависит ли это от того, будет ли принята / предсказана ветвь или нет?
Комментарии:
1. Ни то, ни другое, потому что в любом реальном программном обеспечении узкие места не будут обнаружены при проверке равенства двух двордов.
2. Что такое «реальное программное обеспечение»?
3. Сам акт вызова «процедуры равенства» может быть более дорогостоящим, чем фактическое сравнение.
4. Я понимаю, что это не является вероятным узким местом, но это скорее академическое упражнение. Это часть упрощенного генератора кода, поэтому любое преимущество в производительности будет применено, очевидно встроенное, к довольно большому объему кода.
5. @Cat, я не раз видел, что это узкое место.
Ответ №1:
Обе версии являются плохими. Выполнение случайной ветви занимает много времени, потому что ее невозможно предсказать, а lahf — это просто «нет» из-за частичной записи в регистр. Но, конечно, написание теста на равенство на ассемблере в любом случае является полной бессмыслицей, потому что служебные данные функции будут кратны встроенным эквивалентным инструкциям, поэтому я начинаю:
mov eax, [ebp 8]
cmp eax, [ebp 4]
setz al ;set al to 1 if equal
movzx eax,al ;convert to dword
ret
Комментарии:
1. Я не знал о существовании SZ, это намного лучше, чем любая из моих попыток.
2. «sz al» разве вы не имеете в виду «setz al»?
Ответ №2:
Я уже находил эти узкие места раньше в приложениях, которые мне нужно было оптимизировать, и они являются верным признаком того, что вы уперлись в стену и не можете оптимизировать дальше.
Наилучшим способом действий было бы выбрать другой алгоритм или компоновку данных, который лучше соответствует платформе и шаблонам доступа, чем тот, который у вас есть в настоящее время. Это, вероятно, самая важная вещь, которую вы можете сделать.
Однако из-за крайних сроков или других ограничений, которые иногда также невозможны, и поэтому вам нужно будет подойти к этому творчески, что, вероятно, подразумевает одновременное тестирование нескольких элементов с использованием операций SIMD (например. используйте встроенную _mm_cmpeq_epi32 для сравнения 4 элементов). Если вы собираетесь перейти к этому, вы можете сравнить 16 элементов, побитово или маски вместе, и перейти к этому (затем выберите правильные данные внутри ветки).
Это в первую очередь полезно на платформах, где ответвления очень дороги, а в IA-32/64 это не так (например, ответвления дешевы).
Также имейте в виду, что из-за использования платформ Intel с нарушением порядка выполнения (OOE); вполне может быть, что используемый вами профилировщик сообщает о сбое в более или менее случайном местоположении, потому что так уж получилось, что процессору необходимо дождаться чтения данных из кэша или оперативной памяти.
Если вы оказались в такой ситуации, убедитесь, что вы оптимизировали свой алгоритм, чтобы он был более удобным для кэша (например. выясните, сколько элементов помещается в строку кэша, уменьшите размер структур данных и т.д.)