A (потенциально) более быстрый способ, чем решение Марата на основе Решение Agner Fog :
Вместо разделения hi / low разделите нечетное / четное. Это дает дополнительное преимущество, заключающееся в том, что он работает с чистым SSE2 вместо того, чтобы требовать SSE4.1 (бесполезный для ОП, но для некоторых это хороший дополнительный бонус). Я также добавил оптимизацию, если у вас AVX2. Технически оптимизация AVX2 работает только со встроенными функциями SSE2, но она медленнее, чем сдвиг влево, а затем вправо.
__m128i mullo_epi8(__m128i a, __m128i b)
{
// unpack and multiply
__m128i dst_even = _mm_mullo_epi16(a, b);
__m128i dst_odd = _mm_mullo_epi16(_mm_srli_epi16(a, 8),_mm_srli_epi16(b, 8));
// repack
#ifdef __AVX2__
// only faster if have access to VPBROADCASTW
return _mm_or_si128(_mm_slli_epi16(dst_odd, 8), _mm_and_si128(dst_even, _mm_set1_epi16(0xFF)));
#else
return _mm_or_si128(_mm_slli_epi16(dst_odd, 8), _mm_srli_epi16(_mm_slli_epi16(dst_even,8), 8));
#endif
}
Агнер использует встроенную функцию blendv_epi8
с поддержкой SSE4.1.
Edit:
Интересно, что после проделанной работы по разборке (с оптимизированными сборками), по крайней мере, две мои реализации будут скомпилированы точно так же. Пример разборки прицеливания "ivy-bridge" (AVX).
vpmullw xmm2,xmm0,xmm1
vpsrlw xmm0,xmm0,0x8
vpsrlw xmm1,xmm1,0x8
vpmullw xmm0,xmm0,xmm1
vpsllw xmm0,xmm0,0x8
vpand xmm1,xmm2,XMMWORD PTR [rip+0x281]
vpor xmm0,xmm0,xmm1
Используется версия, оптимизированная для AVX2, с предварительно скомпилированной 128-битной константой xmm. Компиляция только с поддержкой SSE2 дает аналогичные результаты (хотя и с использованием инструкций SSE2). Я подозреваю, что оригинальное решение Agner Fog могло бы быть оптимизировано к тому же самому (было бы сумасшествием, если бы этого не произошло). Не представляю, как оригинальное решение Марата сравнивается в оптимизированной сборке, хотя для меня было бы неплохо иметь единый метод для всех расширений simd x86, более новых, чем SSE2, включая SSE2.