我想乘以SSE4一个__m128i
对象与16位无符号8位整数,但我只能找到一个内在乘法16位整数。有没有什么比如_mm_mult_epi8
?SSE乘法16 x uint8_t
回答
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;
}
唯一的8位SSE乘法指令是PMADDUBSW(SSSE3及更高版本,C/C++内部函数:_mm_maddubs_epi16)。这将16 x 8位无符号值乘以16 x 8位带符号的值,然后求和相邻对以给出8 x 16位带符号结果。如果你不能使用这个相当专业的指令,那么你需要解压缩成16位向量对,并使用常规的16位乘法指令。显然这意味着至少有2倍的吞吐量,所以如果可能的话使用8位乘法。
一个基于Agner Fog's solution不是萨芬的解决方案(可能)更快捷的方式:
而不是分裂高/低,分裂奇偶。这有一个额外的好处,它可以在纯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的原始解决方案是如何在优化构建中进行比较的,尽管对于所有x86 simd扩展包含比SSE2更新并且包含SSE2的方法,这是相当不错的。
这真的很好。它利用了有符号与无符号只影响N×N - > 2N位乘法的高一半的事实,并且[高位中的垃圾不会影响你想要的低位结果]( http://stackoverflow.com/questions/34377711/which-2s-complement-integer-operations-can-be-used-without-zeroing-high-bits-in)。如果在加载掩码时缓存未命中是一个问题,您可以使用2个insns产生它:'pcmpeqw xmm7,xmm7' /'psrlw xmm7,8'。 (有关其他常量生成序列,请参阅http://stackoverflow.com/q/35085059/224132)。 –
这很整洁,我看到[clang优化左移/右移到带有常量掩码的vpand](http://goo.gl/GmFc9H)!这可能是更好的代码,除非掩码容易在缓存中丢失。 gcc不会做这种优化。 shift和mask之间的选择完全不依赖于AVX2。它取决于内存中的一个大常量是否是你想要的。 (我注意到没有avx,clang最后浪费了一个movdqa:它可能在第二个pmul使用了'pmullw xmm0,xmm1',并在'xmm0'(返回值寄存器)中建立了最终结果。 –
您的评论关于'vpbroadcastw'是完全错误的:大多数编译器不会将'set1'编译成常量的运行时广播,因为它很昂贵。'mov eax,0xff' /'movd xmm0,eax'/vpbroadcastw xmm0,xmm0' is Haswell上的3个uops,'vpbroadcastw xmm0,[mem16]'也是3个uops,动态生成比两者都便宜(但编译器倾向于把它们放在内存中),然而'vpbroadcastd'只有1个uop,即使没有熔断:它只需要一个加载端口,而不是ALU,所以你不需要浪费32B的内存在一个将被加载到循环外部的常量上 –
- 1. 帮助组装/ SSE乘
- 2. 装配中的16位乘法?
- 3. 逐元素乘法(X,Y,1)*(X,Y)
- 4. 使用SSE内在函数的矩阵乘法
- 5. css em/rem固定乘以16?
- 6. 16 x 16色块组CSS + HTML
- 7. 将xmm寄存器转换为uint8_t数组[16]
- 8. 4位二进制数乘法器3(mod 16)
- 9. Apache2无法启动 - Mac OS X - 乘客
- 10. 二进制乘法 - 负数X负数
- 11. 如何优化矩阵3乘以3与SSE点?
- 12. 乘以.val()x 2
- 13. uint8_t乘以布尔值是什么类型?
- 14. 使用8085微处理器的16位乘法
- 15. uint8_t to uint16_t
- 16. 乘法
- 17. 转换8 16位SSE寄存器8位数据
- 18. 为什么在Java中右移16乘32将导致16而不是0? 16 >> 32 = 16为什么?
- 19. 与SSE
- 20. 乘两个16位无符号值,而无需使用乘法和除法指令[8086大会]
- 21. 索引与SSE阵列
- 22. SSE的加法和转换
- 23. 正常乘法的定点乘法
- 24. 嵌套乘法
- 25. SWIT Wrapping for uint8_t和uint16_t
- 26. 标量乘法?
- 27. 数组乘法
- 28. uint8_t iostream行为
- 29. 乘法回归
- 30. SSE并行化
你能澄清一下你的问题吗?您是否要将每个16位8位整数的128位整数或16位8位整数的16位整数或16位8位整数乘以一个寄存器来相乘。前一种情况会有点奇怪。 –
只是一个想法,但为什么不把8位到16位?如果你想测试溢出,你可以将AH与AH进行比较,看看是否匹配检查溢出。有点凌乱,只是在黑暗中刺伤。如果直接支持8位mul,也会让我感到惊讶,因为SIMD的指令集是为后8位处理器编写的 –
@Paul:8位值仍在图形中使用。 AltiVec具有8位乘法,但每次只有8位乘法结果为16位。 – Potatoswatter