我想乘以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. 2個64位整數的SSE乘法
- 4. 幫助組裝/ SSE乘
- 5. 裝配中的16位乘法?
- 6. SSE無符號/符號的16位寄存器減法
- 7. 逐元素乘法(X,Y,1)*(X,Y)
- 8. 爲什麼與SSE的矩陣乘法較慢?
- 9. 使用SSE內在函數的矩陣乘法
- 10. 如何改善基於sse的矩陣乘法
- 11. css em/rem固定乘以16?
- 12. 等效AWK的語法[X] [0] + = $ 16
- 13. 16 x 16色塊組CSS + HTML
- 14. OpenCV中的矩陣矩陣和SSE的16字節對齊
- 15. 將xmm寄存器轉換爲uint8_t數組[16]
- 16. 4位二進制數乘法器3(mod 16)
- 17. uint8_t * const LCDMem =(uint8_t *)&LCDM3;含義
- 18. SSE優化
- 19. C/C++ - uint8_t x中冒號的作用是什麼x:6
- 20. ++((無符號)x)的意外結果,x是uint8_t?
- 21. 二進制乘法 - 負數X負數
- 22. Apache2無法啓動 - Mac OS X - 乘客
- 23. 使用最多SSE 4.2比較兩個16字節的值是否相等?
- 24. SSE整數除法?
- 25. 如何優化矩陣3乘以3與SSE點?
- 26. 使用8085微處理器的16位乘法
- 27. 如何在16位機器上執行64位乘法?
- 28. 乘法
- 29. 無法將uint8_t向量迭代器轉換爲常量uint8_t *
- 30. 從uint8_t *到uint8_t無效轉換fpermissive C++
你能澄清一下你的問題嗎?您是否要將每個16位8位整數的128位整數或16位8位整數的16位整數或16位8位整數乘以一個寄存器來相乘。前一種情況會有點奇怪。 –
只是一個想法,但爲什麼不把8位到16位?如果你想測試溢出,你可以將AH與AH進行比較,看看是否匹配檢查溢出。有點凌亂,只是在黑暗中刺傷。如果直接支持8位mul,也會讓我感到驚訝,因爲SIMD的指令集是爲後8位處理器編寫的 –
@Paul:8位值仍在圖形中使用。 AltiVec具有8位乘法,但每次只有8位乘法結果爲16位。 – Potatoswatter