SSE乘法16 x uint8_t

Rob*_*oby 11 x86 sse simd sse4

我想用SSE4乘以一个__m128i带有16个无符号8位整数的对象,但我只能找到一个乘以16位整数的内在函数.有没有这样的_mm_mult_epi8

hel*_*922 14

比基于Agner Fog解决方案的 Marat解决方案(可能)更快的方式:

而不是分裂高/低,分裂奇数/偶数.这有一个额外的好处,它适用于纯SSE2,而不是需要SSE4.1(对OP没有用,但对一些人来说是一个很好的额外奖励).如果你有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
}
Run Code Online (Sandbox Code Playgroud)

Agner使用blendv_epi8内在的SSE4.1支持.

编辑:

有趣的是,在进行了更多的反汇编工作(使用优化的构建版本)之后,至少我的两个实现被编译为完全相同的东西.针对"常春藤桥"(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
Run Code Online (Sandbox Code Playgroud)

它使用"AVX2优化"版本和预编译的128位xmm常量.仅使用SSE2支持进行编译会产生类似的结果(尽管使用SSE2指令).我怀疑Agner Fog的原始解决方案可能会针对同样的事情进行优化(如果不这样做会很疯狂).不知道Marat的原始解决方案如何在优化的构建中进行比较,但对于我来说,对于所有x86 simd扩展而言,比SSE2更新的所有x86 simd扩展都非常好.

  • 这真的很好.它利用了有符号与无符号仅影响N x N - > 2N位乘法的高半部分的事实,以及[高位中的垃圾不会影响低位中所需的结果]( http://stackoverflow.com/questions/34377711/which-2s-complement-integer-operations-can-be-used-without-zeroing-high-bits-in).如果加载掩码时缓存未命中是一个问题,你可以使用2个insn动态生成它:`pcmpeqw xmm7,xmm7` /`psrlw xmm7,8`.(有关其他const生成序列,请参阅http://stackoverflow.com/q/35085059/224132). (2认同)

Mar*_*han 11

MMX/SSE/AVX中没有8位乘法.但是,您可以使用16位乘法模拟8位乘法内在,如下所示:

inline __m128i _mm_mullo_epi8(__m128i a, __m128i b)
{
    __m128i zero = _mm_setzero_si128();
    __m128i Alo = _mm_cvtepu8_epi16(a);
    __m128i Ahi = _mm_unpackhi_epi8(a, zero);
    __m128i Blo = _mm_cvtepu8_epi16(b);
    __m128i Bhi = _mm_unpackhi_epi8(b, zero);
    __m128i Clo = _mm_mullo_epi16(Alo, Blo);
    __m128i Chi = _mm_mullo_epi16(Ahi, Bhi);
    __m128i maskLo = _mm_set_epi8(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 14, 12, 10, 8, 6, 4, 2, 0);
    __m128i maskHi = _mm_set_epi8(14, 12, 10, 8, 6, 4, 2, 0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80);
    __m128i C = _mm_or_si128(_mm_shuffle_epi8(Clo, maskLo), _mm_shuffle_epi8(Chi, maskHi));

     return C;
}
Run Code Online (Sandbox Code Playgroud)


Pau*_*l R 8

唯一的8位SSE乘法指令是PMADDUBSW(SSSE3及更高版本,C/C++内在函数:_mm_maddubs_epi16).此相乘16×8位无符号的 16×8位值签名值,然后求和相邻对,得到8×16位有符号的结果.如果你不能使用这个相当专业的指令,那么你需要解压缩到16位向量对并使用常规的16位乘法指令.显然这意味着至少有2倍的吞吐量命中,所以如果可能的话,使用8位乘法.