12

我想用 SSE4 乘以一个__m128i具有 16 个无符号 8 位整数的对象,但我只能找到一个用于乘以 16 位整数的内在函数。有没有类似的东西_mm_mult_epi8

4

3 回答 3

17

一种(可能)比基于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
}

Agner 使用blendv_epi8带有 SSE4.1 支持的内在函数。

编辑:

有趣的是,在做了更多的反汇编工作(使用优化的构建)之后,至少我的两个实现被编译成完全相同的东西。针对“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

它使用带有预编译的 128 位 xmm 常量的“AVX2 优化”版本。仅使用 SSE2 支持进行编译会产生类似的结果(尽管使用 SSE2 指令)。我怀疑 Agner Fog 的原始解决方案可能会针对同一件事进行优化(如果不这样做会很疯狂)。不知道 Marat 的原始解决方案如何在优化的构建中进行比较,但对我来说,为所有比 SSE2 更新并包括 SSE2 的 x86 simd 扩展提供单一方法非常好。

于 2015-03-19T21:52:55.337 回答
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;
}
于 2011-11-19T19:52:48.753 回答
9

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

于 2011-11-19T11:30:40.153 回答