Похоже, что оптимизатор clang не задумывался о дублировании элемента, чтобы довести его до удобного для SIMD числа сравнений.Но вы правы, это было бы лучше, чем делать дополнительную скалярную работу. Очевидно, что пропущенная оптимизация должна быть зарегистрирована как ошибка оптимизатора clang / LLVM.https://bugs.llvm.org/
Asm для f1()
явно лучше, чем f2()
: vpacksswb xmm
имеет такую же стоимость, как и vpmovsxwd xmm
на основных процессорах Intel и AMD, как и другие однопользовательские тасовки,И если что-нибудь vpmovsx
-> vmovmskps
могло бы иметь задержку обхода между целочисленными доменами и доменами FP 1 .
Сноска 1. Вероятно, нет дополнительной задержки обхода на основных процессорах Intel сAVX2 (семья Sandybridge);целочисленные тасования между операциями FP, как правило, в порядке, IIRC.(https://agner.org/optimize/). Но для версии SSE4.1 в Nehalem, да, может быть дополнительный штраф, который не будет иметь целочисленная версия.
Вам не нужен AVX2, но трансляция в слова водна инструкция без управляющего вектора pshufb
делает ее более эффективной, и clang выбирает pshuflw
-> pshufd
для -march=nehalem
Конечно, обе версии являются подпрограммамиоптимальный . Нет необходимости тасовать, чтобы сжать результат сравнения перед перемещением маски.
Вместо test al, al
, можно выбрать, какие биты вы хотите проверить, например, с помощью test sil, 0b00001010
, чтобы проверить биты1 и 3, но игнорируют ненулевые биты в других позициях.
pcmpeqw
устанавливает оба байта одинаково внутри элемента слова, поэтому можно pmovmskb
получить результат и получить целое число с парами битов.
Существует также нулевое преимущество использования регистра байтов вместо регистра dword: test sil,sil
следует избегать префикса REX и использовать test esi,esi
.
Так что даже без дублирования одним из условий, f2()
может быть:
f2:
vmovd xmm0, edi
vpbroadcastw xmm0, xmm0 # set1(x)
vpcmpeqw xmm0, xmm0, xmmword ptr [rip + .LCPI0_0]
vpmovmskb eax, xmm0
test eax, 0b011111111111111 # (1<<15) - 1 = low 14 bits set
setne al
ret
То, что test
установит ZF в соответствии с младшими 14 битами результата pmovmksb
, потому что старшие биты очищаются в маске TEST.ТЕСТ = И это не записывает свой вывод.Часто полезно для выбора частей маски сравнения.
Но так как в первую очередь нам нужна 16-байтовая константа в памяти, да, мы должны дублировать один из элементов, чтобы заполнить его до 8 элементов.Тогда мы можем использовать test eax,eax
как нормальный человек.Сжатие маски до 8-битового AL
- это пустая трата времени и размера кода.test r32, r32
так же быстро, как test r8,r8
и не нуждается в префиксе REX для SIL, DIL или BPL.
Интересный факт: AVX512VL позволит нам использовать vpbroadcastw xmm0, edi
для объединения movd
и транслировать.
Или для сравнения только 4 элементов, вместо дополнительной перестановки для movmskps
, нам нужен только SSE2 здесь.И использование маски действительно полезно.
test_4_possibilities_SSE2:
movd xmm0, edi
pshufd xmm0, xmm0, 0 # set1_epi32(x)
pcmpeqw xmm0, [const] # == set_epi32(a, b, c, d)
pmovmskb eax, xmm0
test eax, 0b0001000100010001 # the low bit of each group of 4
setne al
ret
Мы выполняем трансляцию слова и игнорируем результат сравнения в старших 16 битах каждого 32-битного элемента.Использование маски для test
позволяет нам делать это дешевле, чем любая дополнительная инструкция.
Без AVX2 широковещательная передача SIMD-меча с pshufd
дешевле, чем необходимость трансляции слова.
Другим вариантом является imul
с 0x00010001
для трансляции слова в 32-битный регистр, но с задержкой в 3 цикла, поэтому она потенциально хуже, чем punpcklwd
-> pshufd
Внутри циклатем не менее, стоило бы загрузить управляющий вектор для pshufb
(SSSE3) вместо использования 2 перемешиваний или imul.