2015-12-15 41 views
4

我正在優化圖像上的高斯模糊算法,我想用__m256內在函數替換下面代碼中的浮點緩衝區[8]的用法變量。什麼系列的指令最適合這項任務?將8個字符從內存加載到__m256變量中作爲打包的單精度浮點數

// unsigned char *new_image is loaded with data 
... 
    float buffer[8]; 

    buffer[x ]  = new_image[x];  
    buffer[x + 1] = new_image[x + 1]; 
    buffer[x + 2] = new_image[x + 2]; 
    buffer[x + 3] = new_image[x + 3]; 
    buffer[x + 4] = new_image[x + 4]; 
    buffer[x + 5] = new_image[x + 5]; 
    buffer[x + 6] = new_image[x + 6]; 
    buffer[x + 7] = new_image[x + 7]; 
// buffer is then used for further operations 
... 

//What I want instead in pseudocode: 
__m256 b = [float(new_image[x+7]), float(new_image[x+6]), ... , float(new_image[x])]; 
+0

你檢查優化的彙編編譯器,您可能已經爲你做這個 – Drop

+0

類似問題到http:/ /sackoverflow.com/questions/32284106/scaling-byte-pixel-values-y-axb-with-sse2-as-floats。其中一個更廣泛,並詢問具體的處理任務,但我的答案包括開箱浮動和回(對於SSE,而不是AVX),如果你可以使用它,我們也可以使用16位定點建議。 –

回答

6

如果您使用的是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 7EVEX.128.F3.0F.WIG 7E)爲MOVQ xmm, xmm/m64)。這裏列出的唯一內在因素是m128i _mm_mov_epi64(__m128i a),用於在複製矢量的同時清零矢量的高64位。

這真是愚蠢的。 gcc甚至沒有爲32位目標定義_mm_cvtsi64_si128vmovq xmm, r/m64當然不能在32位模式下編碼,因爲它依賴於VEX.W(或者非AVX編碼的REX前綴),並且可以將64位寄存器編碼爲源,而不是64位存儲器位置。你也許可以使用MMX寄存器的內部負載,然後mmx - > xmm,然後_mm_mov_epi64,但它可能不會優化通過mmx寄存器的反彈。

ICC13確定了32位的_mm_cvtsi64_si128but with -O0 it compiles to 2xvmovd + vpunpckldq。 。?它管理使用vmovq-O3,雖然(for a separate test function) even in 32bit mode所以它不是停留模仿新空房禁地方式

+0

我根據你在VS2015中引用的評論與AVX2一起試了一下,用'__m256i m = _mm256_cvtepu8_epi32(( __m128i)(new_image + x));'它不會編譯。查看[intel intrinsics頁面](https://software.intel.com/sites/landingpage/IntrinsicsGuide/)該指令具體需要__m128i。是否有理由相信我嘗試過的那種演員陣容會奏效?我注意到VPMOVZXBD確實需要一個內存操作數,所以奇怪的是一個內在的東西不會。 – pseudomarvin

+1

@pseudomarvin:在演員陣前注意'*'解除引用操作符。你錯過了,所以你傳遞了一個指向'_mm256_cvtepu8_epi32'的指針,而不是'__m128i'。另外,我建議使用'_mm_loadu_si128',這樣你的代碼在使用'-O0'編譯時不太可能崩潰。如果你想要的代碼仍然不能在數組邊界之外訪問,即使使用'-O0',你也必須使用'_mm_cvtsi64_si128',但是就像我的gcc bug報告一樣,那麼加載不會摺疊到內存中「pmovzxbd」的操作數。你是對的,這是奇怪的,並且是一個糟糕的內部設計。 –

相關問題