3

バイト最適化によってパフォーマンスがどの程度向上するか (8、32、64 の倍数など)?

サンプル構造は次のとおりです。

[StructLayout(LayoutKind.Explicit)]
public struct RenderItem
{
   [FieldOffset(0)] byte[] mCoordinates = new byte[3]; //(x,y,z)
   [FieldOffset(3)] short  mUnitType;            

}

だから私の質問は、次のようなことをすることがどれほど重要かということです:

[StructLayout(LayoutKind.Explicit)]
public struct RenderItem
{
   [FieldOffset(0)] byte[] mCoordinates = new byte[3]; //(x,y,z)
   [FieldOffset(4)] short  mUnitType;
   [FieldOffset(6)] byte[] mPadding = new byte[2];     //make total to 8 bytes

}

「サイズに合わせてスケーリングする」ものの 1 つであると確信しているため、特に、VertexBuffer オブジェクトを作成するためにこの構造が約 150,000 回使用される操作に興味があります。

//int objType[,,] 3 dimensional int with object type information stored in it

int i = 0;
RenderItem vboItems[16 * 16 * 16 * 36]  //x - 16, y - 16, z - 16, 36 verticies per object

For(int x = 0; x < 16; x++)
{
     For(int y = 0; y < 16; y++)
     {
          For(int z = 0; z < 16; z++)
          {
               vboItems[i++] = (x,y,z,objType[x,y,z]);
          }
     }
 }

 //Put vboItems into a VBO
4

2 に答える 2

13

[MarshalAs] 属性を適用して配列を ByValArray にすると仮定しますが、これはこのような構造で意味のあることだけです。実際には、構造体を2バイト大きくすることで遅くしています。これにより、プロセッサのキャッシュの使用効率が低下し、配列で使用するときに収まる構造体が少なくなります。これは、パフォーマンスにとって非常に重要です。

デフォルトの StructLayoutAttribute.Pack 値 8 は、構造の最適なレイアウトを提供するために既に最適化されています。実際には、構造体には何の影響もありません。Pack の値に関係なく、メンバーは既に最適に配置されています。最新のプロセッサが最高のパフォーマンスを得るためのルール:

  • メンバーは、メンバー サイズで割り切れるアドレスに配置する必要があります。これにより、メンバー間にパディング バイトが追加される場合があります。このルールにより、プロセッサがメモリ読み取りからのバイト値を多重化したり、2 つの読み取りを実行してバイトを結合したりする必要がなくなります。構造体の問題ではありません。位置合わせが必要な唯一のメンバーは mUnitType です。2 で位置合わせする必要があり、既に 4 で位置合わせされています。また、[FieldOffset] を使用する必要がないことに注意してください。デフォルトのレイアウトは既に適切です。 .

  • 構造体が配列で使用されている場合、メンバーは適切に配置されている必要があります。これにより、構造体の最後にパッキングが追加され、配列内の次の要素が適切に配置されます。これも構造体の問題ではありません。長さは 6 バイトであるため、配列の次の要素は 2 しか必要としないため、mUnitType が整列されます。実際に [MarshalAs] なしで配列を宣言した場合、ジッターは自動的に 2 バイトを追加します。配列ポインターが正しく配置されるように、助けを借りずにパディングを行います。

  • メンバーが CPU キャッシュ ラインにまたがってはなりません。私が知っている最新のプロセッサでは、これは 64 バイトです。パフォーマンスに非常に有害です。CPU は 2 つのキャッシュ ラインに相当するデータを読み取って、常にバイトを結合する必要があります。パフォーマンス ヒットは約 3 倍遅くなります。これは、構造体にサイズ 8 以上のメンバーが含まれている場合に、32 ビット マシンで発生する可能性があるものです。long、double、または decimal です。メンバーの配置だけでなく、構造体がメモリ内のどこに割り当てられているかも重要です。これは、x86 バージョンの .NET では少し問題です。スタックまたは GC ヒープから割り当てられたデータの開始アドレスが 4 の倍数に揃えられることのみを保証できます。x64 では問題ありません。構造体の問題ではありません。CPU キャッシュ ラインにまたがることができない小さなメンバーのみが含まれています。

したがって、これらのルールにより、あなたが手助けする必要はありません。構造体は、LayoutKind.Explicit がなくても、そのままで最適化されています。

もう 1 つの考慮事項が適用されます。これは、配置とは関係ありません。shortは、32 ビットまたは 64 ビット プロセッサの最適なデータ型ではありません。単純なロードとストア以外のことを行う場合、16 ビットから 32 ビットに変換するために追加のオーバーヘッドが必要になります。その裏話はこちら。CPU キャッシュの使用率を向上させ、効率の低い操作とのバランスを取る必要があります。これは、プロファイラーでのみ確実に行うことができます。

于 2012-11-28T17:40:41.363 に答える
2

それはあなたが思っているようには機能しません。

[StructLayout(LayoutKind.Explicit)]
public struct RenderItem
{
   [FieldOffset(0)] byte[] mCoordinates = new byte[3]; //(x,y,z)
   [FieldOffset(3)] short  mUnitType;            

}

配列は参照型です。のストレージ要件mCoordinatesは IntPtr.Size (x86 では 4 バイト、x64 では 8 バイト) です。3 バイト要素はヒープに格納されます。

FieldOffset がそのような参照と重なっている場合、何が悪いことが起こるか (または起こらないか) はわかりません。

その正確なレイアウトの構造が必要な場合は、別の値の型を作成する必要があります

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Coordinate
{
    byte x;
    byte y;
    byte z;
};


[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct RenderItem
{
   Coordinate coord;
   short  mUnitType;            

}

これは配置に関する質問には答えませんが、マシューが提供するリンクは答えます。

于 2012-11-28T17:07:21.777 に答える