2

わかりましたので、コードで C 組み込み関数を使い始めたばかりで、クラ​​スを作成しました。これは次のように簡略化されています。

class _Vector3D
{
public:
_Vector3D() 
{
    aVals[0] = _mm_setzero_ps();
    aVals[1] = _mm_setzero_ps();
    aVals[2] = _mm_setzero_ps();
}
~_Vector3D() {}
private:
__m128 aVals[3];
};

ここまでは順調ですね。しかし、_Vector3D メンバーを持つ 2 番目のクラスを作成すると、問題が発生します。

class RayPacket
{
public:
RayPacket() {orig = _Vector3D(); dir = _Vector3D(); power = _mm_setzero_ps();}
~RayPacket() {}

RayPacket(_Vector3D origins, _Vector3D directions, float pow)
{
    orig = origins;
    dir = directions;
    power = _mm_set_ps1(pow);
}

_Vector3D orig;
_Vector3D dir;
__m128 power;
};

次のエラーが表示されます。

エラー C2719: 'origins': __declspec(align('16')) の仮パラメーターは位置合わせされません

コンストラクターのオーバーロードを指しています:

RayPacket(_Vector3D origins, _Vector3D directions, float pow)

それで、私はこれについて間違った方法をとっていますか?代わりに構造体を使用する必要がありますか、それともクラスで動作させることができますか?

4

3 に答える 3

4

_Vector3D問題は、コンパイラーが、コンストラクターに渡すオブジェクトをスタック上に作成するときに、スタックポインターが適切に整列されることを保証できないことだと思います。

32ビットシステムでは、スタックポインタは通常4バイトで整列されることが保証されます(場合によっては8バイトで整列されます)。64ビットシステムでは、スタックポインタは通常8バイトで整列されることが保証されていると思います。したがって、コンパイラはそうしません。コンストラクターを呼び出すときに、スタックが適切に整列されることを認識します。ポインタまたは参照を渡す必要がある場合があります。

malloc()また、ブロックに対して返される保証された配置は、このような特殊なタイプを処理できることが保証されていない場合があることに注意してください。その場合、プラットフォームには、これらのオブジェクトを割り当てるための特別な割り当て機能があります。

MSVC(http://msdn.microsoft.com/en-us/library/aa290049.aspx)の詳細については、以下を参照してください。

スタックの配置

両方の64ビットプラットフォームで、各スタックフレームの上部は16バイトに整列されます。これは必要以上のスペースを使用しますが、コンパイラーがすべての要素を整列させる方法ですべてのデータをスタックに配置できることを保証します。

x86コンパイラは、スタックを整列するために別の方法を使用します。デフォルトでは、スタックは4バイトに整列されます。これはスペース効率に優れていますが、8バイトのアラインメントが必要なデータ型がいくつかあり、良好なパフォーマンスを得るには、16バイトのアラインメントが必要になる場合があります。コンパイラーは、場合によっては、動的な8バイトのスタックアラインメントが有益であると判断できます。特に、スタックに2つの値がある場合はそうです。

コンパイラはこれを2つの方法で行います。まず、コンパイラーは、コンパイル時およびリンク時にユーザーが指定した場合、リンク時コード生成(LTCG)を使用して、完全なプログラムの呼び出しツリーを生成できます。これにより、8バイトのスタックアラインメントが有益となるコールツリーの領域を特定でき、動的スタックアラインメントが最大の見返りを得るコールサイトを特定できます。2番目の方法は、関数がスタック上にdoubleを持っているが、何らかの理由でまだ8バイトにアラインされていない場合に使用されます。コンパイラーは、ヒューリスティック(コンパイラーの反復ごとに改善されます)を適用して、関数を動的に8バイトに整列させる必要があるかどうかを判断します。

注パフォーマンスに関する動的な8バイトスタックアラインメントの欠点は、フレームポインタの省略(/ Oy)が効果的にオフになることです。レジスタEBPは、動的8バイトスタックでスタックを参照するために使用する必要があるため、関数の汎用レジスタとして使用することはできません。

上記のリンクされた記事には、malloc()必要に応じて標準よりも高いアライメント保証を提供する特別なヒープ関数に関する情報も含まれています。

于 2009-10-28T19:43:32.683 に答える
4

この回答は、実際の知識ではなく、文書化と推測に基づいています。注意してください!

__m128のドキュメントには次のように書かれています。

タイプ_m128[sic]の変数は、16バイト境界で自動的に整列されます。

したがって、__m128クラスのメンバーを使用することにより、コンパイラーはクラスのインスタンスを16バイト境界に揃えるように強制されます。暗黙的に、__declspec(align(16))クラスに追加されますが、コンパイラがスタックフレーム内で整列を強制するのは難しい(不可能ですか?)ため、これは関数パラメータでは許可されていません。

回避策として、コンストラクター引数を参照で渡してみてください。

RayPacket(_Vector3D const &origins, _Vector3D const &directions, float pow)
于 2009-10-28T19:39:43.143 に答える
0

_Vector3D次のように、const参照 を渡してみてください。

RayPacket( const _Vector3D& origins, const _Vector3D& directions, float pow );
これにより、値の代わりにポインターが呼び出しスタックに配置されます。

于 2009-10-28T19:40:35.843 に答える