Я думаю, что я бы, вероятно, пойти на подходе "грубой силы и невежества" изначально, может быть что-то вроде этого:
uint8_t u = 0x53; // 01010011
const union {
uint32_t a[4];
__m128i v;
} kLUT[16] = { { { 0, 0, 0, 0 } },
{ { -1, 0, 0, 0 } },
{ { 0, -1, 0, 0 } },
{ { -1, -1, 0, 0 } },
{ { 0, 0, -1, 0 } },
{ { -1, 0, -1, 0 } },
{ { 0, -1, -1, 0 } },
{ { -1, -1, -1, 0 } },
{ { 0, 0, 0, -1 } },
{ { -1, 0, 0, -1 } },
{ { 0, -1, 0, -1 } },
{ { -1, -1, 0, -1 } },
{ { 0, 0, -1, -1 } },
{ { -1, 0, -1, -1 } },
{ { 0, -1, -1, -1 } },
{ { -1, -1, -1, -1 } } };
__m256i v = _mm256_set_m128i(kLUT[u >> 4].v, kLUT[u & 15].v);
Использование clang -O3
это компилируется:
movl %ebx, %eax ;; eax = ebx = u
andl $15, %eax ;; get low offset = (u & 15) * 16
shlq $4, %rax
leaq _main.kLUT(%rip), %rcx ;; rcx = kLUT
vmovaps (%rax,%rcx), %xmm0 ;; load low half of ymm0 from kLUT
andl $240, %ebx ;; get high offset = (u >> 4) * 16
vinsertf128 $1, (%rbx,%rcx), %ymm0, %ymm0
;; load high half of ymm0 from kLUT
FWIW я бросил вместе простой тест проводов для трех реализаций: (я) простой скаляр код эталонной реализации, (б) выше код, (III) реализация на основе @ ответ Zboson, в (я v) несколько улучшенная версия (iii) и (v) дальнейшее улучшение на (iv) с использованием предложения от @MarcGlisse. Я получил следующие результаты с 2.6GHz Haswell CPU (скомпилирован с clang -O3
):
scalar code: 7.55336 ns/vector
Paul R: 1.36016 ns/vector
Z boson: 1.24863 ns/vector
Z boson (improved): 1.07590 ns/vector
Z boson (improved + @MarcGlisse suggestion): 1.08195 ns/vector
Так @ решения Zboson (ов) выигрыш, примерно 10% - 20%, по-видимому, потому что они нужны только 1 нагрузку, по сравнению 2 для моего.
Если мы получим какие-либо другие реализации, я добавлю их на тестовый жгут и обновить результаты.
Немного улучшенная версия @ реализации Zboson в:
__m256i v = _mm256_set1_epi8(u);
v = _mm256_and_si256(v, mask);
v = _mm256_xor_si256(v, mask);
return _mm256_cmpeq_epi32(v, _mm256_setzero_si256());
Далее улучшенный вариант @ реализации, включающего предложения Zboson в от @MarcGlisse:
__m256i v = _mm256_set1_epi8(u);
v = _mm256_and_si256(v, mask);
return _mm256_cmpeq_epi32(v, mask);
(Обратите внимание, что mask
должен содержать реплицируются 8 бит значения в каждом 32-битном элементе, то есть 0x01010101, 0x02020202, ..., 0x80808080
)
Не могу придумать приятное решение. Вы можете создать таблицу со всеми предварительно вычисленными _m256i, индексированными uint8_t. Поскольку инструкции по смешиванию требуют немедленного, у вас может быть таблица смесей. AVX512 поможет с этим, я думаю. –
В качестве альтернативы вы можете попробовать транслировать байт в каждую полосу, маскируя один важный бит в каждом из них и, наконец, сравнивая создание маски. – doynax
@MarcGlisse lol, мы все ждем AVX512. Это буквально две инструкции. 'kmov + vmovdqa32' – Mysticial