Эффективность вашей маски обратного хода сильно зависит от компилятора.С gcc требуется около 21 инструкций .
Но с clang -std=c99 -O3 -m64 -Wall -march=nehalem
код векторизуется хорошо, и результаты на самом деле не так уж плохи:
movemask_inverse_original: # @movemask_inverse_original
movd xmm0, edi
pshufd xmm0, xmm0, 0 # xmm0 = xmm0[0,0,0,0]
pand xmm0, xmmword ptr [rip + .LCPI0_0]
cvtdq2ps xmm1, xmm0
xorps xmm0, xmm0
cmpneqps xmm0, xmm1
ret
Тем не менеевам не нужно целое число cvtdq2ps
для преобразования с плавающей запятой.Более эффективно вычислять маску в целочисленной области и приводить (без преобразования) результаты для последующего плавания.В ответе Питера Кордеса: есть ли обратная инструкция к инструкции movemask в intel avx2? , обсуждаются многие идеи по делу AVX2.Большинство из этих идей могут быть использованы в той или иной форме и для случая SSE.Решение LUT и решение ALU подходят для вашего случая.
Решение ALU с собственными характеристиками:
__m128 movemask_inverse_alternative(int x) {
__m128i msk8421 = _mm_set_epi32(8, 4, 2, 1);
__m128i x_bc = _mm_set1_epi32(x);
__m128i t = _mm_and_si128(x_bc, msk8421);
return _mm_castsi128_ps(_mm_cmpeq_epi32(x_bc, t));
}
Генерируемая сборка с gcc -std=c99 -O3 -m64 -Wall -march=nehalem
:
movemask_inverse_alternative:
movdqa xmm1, XMMWORD PTR .LC0[rip] % load constant 8, 4, 2, 1
movd xmm2, edi % move x from gpr register to xmm register
pshufd xmm0, xmm2, 0 % broadcast element 0 to element 3, 2, 1, 0
pand xmm1, xmm0 % and with 8, 4, 2, 1
pcmpeqd xmm0, xmm1 % compare with 8, 4, 2, 1
ret
movdqa
, вероятно, выводится из цикла после включения функции.