如果您使用的是AVX2,您可以使用PMOVZX將您的字符零擴展爲256位寄存器中的32位整數。從那裏,轉換爲浮動可以在原地發生。
; rsi = new_image
VPMOVZXBD ymm0, [rsi] ; or SX to sign-extend (Byte to DWord)
VCVTDQ2PS ymm0, ymm0 ; convert to packed foat
我在Scaling byte pixel values (y=ax+b) with SSE2 (as floats)?上的回答可能是相關的。如果與AVX2 packssdw/packuswb
一起使用,那麼後面的數據包返回字節是非常棘手的,因爲它們在內部工作,與vpmovzx
不同。因爲這個答案的提交
兩個海灣合作委員會的錯誤:
由於只有AVX1,不AVX2,你應該做的:
VPMOVZXBD xmm0, [rsi]
VPMOVZXBD xmm1, [rsi+4]
VINSERTF128 ymm0, ymm0, xmm1, 1 ; put the 2nd load of data into the high128 of ymm0
VCVTDQ2PS ymm0, ymm0 ; convert to packed foat. Yes, works without AVX2
當然,你永遠需要浮動的陣列,只是__m256
載體。
我實際上找不到一種方法來做到這一點,既是安全的(避免加載期望的8B與-O0
以外)和最佳(使用-O3
良好的代碼)。
使用SSE4.1 pmovsx
/pmovzx
作爲一個負載,只有一個__m128i
源操作數沒有內在。但是,他們只會讀取實際使用的數據量。與punpck*
不同,您可以在頁面的最後8B使用此功能而不發生錯誤。 (即使在非AVX版本的情況下也可以在未對齊的地址上)。
有一個內在的movq
,但gcc 5.3沒有看到它,仍然將負載放入內存操作數vpmovzx
。所以這個函數被編譯成3條指令。鏗鏘3.6確實將movq摺疊成pmovzx的內存操作數,但是鏗鏘聲3.5.1不會。 ICC13也製作最佳代碼。
所以這裏是我提出的惡毒解決方案。不要使用這個,#ifdef __OPTIMIZE__
是壞的。
#if !defined(__OPTIMIZE__)
// Making your code compile differently with/without optimization is a TERRIBLE idea
// great way to create Heisenbugs that disappear when you try to debug them.
// Even if you *plan* to always use -Og for debugging, instead of -O0, this is still evil
#define USE_MOVQ
#endif
__m256 load_bytes_to_m256(uint8_t *p)
{
#ifdef USE_MOVQ // compiles to an actual movq then movzx reg, reg with gcc -O3
__m128i small_load = _mm_cvtsi64_si128(*(uint64_t*)p);
#else // USE_LOADU // compiles to a 128b load with gcc -O0, potentially segfaulting
__m128i small_load = _mm_loadu_si128((__m128i*)p);
#endif
__m256i intvec = _mm256_cvtepu8_epi32(small_load);
//__m256i intvec = _mm256_cvtepu8_epi32(*(__m128i*)p); // compiles to an aligned load with -O0
return _mm256_cvtepi32_ps(intvec);
}
隨着USE_MOVQ啓用,gcc -O3
(v5.3.0) emits
load_bytes_to_m256(unsigned char*):
vmovq xmm0, QWORD PTR [rdi]
vpmovzxbd ymm0, xmm0
vcvtdq2ps ymm0, ymm0
ret
愚蠢vmovq
是我們希望避免的。如果你讓它使用loadu_si128
版本,它會做出優化的代碼。
寫作與內在的唯一AVX1版本留作無趣讀者練習。您要求提供「指令」,而不是「內部函數」,這是內部函數存在差距的一個地方。不得不使用_mm_cvtsi64_si128
來避免潛在的從越界地址加載是愚蠢的,IMO。我希望能夠根據它們映射的指令來考慮內在函數,加載/存儲內在函數通知編譯器關於對齊保證或缺少對齊。不得不使用內在的指令,我不想要的是非常愚蠢的。
另外請注意,如果你正在尋找在英特爾的insn參考手冊,也有MOVQ兩個單獨的條目:
MOVD/MOVQ,可以有一個整數寄存器作爲一個src版本/ (V)MOVQ xmm,r/m64)的目標操作數(66 REX.W 0F 6E
(或VEX.128.66.0F.W1 6E
))。這就是你會發現可以接受64位整數的內在因素的地方。
movq:可以將兩個xmm寄存器作爲操作數的版本。這是MMXreg - > MMXreg指令的擴展,它也可以像MOVDQU一樣加載/存儲。其操作碼F3 0F 7E
(VEX.128.F3.0F.WIG 7E
)爲MOVQ xmm, xmm/m64)
。這裏列出的唯一內在因素是m128i _mm_mov_epi64(__m128i a)
,用於在複製矢量的同時清零矢量的高64位。
這真是愚蠢的。 gcc甚至沒有爲32位目標定義_mm_cvtsi64_si128
。 vmovq xmm, r/m64
當然不能在32位模式下編碼,因爲它依賴於VEX.W(或者非AVX編碼的REX前綴),並且可以將64位寄存器編碼爲源,而不是64位存儲器位置。你也許可以使用MMX寄存器的內部負載,然後mmx - > xmm,然後_mm_mov_epi64
,但它可能不會優化通過mmx寄存器的反彈。
ICC13確定了32位的_mm_cvtsi64_si128
,but with -O0
it compiles to 2xvmovd
+ vpunpckldq
。 。?它管理使用vmovq
與-O3
,雖然(for a separate test function) even in 32bit mode所以它不是停留模仿新空房禁地方式
你檢查優化的彙編編譯器,您可能已經爲你做這個 – Drop
類似問題到http:/ /sackoverflow.com/questions/32284106/scaling-byte-pixel-values-y-axb-with-sse2-as-floats。其中一個更廣泛,並詢問具體的處理任務,但我的答案包括開箱浮動和回(對於SSE,而不是AVX),如果你可以使用它,我們也可以使用16位定點建議。 –