如何使用SSE2实现8位madd 从英特尔C++内部引用的官方阅读中,

如何使用SSE2实现8位madd 从英特尔C++内部引用的官方阅读中,,c++,video,sse,simd,sse2,C++,Video,Sse,Simd,Sse2,SSE 2具有以下命令 __m128i _mm_madd_epi16(__m128i a, __m128i b) 将a中的8个有符号16位整数乘以b中的8个有符号16位整数。 成对添加带符号32位整数结果,并打包4个带符号32位整数 结果 而SSE 3 __m128i _mm_maddubs_epi16 (__m128i a, __m128i b) 将有符号和无符号字节相乘,添加有符号字的水平对,打包 饱和符号词 由于我使用的是8位像素,我必须只使用SSE 2(目标是旧体系结构),因此我需要

SSE 2具有以下命令

__m128i _mm_madd_epi16(__m128i a, __m128i b)
将a中的8个有符号16位整数乘以b中的8个有符号16位整数。 成对添加带符号32位整数结果,并打包4个带符号32位整数 结果

而SSE 3

__m128i _mm_maddubs_epi16 (__m128i a, __m128i b)
将有符号和无符号字节相乘,添加有符号字的水平对,打包 饱和符号词

由于我使用的是8位像素,我必须只使用SSE 2(目标是旧体系结构),因此我需要8位madd指令。
我该怎么做呢?

希望这能奏效-我这里没有编译器。但即使我遗漏了什么,你也应该了解整体情况

编辑:感谢@Peter Cordes指出,最好直接使用
\u mm\u setzero\u si128

inline __m128i _mm_madd_epi8_SSE2(const __m128i & a, const __m128i & b)
{
    // a = 0x00 0x01 0xFE 0x04 ...
    // b = 0x00 0x02 0x80 0x84 ...

    // To extend signed 8-bit value, MSB has to be set to 0xFF
    __m128i sign_mask_a  = _mm_cmplt_epi8(a, _mm_setzero_si128());
    __m128i sign_mask_b  = _mm_cmplt_epi8(b, _mm_setzero_si128());

    // sign_mask_a = 0x00 0x00 0xFF 0x00 ...
    // sign_mask_b = 0x00 0x00 0xFF 0xFF ...

    // Unpack positives with 0x00, negatives with 0xFF
    __m128i a_epi16_l    = _mm_unpacklo_epi8(a, sign_mask_a);
    __m128i a_epi16_h    = _mm_unpackhi_epi8(a, sign_mask_a);
    __m128i b_epi16_l    = _mm_unpacklo_epi8(b, sign_mask_b);
    __m128i b_epi16_h    = _mm_unpackhi_epi8(b, sign_mask_b);

    // Here - valid 16-bit signed integers corresponding to the 8-bit input
    // a_epi16_l = 0x00 0x00 0x01 0x00 0xFE 0xFF 0x04 0x00 ... 

    // Get the a[i] * b[i] + a[i+1] * b[i+1] for both low and high parts
    __m128i madd_epi32_l = _mm_madd_epi16(a_epi16_l, b_epi16_l);
    __m128i madd_epi32_h = _mm_madd_epi16(a_epi16_h, b_epi16_h);

    // Now go back from 32-bit values to 16-bit values & signed saturate
    return _mm_packs_epi16(madd_epi32_l, madd_epi32_h);
}

设置编译器标志,包括标题,调用函数,同时注意对齐:)使用punpckhbw和punpcklbw将8位无符号数据扩展到16位,并使用_-mm_-madd_-epi16。@user874877顺便说一下
u-m128i u-mm_-maddubs_-epi16(u-m128i a,u-m128i-b)
内在在SSSE3中,而不是在SSE3中进行整数比较,只有
cmpeq
cmpgt
(直到AVX512给我们一个整数比较指令,该指令需要一个立即字节来选择谓词,如
cmpps
)。我们可以使用
\u mm\u cmpgt\u epi8(\u mm\u setzero\u si128(),a)
来制作符号扩展的上半部分。另外,不要使用
静态…=_mm_setzero_si128()
对于gcc,
\u mm_setzero
实际上不是编译时常量,因此您将得到在运行时将一些内存归零的代码。另外,您也不想从内存中加载所有的零
px或xmm1,xmm1
比负载便宜。只需直接使用
\u mm\u setzero\u si128()
或将其分配给非静态
常量m128i
局部变量即可。与
\u mm\u set\u epi8(一些数据)
相同:只需让编译器担心常量的管理。OP说他正在使用“8位像素”,这可能是无符号的。因此,您应该能够进行无符号解包(即在高字节中仅与0交叉)。对于RGB颜色模型,这将是正确的,但对于Y'CbCr,有两种能量映射方案:无符号Y´、两个补码Cb、Cr和无符号Y´、偏移二进制Cb、Cr,它们相应地使用有符号和无符号数据类型。由于OP在他的帖子中只提到了
epi16
例程,我假设他需要的
madd
应该是
epi8
,而不是
epu8
。并且re:xor zeroring:
px或same,same
是a,所以它实际上没有意义上的延迟:它总是新依赖链的开始。在Intel SnB系列CPU上,它甚至不需要执行单元,因此每0.25个周期就有一个吞吐量。(在寄存器重命名阶段通过重命名为物理零寄存器来处理)。