12

VS2012のC++コンパイラで自動ベクトル化できない理由について私が見ていない以下のループコードについて明らかなことを誰かが見ていますか?

コンパイラが私に与えるのは、コマンドラインスイッチinfo C5002: loop not vectorized due to reason '1200'を使用するときだけです。/Qvec-report:2

Reason 1200は、MSDNで次のように文書化されています。

ループには、ベクトル化を妨げるループで運ばれるデータ依存関係が含まれています。ループの異なる反復は互いに干渉し合うため、ループをベクトル化すると間違った答えが生成され、自動ベクトル化機能はそのようなデータ依存性がないことを証明できません。

ループで運ばれるデータの依存関係がないことは知っていますが(またはかなり確信しています)、コンパイラーがこれを実現するのを妨げている原因はわかりません。

これらsourcedestポインタは同じメモリをオーバーラップしたりエイリアスしたりすることはなく、コンパイラにそのヒントを提供しようとしてい__restrictます。

pitch4096これは8bpp->32bppのレンダリング/変換関数であり、列ごとに動作するため、画面の解像度に応じて、は常に正の整数値になります。

byte  * __restrict source;
DWORD * __restrict dest;
int pitch;

for (int i = 0; i < count; ++i) {
    dest[(i*2*pitch)+0] = (source[(i*8)+0]);
    dest[(i*2*pitch)+1] = (source[(i*8)+1]);
    dest[(i*2*pitch)+2] = (source[(i*8)+2]);
    dest[(i*2*pitch)+3] = (source[(i*8)+3]);

    dest[((i*2+1)*pitch)+0] = (source[(i*8)+4]);
    dest[((i*2+1)*pitch)+1] = (source[(i*8)+5]);
    dest[((i*2+1)*pitch)+2] = (source[(i*8)+6]);
    dest[((i*2+1)*pitch)+3] = (source[(i*8)+7]);
}

それぞれの周りのsource[]親は、最も単純な形式で、関数呼び出しなしではループが自動ベクトル化されないため、ここで省略した関数呼び出しの残骸です。

編集:

ループを単純化して、次のような最も簡単な形式にしました。

for (int i = 0; i < 200; ++i) {
    dest[(i*2*4096)+0] = (source[(i*8)+0]);
}

これでも、同じ1200理由コードが生成されます。

編集(2):

ローカル割り当てと同一のポインタータイプを使用したこの最小限のテストケースでも、自動ベクトル化に失敗します。私はこの時点で単に困惑しています。

const byte * __restrict source;
byte * __restrict dest;
source = (const byte * __restrict ) new byte[1600];
dest = (byte * __restrict ) new byte[1600];
for (int i = 0; i < 200; ++i) {
    dest[(i*2*4096)+0] = (source[(i*8)+0]);
}
4

2 に答える 2

11

このループがベクトル化するのを妨げるものがいくつかあるとだけ言っておきましょう...

このことを考慮:

int main(){
    byte  *source = new byte[1000];
    DWORD *dest   = new DWORD[1000];

    for (int i = 0; i < 200; ++i) {
        dest[(i*2*4096)+0] = (source[(i*8)+0]);
    }
    for (int i = 0; i < 200; ++i) {
        dest[i*2*4096] = source[i*8];
    }
    for (int i = 0; i < 200; ++i) {
        dest[i*8192] = source[i*8];
    }
    for (int i = 0; i < 200; ++i) {
        dest[i] = source[i];
    }
}

コンパイラ出力:

main.cpp(10) : info C5002: loop not vectorized due to reason '1200'
main.cpp(13) : info C5002: loop not vectorized due to reason '1200'
main.cpp(16) : info C5002: loop not vectorized due to reason '1203'
main.cpp(19) : info C5002: loop not vectorized due to reason '1101'

これを分解してみましょう:

  1. 最初の2つのループは同じです。1200したがって、それらはループによって運ばれる依存関係である元の理由を与えます。

  2. 3番目のループは2番目のループと同じです。しかし、コンパイラには別の理由があり1203ます。

    ループ本体には、配列への非連続アクセスが含まれます

    わかりました...なぜ別の理由ですか?私は知らないよ。しかし今回はその理由が正しいです。

  3. 4番目のループは次のようになります1101

    ループにベクトル化できない変換操作が含まれている(暗黙的である可能性があります)

    pmovzxbdしたがって、VC++はSSE4.1命令を発行するほど賢くはありません。

    これはかなりニッチなケースです。最近のコンパイラでこれができるとは思っていませんでした。可能であれば、SSE4.1を指定する必要があります。


したがって、異常なのは、最初のループがループで運ばれる依存関係を報告する理由だけです。
まあ、それは難しい呼び出しです...私はこれまでのところ、コンパイラが正しい理由を発行していないと言っています。(実際に非連続アクセスである必要がある場合。)

要点に戻ると、MSVCやコンパイラが元のループをベクトル化できるとは思いません。元のループには、4つのチャンクにグループ化されたアクセスがあります。これにより、ベクトル化するのに十分な連続性が得られます。しかし、コンパイラーがそれを認識できることを期待するのは長い道のりです。

したがって、重要な場合は、このループを手動でベクトル化することをお勧めします。必要な本質はです_mm_cvtepu8_epi32()


元のループ:

for (int i = 0; i < count; ++i) {
    dest[(i*2*pitch)+0] = (source[(i*8)+0]);
    dest[(i*2*pitch)+1] = (source[(i*8)+1]);
    dest[(i*2*pitch)+2] = (source[(i*8)+2]);
    dest[(i*2*pitch)+3] = (source[(i*8)+3]);

    dest[((i*2+1)*pitch)+0] = (source[(i*8)+4]);
    dest[((i*2+1)*pitch)+1] = (source[(i*8)+5]);
    dest[((i*2+1)*pitch)+2] = (source[(i*8)+6]);
    dest[((i*2+1)*pitch)+3] = (source[(i*8)+7]);
}

次のようにベクトル化します。

for (int i = 0; i < count; ++i) {
    __m128i s0 = _mm_loadl_epi64((__m128i*)(source + i*8));
    __m128i s1 = _mm_unpackhi_epi64(s0,s0);

    *(__m128i*)(dest + (i*2 + 0)*pitch) = _mm_cvtepu8_epi32(s0);
    *(__m128i*)(dest + (i*2 + 1)*pitch) = _mm_cvtepu8_epi32(s1);
}

免責事項:これはテストされておらず、配置を無視します。

于 2012-12-23T01:12:13.567 に答える
2

MSDNのドキュメントから、エラー1203が報告されるケース

void code_1203(int *A)
{
    // Code 1203 is emitted when non-vectorizable memory references
    // are present in the loop body. Vectorization of some non-contiguous 
    // memory access is supported - for example, the gather/scatter pattern.

    for (int i=0; i<1000; ++i)
    {
        A[i] += A[0] + 1;       // constant memory access not vectorized
        A[i] += A[i*2+2] + 2;  // non-contiguous memory access not vectorized
    }
}

自動ベクトル化を混乱させるのは、実際にはインデックスでの計算である可能性があります。面白いことに、表示されているエラーコードは1203ではありません。

MSDNParallelizerおよびVectorizerメッセージ

于 2012-12-23T01:11:57.650 に答える