8

ベクトル組み込み関数を使用して個人の画像処理ライブラリを書き直すことにより、SIMD機能の使用方法を学んでいます。基本的な機能の1つは、単純な「配列+=」です。

void arrayAdd(unsigned char* A, unsigned char* B, size_t n) {
    for(size_t i=0; i < n; i++) { B[i] += A[i] };
}

任意の配列長の場合、明らかなSIMDコード(16で整列されていると仮定)は次のようになります。

size_t i = 0;
__m128i xmm0, xmm1;
n16 = n - (n % 16);
for (; i < n16; i+=16) {
    xmm0 = _mm_load_si128( (__m128i*) (A + i) );
    xmm1 = _mm_load_si128( (__m128i*) (B + i) );
    xmm1 = _mm_add_epi8( xmm0, xmm1 );
    _mm_store_si128( (__m128i*) (B + i), xmm1 );
}
for (; i < n; i++) { B[i] += A[i]; }

しかし、SIMD命令ですべての追加を行うことは可能ですか?私はこれを試してみようと思いました:

__m128i mask = (0x100<<8*(n - n16))-1;
_mm_maskmoveu_si128( xmm1, mask, (__m128i*) (B + i) );

余分な要素のために、しかしそれは未定義の振る舞いをもたらすでしょうか?配列のmask境界を超えて実際にアクセスが行われないことを保証する必要があります(私は思います)。別の方法は、最初に追加の要素を実行することですが、次に配列をで整列させる必要がありますがn-n16、これは正しくないようです。

ベクトル化されたループのような別のより最適なパターンはありますか?

4

1 に答える 1

7

1 つのオプションは、配列を 16 バイトの倍数にパディングすることです。次に、128 ビットのロード/追加/ストアを実行し、関心のあるポイントに続く結果を単純に無視できます。

大規模な配列の場合、バイトごとの「エピローグ」のオーバーヘッドは非常に小さくなります。ループをアンロールすると、次のようにパフォーマンスがさらに向上する場合があります。

for (; i < n32; i+=32) {
    xmm0 = _mm_load_si128( (__m128i*) (A + i) );
    xmm1 = _mm_load_si128( (__m128i*) (B + i) );
    xmm2 = _mm_load_si128( (__m128i*) (A + i + 16) );
    xmm3 = _mm_load_si128( (__m128i*) (B + i + 16) );
    xmm1 = _mm_add_epi8( xmm0, xmm1 );
    xmm3 = _mm_add_epi8( xmm2, xmm3 );
    _mm_store_si128( (__m128i*) (B + i), xmm1 );
    _mm_store_si128( (__m128i*) (B + i + 16), xmm3 );
}
// Do another 128 bit load/add/store here if required

しかし、プロファイリングを行わずに言うのは難しいです。

最後にアライメントされていないロード/ストアを実行することもできます (16 バイト以上あると仮定します) が、これはおそらく大きな違いにはなりません。たとえば、20 バイトの場合、オフセット 0 に対して 1 つのロード/ストアを実行し、オフセット 4 に対して別のアライメントされていないロード/追加/ストア ( _mm_storeu_si128__mm_loadu_si128) を実行します。

使用できます_mm_maskmoveu_si128が、マスクを xmm レジスターに入れる必要があり、サンプル コードは機能しません。マスク レジスタをすべての FF に設定してから、シフトを使用して整列させたいと思うでしょう。最終的には、アラインされていないロード/追加/ストアよりも遅くなる可能性があります。

これは次のようになります。

mask = _mm_cmpeq_epi8(mask, mask); // Set to all FF's
mask = _mm_srli_si128(mask, 16-(n%16)); // Align mask
_mm_maskmoveu_si128(xmm, mask, A + i);
于 2012-04-16T01:19:07.073 に答える