1

構造体シムを使用してジャグ配列を置き換えるオーバーヘッドはありますか?

具体例を挙げると

vertices = new KeyValuePair<uint, EdgeData>[][];

private struct Vertex
{
    public KeyValuePair<uint, EdgeData>[] Arcs { get; set; }
}

vertices = new KeyValuePair<uint, Vertex>[];

EdgeData は、何らかの違いがある場合はクラスです
。構造体の例では明らかに意図が明確ですが、大規模なグラフを保持できる必要があるため、メモリのオーバーヘッドが大きくなります。

4

3 に答える 3

4

Astructは、スタックに割り当てられる場合と割り当てられない場合があります。参照型をスタックに割り当てることはできません。それらは常にヒープに割り当てられます。

標準 (ISO 23270)、§ 8.8 から:

8.8 構造体 クラスと構造体の類似点のリストは長く、構造体はインターフェースを実装でき、クラスと同じ種類のメンバーを持つことができます。ただし、構造体はいくつかの重要な点でクラスと異なります。構造体は参照型ではなく値型であり、構造体では継承がサポートされていません。構造体の値は「スタック上」または「インライン」に格納されます。注意深いプログラマーは、構造体を適切に使用することでパフォーマンスを向上させることができます。

たとえば、 Point のクラスではなく構造体を使用すると、実行時に実行されるメモリ割り当ての数に大きな違いが生じる可能性があります。以下のプログラムは、100 ポイントの配列を作成して初期化します。

クラスとして実装するとPoint、101 個の個別のオブジェクトがインスタンス化されます。1 つは配列用で、1 つは 100 個の要素用です。

class Point
{
  public int x, y;
  public Point(int x, int y)
  {
    this.x = x;
    this.y = y;
  }

}
class Test
{
  static void Main()
  {
    Point[] points = new Point[100];
    for (int i = 0; i < 100; i++)
    {
      points[i] = new Point(i, i*i);
    }
}

IfPointは、代わりに構造体として実装されます。

struct Point
{
  public int x, y;
  public Point(int x, int y)
  {
    this.x = x;
    this.y = y;
  }
}

インスタンス化されるオブジェクトは 1 つだけです。つまり、配列用のオブジェクトです。Point インスタンスは、配列内でインラインで割り当てられます。この最適化は悪用される可能性があります。クラスの代わりに構造体を使用すると、アプリケーションの実行が遅くなったり、メモリの消費量が増えたりする可能性があります。構造体インスタンスを値で渡すと、その構造体のコピーが作成されるためです。

したがって、答えは「たぶん」です。

あなたの例では、配列(参照型)をstruct(値型)内にラップしても意味がありません。その配列はまだヒープに割り当てられています。

ただし、クラスEdgeDataを構造体に変更すると、配列内でインラインに割り当てることができます (ただし、割り当てられない場合があります)。EdgeDataたとえば、クラスのサイズが 16 バイトで、100 個のエントリを作成して入力すると、実際には 1 つの配列インスタンスが割り当てられます (100 個のオブジェクト参照とクラスEdgeData[]の 100 個の個々のインスタンスを保持するサイズのバッキング ストアを使用)。EdgeData.

が構造体の場合EdgeData、100 個のEdgeDataインスタンスを保持するサイズのバッキング ストアを含む 1 つの配列を割り当てます (この場合、架空のEdgeData構造体のサイズは 16 バイトであるため、1600 バイトです)。

EdgeData配列のクラス バージョンを反復すると、特に配列が非常に大きい場合、ページングが発生する可能性があります。これは、ヒープ全体をジャンプして個々のインスタンスにヒットするため、おそらく参照の局所性が失われるためです。

インスタンスがインラインであるため、配列のバージョンを反復処理すると、struct参照の局所性が保持されます。EdgeData

于 2013-04-24T19:00:27.953 に答える
2

2D 配列を構造体の 1D 配列に置き換えても、問題は発生しません。データをどう見るかが重要です。それぞれがアークの配列を含む構造体の配列としてモデル化する方が理にかなっている場合は、それがコードで表現する方法です。

保管方法に多少の違いがあります。特に、1D 配列アプローチは、2D 配列アプローチよりも多くのメモリ (合計) を占有します。uint基本的に、各行に余分なものがあります。

続いて打った。OPが使用している[,]ジャグ配列( )ではなく、構造体アプローチと2D配列(つまり )の違いについて説明しています。[][]

実際には、使用されるメモリの合計はそれ以上になります。2D 配列アプローチで(row * col) KeyValuePairは、配列内に構造があります。配列には、64 ビット ランタイムで約 50 バイトの割り当てオーバーヘッドがあります (思い出すと、32 ビット ランタイムで約 40 バイト)。1D 配列アプローチでも(row * col) KeyValuePair構造体は残りますが、それぞれに同じ 50 バイトの割り当てオーバーヘッドを持つ配列が含まれます。さらに、構造体verticesを含む配列があります。(row) KeyvaluePair

ただし、2D 配列 (配列のみ) には(rows * cols * (4 + sizeof(IntPtr)))バイトが必要です。1D配列はバイトverticesのみを必要とします。(rows * (4 + sizeof(IntPtr)))1 つの配列に対して 2 ギガバイトに制限されている場合 (非常に大きなオブジェクトを有効にしない限り、.NET 4.0 以前、または .NET 4.5 の場合)、1D 配列を使用すると、合計でより多くのアイテムを使用できる可能性があります。 2D 配列よりも構造体の もちろん、その数のKeyValuePair<uint, EdgeData>インスタンスを保持するのに十分なメモリがあると仮定します。

したがって、全体的なメモリ使用量は増加しますが、最大の単一割り当てははるかに小さくなります。

于 2013-04-24T19:05:28.183 に答える
2

特定の例では、各行に追加の uint がありますが、構造体の配列はかなり効率的である傾向があります。また、構造タイプのプロパティを公開することは避け、構造がダクト テープ (ポイントの座標など) で結合された独立した値のコレクションを表す場合は、単純にそれらの項目をフィールドとして公開します。JIT がプロパティ アクセスをフィールド アクセスに変換する状況は数多くありますが、そうでない場合も多くあります。

次の効率を比較する場合:

struct FloatPoint2D {public float X,Y;}
FloatPoint3D[] MyArray;

float[] MyXCoords, MyYCoords;

アイテムの X と Y の両方にランダムな順序でアクセスすると、別々の配列のペア (通常は 2 つではなく 1 つのキャッシュ ミス) よりも上で定義した構造体の方が高速になりますが、順番に多くのアイテムの X 座標または Y 座標のみにアクセスします。別々の配列を使用している場合は高速になります (各キャッシュ ラインで 2 倍の有用な座標がフェッチされます)。

特定の例では、タイプがカプセル化する必要があるデータが正確に不明です。構造体と非構造体の例は異なるデータを保持しているため、「より効率的」とは言えません。

于 2013-04-24T20:40:18.480 に答える