0

SSE2命令を含む次のコードが、標準のC++実装よりも乗算の実行速度が遅いのはなぜか疑問に思います。コードは次のとおりです。

        m_win = (double*)_aligned_malloc(size*sizeof(double), 16);
        __m128d* pData = (__m128d*)input().data;
        __m128d* pWin = (__m128d*)m_win;
        __m128d* pOut = (__m128d*)m_output.data;
        __m128d tmp;
        int i=0;
        for(; i<m_size/2;i++)
            pOut[i] = _mm_mul_pd(pData[i], pWin[i]);

のメモリm_output.datainput().dataは_aligned_mallocで割り当てられています。

ただし、2 ^ 25アレイの場合、このコードを実行する時間は、このコードの時間(350ms)と同じです。

for(int i=0;i<m_size;i++)
    m_output.data[i] = input().data[i] * m_win[i];

そんなことがあるものか?理論的には50%の時間しかかからないはずですよね?または、SIMDレジスタからm_output.data配列へのメモリ転送のオーバーヘッドは非常に高価ですか?

最初のスニペットの行を置き換えると

pOut[i] = _mm_mul_pd(pData[i], pWin[i]);

tmp = _mm_mul_pd(pData[i], pWin[i]);

ここで__m128d tmp;、コードは非常に高速に実行され、タイマー関数の解像度よりも低くなります。それは、すべてがメモリではなくレジスタに格納されているためですか?

さらに驚くべきことに、デバッグモードでコンパイルすると、SSEコードは93ミリ秒しかかかりませんが、標準の乗算は309ミリ秒かかります

  • デバッグ:93ms(SSE2)/ 309ms(標準乗算)
  • リリース:350ms(SSE2)/ 350(標準乗算)

何が起きてる???

QtCreator2.2.1でMSVC2008をリリースモードで使用しています。RELEASE用のコンパイラスイッチは次のとおりです。

cl -c -nologo -Zm200 -Zc:wchar_t- -O2 -MD -GR -EHsc -W3 -w34100 -w34189

これらはDEBUG用です:

cl -c -nologo -Zm200 -Zc:wchar_t- -Zi -MDd -GR -EHsc -W3 -w34100 -w34189

RELEASE vs DEBUGの問題に関する編集:コードのプロファイルを作成したところ、リリースモードでは SSEコードの速度が実際に遅くなっていることに注意してください。 これは、VS2008がオプティマイザで組み込み関数を適切に処理できないという仮説をどういうわけか確認するだけです。Intel VTuneは、DEBUGのSSEループで289ミリ秒、RELEASEモードで504ミリ秒を提供します。うわー...ただすごい...

4

2 に答える 2

3

まず第一に、VS 2008 は、必要以上に多くのレジスタ移動を追加する傾向があり、一般的に最適化がうまくいかないため、組み込み関数には不適切な選択です (たとえば、SSE 命令が存在する場合、ループ誘導変数の分析に問題があります)。

したがって、コンパイラはmulss、CPU が簡単に並べ替えて並列に実行できる命令を生成し (反復間の依存関係はありません)、組み込み関数により多くのレジスタ移動/複雑な SSE コードが発生する可能性があります-トレースキャッシュを吹き飛ばす可能性さえあります最新のCPUで。VS2008 はすべての計算をレジスタで行うことで悪名高く、CPU がスキップできないいくつかの危険があると思います (xor reg、move mem->reg、xor、mov mem->reg、mul、mov mem->reg など)。は依存関係の連鎖ですが、スカラー コードは mem->reg の移動、mem オペランドの mul、mov の可能性があります。生成されたアセンブリを確認するか、組み込み関数のサポートがはるかに優れている VS 2010 を試してください。

最後に、そして最も重要なことは、あなたのコードはまったく計算に縛られていないということです。各反復で、4 つの double 値を読み取り、2 つの値を書き込みます。これは、FLOP が問題ではないことを意味します。その場合、あなたはキャッシュ/メモリサブシステムに翻弄されており、それがおそらくあなたが見ている差異を説明しています. デバッグ乗算はリリースよりも高速であってはなりません。そして、それがより多くの実行を行う必要があるよりも高速であることがわかった場合は、他に何が起こっているかを確認します (CPU がターボ モードをサポートしている場合は注意してください。これにより、さらに 20% の変動が追加されます)。キャッシュを空にするコンテキスト スイッチで十分な場合があります。この場合。

したがって、全体として、作成したテストはほとんど無意味であり、メモリにバインドされたケースでは SSE を使用するかどうかに違いがないことを示しています。計算密度が高く並列なコードが実際にある場合は、SSE を使用する必要があります。その場合でも、プロファイラーを使用して、最適化する正確な場所を特定するのに多くの時間を費やします。単純な内積は、SSE によるパフォーマンスの向上を確認するのには適していません。

于 2011-07-03T19:37:00.083 に答える
1

いくつかのポイント:

  • すでに指摘されているように、MSVC は SSE に対してかなり悪いコードを生成します。
  • ロードとストアの間に 1 つの操作しか実行していないため、コードはほぼ確実にメモリ帯域幅が制限されています。
  • 最新の x86 CPU のほとんどは浮動小数点 ALU を 2 つ備えているため、帯域幅に制限がない場合でも、倍精度浮動小数点演算に SSE を使用しても得られるものはほとんどない可能性があります。
于 2011-07-03T20:05:46.500 に答える