2017-09-01 71 views
0

我目前正在處理一個處理圖像的項目。爲了加速這個過程(並增加我的知識),我決定使用SIMD指令編寫一些基本功能。SIMD - > uint16_t數組浮動浮點數組然後返回到uint16_t

使用for循環的代碼是

int idx; 
uint16_t* A, B, C; 
float gAlpha = 0.8; 
float alpha = 0.2; 
for (size_t rw = 0; rw < height; rw++) { 
    for (size_t cl = 0; cl < width; cl++) { 
      idx = rw * width + height; 
      C[idx] = static_cast<uint16_t>(gAlpha * static_cast<float>(A[idx]) + alpha * static_cast<float>(B[idx])); 
     } 
    } 
} 

這個循環可能不是完美的,但它完美地讓自己的工作和我的單元測試給了我預期的結果。

正如我所說的,我試圖用SIMD內在的轉換這些循環。這是我的工作代碼,正如你將會看到的那樣,它不是很漂亮......我們確實可以使用AVX2以上的固有代碼。

size_t n_pixels = height * width; 
for (size_t px = 0; px < n_pixels; px += 8) { 
    __m128i xlo = _mm_unpacklo_epi16(_mm_load_si128((__m128i*)&A[px]), _mm_set1_epi16(0)); 
    __m128i xhi = _mm_unpackhi_epi16(_mm_load_si128((__m128i*)&A[px]), _mm_set1_epi16(0)); 
    __m128 ylo = _mm_cvtepi32_ps(xlo); 
    __m128 yhi = _mm_cvtepi32_ps(xhi); 
    __m256 pxMinFl = _mm256_castps128_ps256(ylo); 
    pxMinFl = _mm256_insertf128_ps(pxMinFl, yhi, 1); 

    xlo = _mm_unpacklo_epi16(_mm_load_si128((__m128i*)&B[px]), _mm_set1_epi16(0)); 
    xhi = _mm_unpackhi_epi16(_mm_load_si128((__m128i*)&B[px]), _mm_set1_epi16(0)); 
    ylo = _mm_cvtepi32_ps(xlo); 
    yhi = _mm_cvtepi32_ps(xhi); 
    __m256 pxMaxFl = _mm256_castps128_ps256(ylo); 
    pxMaxFl = _mm256_insertf128_ps(pxMaxFl, yhi, 1); 

    __m256 avGain1 = _mm256_set1_ps(gAlpha); 
    __m256 avGain2 = _mm256_set1_ps(alpha); 

    __m256 prodUp = _mm256_mul_ps(prodUp, avGain1); 
    __m256 prodBt = _mm256_mul_ps(prodBt, avGain2); 
    __m256 pxOutFl = _mm256_add_ps(prodUp, prodBt); 

    __m128 ylo_ps = _mm256_castps256_ps128(pxOutFl); 
    __m128 yhi_ps = _mm256_extractf128_ps(pxOutFl, 1); 
    __m128i xlo_ep = _mm_cvtps_epi32(ylo_ps); 
    __m128i xhi_ep = _mm_cvtps_epi32(yhi_ps); <- POINT 1 

    int* xl = reinterpret_cast<int*>(&xlo_ep); <- POINT 2 
    for (int i=0; i < 8; i++) {    <- POINT 2 
     C[px + i] = static_cast<uint16_t>(xl[i]); <- POINT 2 
    } 
} 

有可能噸的優化,可以在這個代碼來完成,但我已經檢查了的pxOutFl輸出對應於預期值。對我而言,開始看起來像是什麼樣的黑魔法就是當我看到如何將數據保存回輸出陣列C.首先,如果我在POINT 1處評論該行,則代碼不起作用如果你可以閱讀,我不使用這個變量。其次,我會猜想有一個比我用來將數據存儲回uint16_t數組(POINT 2)的技巧更好的解決方案,但是我找不到一個可行的解決方案。

難道有人能指引我進入正確的方向嗎?我錯過了什麼?我怎樣才能改善這個代碼?

在此先感謝! PS:我們使用英特爾編譯器2017作爲Linux(Fedora 25)上的平行工作室專業版2117。

+2

這樣的混合在固定點很容易,實際上更容易,因爲不會改變車道寬度。 – harold

回答

1

您可以重新編寫所有POINT 2爲:

_mm_storeu_si128((__m128i *)&C[px], xlo_ep); 

還要指出的是_mm_load_si128所有實例也許應該_mm_loadu_si128,因爲你似乎沒有在任何地方保證對齊。