如何使用SSE2实现8位madd 从英特尔C++内部引用的官方阅读中,
SSE 2具有以下命令如何使用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(目标是旧体系结构),因此我需要
__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个周期就有一个吞吐量。(在寄存器重命名阶段通过重命名为物理零寄存器来处理)。