4

シェーダーを最適化する必要があり (多くのベクトル操作を使用)、問題をよりよく理解するために SSE 命令を試しています。

非常に単純なサンプルコードがいくつかあります。USE_SSEdefine では、明示的な SSE 組み込み関数を使用します。それがなければ、GCCが私のために仕事をしてくれることを願っています。自動ベクトル化は少し面倒ですが、少しでも節約できることを願っています。

コンパイラとプラットフォーム: gcc 4.7.1 (tdm64)、ターゲット x86_64-w64-mingw32、および Ivy Bridge 上の Windows 7。

テストコードは次のとおりです。

/*
    Include all the SIMD intrinsics.
*/
#ifdef USE_SSE
#include <x86intrin.h>
#endif
#include <cstdio>

#if   defined(__GNUG__) || defined(__clang__) 
    /* GCC & CLANG */

    #define SSVEC_FINLINE __attribute__((always_inline))

#elif defined(_WIN32) && defined(MSC_VER) 
    /* MSVC. */

    #define SSVEC_FINLINE __forceinline

#else
#error Unsupported platform.
#endif


#ifdef USE_SSE

    typedef __m128 vec4f;

    inline void addvec4f(vec4f &a, vec4f const &b)
    {
        a = _mm_add_ps(a, b);
    }

#else

    typedef float vec4f[4];

    inline void addvec4f(vec4f &a, vec4f const &b)
    {
        a[0] = a[0] + b[0];
        a[1] = a[1] + b[1];
        a[2] = a[2] + b[2];
        a[3] = a[3] + b[3];
    }

#endif

int main(int argc, char *argv[])
{
    int const count = 1e7;

    #ifdef USE_SSE
    printf("Using SSE.\n");
    #else
    printf("Not using SSE.\n");
    #endif

    vec4f data = {1.0f, 1.0f, 1.0f, 1.0f};

    for (int i = 0; i < count; ++i)
    {
        vec4f val = {0.1f, 0.1f, 0.1f, 0.1f};
        addvec4f(data, val);
    }

    float result[4] = {0};
    #ifdef USE_SSE
    _mm_store_ps(result, data);
    #else
    result[0] = data[0];
    result[1] = data[1];
    result[2] = data[2];
    result[3] = data[3];
    #endif

    printf("Result: %f %f %f %f\n", result[0], result[1], result[2], result[3]);

    return 0;
}

これは以下でコンパイルされます:

g++ -O3 ssetest.cpp -o nossetest.exe
g++ -O3 -DUSE_SSE ssetest.cpp -o ssetest.exe

明示的な SSE バージョンが少し速いことを除けば、出力に違いはありません。

ループのアセンブリ、最初の明示的な SSE は次のとおりです。

.L3:
subl    $1, %eax
addps   %xmm1, %xmm0
jne .L3

呼び出しをインライン化しました。いいね、多かれ少なかれまっすぐに_mm_add_ps

アレイのバージョン:

.L3:
subl    $1, %eax
addss   %xmm0, %xmm1
addss   %xmm0, %xmm2
addss   %xmm0, %xmm3
addss   %xmm0, %xmm4
jne .L3

SSE数学を問題なく使用していますが、各アレイメンバーで。本当に望ましくありません。

私の質問は、GCC が の配列バージョンをより適切に最適化できるようにするにはどうすればよいvec4fですか?

Linux 固有のヒントも役に立ちます。実際のコードはここで実行されます。

4

2 に答える 2

7

gcc 4.7 を使用した自動ベクトル化に関するこのLockLess記事は、私がこれまでに見た中で最高の記事であり、同様のトピックに関する優れた記事をしばらく探してきました。また、低レベルのソフトウェア開発のあらゆる方法を扱っている同様の主題について非常に役立つ記事が他にもたくさんあります。

于 2013-03-17T23:06:51.340 に答える
5

gcc の自動ベクトル化を機能させるためのコードに基づくヒントを次に示します。

  • ループアップバウンドを const にします。ベクトル化するために、GCC は 128 ビット長の SSE XMM レジスターに収まるようにループを 4 回の反復で分割する必要があります。const ループの上限は、GCC がループに十分な反復があり、ベクトル化が有益であることを確認するのに役立ちます。
  • inlineキーワードを削除します。コードがインラインとしてマークされている場合、GCC は、によってオンにされないプロシージャー間の分析なしでは、配列の開始点が整列されているかどうかを知ることができません-O3

    したがって、コードをベクトル化するには、addvec4f関数を次のように変更する必要があります。

    void addvec4f(vec4f &a, vec4f const &b)
    {
        int i = 0;
        for(;i < 4; i++)
          a[i] = a[i]+b[i];
    }
    

ところで:

  • GCC には、ループがベクトル化されているかどうかを確認するのに役立つフラグもあります。-ftree-vectorizer-verbose=2、数値が大きいほど出力情報が多くなります。現在、値は012です。このフラグとその他の関連フラグのドキュメントを次に示します。
  • 位置合わせに注意してください。配列のアドレスはアラインされている必要があり、コンパイラは実行しないとアドレスがアラインされているかどうかを知ることができませんbus error。通常、データがアラインされていない場合は . これが理由です。
于 2013-03-19T08:47:50.793 に答える