2017-09-01 9 views
0

私は現在、画像を操作するプロジェクトに取り組んでいます。プロセスをスピードアップして知識を増やすために、私はSIMD命令を使っていくつかの基本機能を書くことにしました。SIMD - > uint16_t配列を浮動小数点配列にfloatしてからuint16_tに戻す

ループためを使用したコードは、このループは、おそらく完璧ではありません

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にデータを保存し直さなければならないところです。まず、の行でコメントすると、のコードは機能しません読めるように、私は変数を使用しません。第二に、データをuint16_t配列(POINT 2)に格納するのに使ったトリックよりも優れた解決策があると推測しましたが、動作しているものが見つかりません。

誰かが私に正しい方向を指示できますか?私は何が欠けていますか?このコードをどうすれば改善できますか?

ありがとうございます!

PS:Linux(Fedora 25)の並列スタジオプロフェッショナルエディション2117には、Intelコンパイラ2017を使用しています。

+2

このようなブレンドは、レーン幅の変更がないため、固定小数点で簡単ですが、実際には簡単です。 – harold

答えて

1

あなたがようPOINT 2のすべてを再作成することができます:

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

また、あなたはどこにでもアライメントを保証していないようですので、_mm_load_si128のすべてのインスタンスは、おそらく、_mm_loadu_si128でなければならないことに注意してください。

関連する問題