26

多くの SSE コンパイラ組み込み関数を使用して 3D ベクトル クラスを作成しました。new のメンバーとして 3D ベクトルを持つクラスを開始するまで、すべてがうまくいきました。リリース モードでは奇妙なクラッシュが発生しましたが、デバッグ モードでは発生しませんでした。

そこで、いくつかの記事を読んで、3D ベクター クラスのインスタンスを所有するクラスも 16 バイトに揃える必要があると考えました。したがって、次のようにクラスの前に_MM_ALIGN16( ) を追加しました。__declspec(align(16)

_MM_ALIGN16 struct Sphere
{
    // ....

    Vector3 point;
    float radius
};

それは最初に問題を解決するように見えました。しかし、いくつかのコードを変更した後、私のプログラムは奇妙な方法で再びクラッシュし始めました。さらにウェブを検索したところ、ブログ記事を見つけました。この問題を解決するために著者の Ernst Hot が行ったことを試してみましたが、私にとってもうまくいきました。次のように、新しい演算子と削除演算子をクラスに追加しました。

_MM_ALIGN16 struct Sphere
{
    // ....

    void *operator new (unsigned int size)
     { return _mm_malloc(size, 16); }

    void operator delete (void *p)
     { _mm_free(p); }

    Vector3 point;
    float radius
};

Ernst は、このアプローチにも問題がある可能性があると述べていますが、問題が発生する理由を説明せずに、もはや存在しないフォーラムにリンクしているだけです。

だから私の質問は:

  1. 演算子を定義する際の問題は何ですか?

  2. _MM_ALIGN16クラス定義への追加が十分でないのはなぜですか?

  3. SSE 組み込み関数に伴うアラインメントの問題を処理する最善の方法は何ですか?

4

3 に答える 3

21

まず、2 種類のメモリ割り当てに注意する必要があります。

  • 静的割り当て。自動変数を適切にアラインするには、型に適切なアラインメント仕様 ( __declspec(align(16))__attribute__((aligned(16)))、または など_MM_ALIGN16) が必要です。しかし幸いなことに、型のメンバー (存在する場合) によって指定されたアライメント要件が十分でない場合にのみ、これが必要になります。あなたがすでに適切に整列していることSphereを考えると、あなたはあなたのためにこれを必要としません。Vector3そして、あなたVector3にメンバーが含まれている場合__m128(そうである可能性はかなり高いですが、そうでない場合はそうすることをお勧めします)Vector3、. したがって、通常、コンパイラ固有のアライメント属性をいじる必要はありません。

  • 動的割り当て。簡単な部分はこれで終わりです。問題は、C++ が最も低いレベルで、動的メモリを割り当てるために型にとらわれないメモリ割り当て関数を使用することです。これは、すべての標準型の適切なアラインメントを保証するだけであり、たまたま 16 バイトになる可能性がありますが、保証されていません。

    これを補うには、ビルトインをオーバーロードしoperator new/deleteて独自のメモリ割り当てを実装し、古き良きの代わりにボンネットの下で整列割り当て関数を使用する必要がありますmalloc。オーバーロードoperator new/deleteはそれ自体がトピックですが、最初に思われるほど難しくはありません (あなたの例では十分ではありませんが) 。この優れた FAQ の質問でそれについて読むことができます。

    残念ながら、非標準のアラインメントを必要とするメンバーを持つ型ごとにこれを行う必要があります。あなたの場合はSphereとの両方Vector3です。しかし、少し簡単にするためにできることは、これらの演算子の適切なオーバーロードを使用して空の基本クラスを作成し、この基本クラスから必要なすべてのクラスを派生させることです。

    ほとんどの人が時々忘れがちなことは、標準のアロケーターはすべてのメモリ割り当てにstd::alocatorグローバルを使用するため、型が標準のコンテナーでは機能しないことです (ユース ケースはそれほどまれではありません)。あなたがする必要があるのは、独自の標準準拠アロケーターを作成し、これを使用することです。しかし、利便性と安全性のために、実際には型に特化した方が実際には(カスタム アロケーターから派生させるだけかもしれません)、それが常に使用され、使用するたびに適切なアロケーターを使用する必要がなくなります。残念ながら、この場合、整列された型ごとに再度特殊化する必要がありますが、小さな邪悪なマクロがそれを助けます。operator newstd::vector<Vector3>std::allocatorstd::vector

    operator new/deleteさらに、カスタムのものの代わりにグローバルを使用して、 や などの他のものstd::get_temporary_bufferstd::return_temporary_buffer注意し、必要に応じてそれらの世話をする必要があります。

残念ながら、ネイティブに 16 に対応するプラットフォームを使用していて、これについて知っている場合を除き、これらの問題に対するより良いアプローチはまだないと思います。または、グローバルoperator new/deleteをオーバーロードして、各メモリ ブロックを常に 16 バイトにアラインし、SSE メンバーを含むすべてのクラスのアラインメントを気にしないようにすることもできますが、このアプローチの意味についてはわかりません。最悪の場合、メモリを浪費するだけですが、通常は C++ で小さなオブジェクトを動的に割り当てることはありません (ただしstd::liststd::mapこれについては別の考え方があるかもしれません)。

要約すると:

  • のようなものを使用して、静的メモリの適切な配置に注意してください__declspec(align(16))。ただし、メンバーによってまだ管理されていない場合に限ります。

  • operator new/delete非標準のアラインメント要件を持つメンバーを持つすべての型のオーバーロード。

  • アラインされた型の標準コンテナーで使用するカスタムの標準準拠アロケーターを作成するか、さらに良いことに、std::allocatorアラインされた型ごとに特化します。


最後に、一般的なアドバイスをいくつか。多くの場合、多くのベクトル演算を実行するときに、計算量の多いブロックで SSE からのみ利益が得られます。このすべてのアラインメントの問題、特に を含むすべての型のアラインメントを処理する問題を単純化するにはVector3、特別な SSE ベクトル型を作成し、通常の非ストレージ変数とメンバー変数の SSE ベクトル。

于 2012-09-20T12:08:17.777 に答える
2

基本的に、SIMDベクトルタイプには通常、どの組み込みタイプよりも大きな位置合わせ要件があるため、ベクトルが適切に位置合わせされていることを確認する必要があります。

それには、次のことを行う必要があります。

  1. Vector3スタックまたは構造体のメンバーにあるときに、それが適切に配置されていることを確認してください。これは、クラス(またはコンパイラでサポートされている属性)に適用__attribute__((aligned(32)))することによって行われます。Vector3を含む構造に属性を適用する必要はないことに注意してください。これは必要Vector3ではなく、十分ではありません(つまり、に適用する必要はありませんSphere)。

  2. Vector3ヒープ割り当てを使用するときは、またはその囲み構造が適切に整列されていることを確認してください。これはposix_memalign()、プレーンを使用する代わりに(またはプラットフォームの同様の機能)を使用することによって、malloc()またはoperator new()後者の2つがSIMDタイプに十分であることが保証されていない組み込みタイプ(通常は8または16バイト)のメモリを整列させるために行われます。

于 2012-09-20T09:49:08.847 に答える
1
  1. 演算子の問題は、それだけでは不十分であるということです。スタックの割り当てには影響しませんが、それでも必要__declspec(align(16))です。

  2. __declspec(align(16))選択できる場合に限り、コンパイラがオブジェクトをメモリに配置する方法に影響します。新しいオブジェクトの場合、コンパイラは。によって返されたメモリを使用する以外に選択肢はありませんoperator new

  3. 理想的には、それらをネイティブに処理するコンパイラを使用します。とは異なる方法で処理する必要がある理論的な理由はありませんdouble。それ以外の場合は、回避策についてコンパイラのドキュメントをお読みください。障害のあるコンパイラにはそれぞれ独自の問題があり、したがって独自の回避策があります。

于 2012-09-20T08:24:03.943 に答える