2009-03-03 24 views
13

非常に小さなオブジェクトが必要なときに、2つの浮動小数点プロパティが含まれていて、すぐに「破棄」されない何百万ものオブジェクトがある場合は、構造体を使用する方がよいまたはクラス?構造体と長命オブジェクトのクラス

ライブラリとしてのxnaのように、構造体としてpoint3sなどがありますが、それらの値を長時間保持する必要がある場合は、パフォーマンス上の脅威ですか?

答えて

33

構造体に関するほとんどの質問とは異なり、これは実際に構造体をうまく使用しているようです。それに含まれるデータが値型で、これらのデータを大量に使用する場合は、構造体がうまくいくでしょう。

いくつかのヒント:

::構造体が16バイトより大きくすべきではないか、パフォーマンス上の利点を失います。

::構造体を不変にします。これにより、使用法がより明確になります。

例:

public struct Point3D { 

    public float X { get; private set; } 
    public float Y { get; private set; } 
    public float Z { get; private set; } 

    public Point3D(float x, float y, float z) { 
     X = x; 
     Y = y; 
     Z = z; 
    } 

    public Point3D Invert() { 
     return new Point3D(-X, -Y, -Z); 
    } 

} 
+0

素晴らしい投稿は、今日私の最後のupvoteを取った。 –

+4

これはおそらくreadonlyキーワードをよく使用しています。つまり、プロパティとしてX、Y、Zを宣言する代わりに、読み取り専用フィールドを持つことができます。 あなたが誤ってあなたの不変性を破ることがないという利点があります。 – Ant

+0

'myRect.Y + = 4;'よりも 'myRect = new Rectangle(myRect.X、myRect.Y + 4、myRect.Width、myRect.Height);構造体のMutating * methods *は問題がありますが、多くの場合、公開されたフィールドは標準であるべきです。 – supercat

2

大きな懸念は、メモリがスタックまたはヒープに割り当てられているかどうかです。構造体はデフォルトでスタックに格納され、スタックは一般に空間の面でより制限されています。そのような構造体をたくさん作成することは問題になります。

実際には、私は本当にそれが大したことだとは思わない。それらの多くがある場合、おそらくクラスインスタンスの一部(ヒープ上)のどこかにあるでしょう。

+1

多くのローカル変数を持つことはめったにないため、スタックスペースは構造体の問題ではありません。構造体の配列を作成すると、スタックではなくヒープに割り当てられます。 – Guffa

+0

私はそれが私の主張だったと思います。もしあなたがそれをたくさん持っているのなら、それは問題ですが、あなたはそう多くないでしょう。 –

+0

もし多くの変数を持っているのであれば、それは構造体であれクラスであれ、問題はスタック上にも参照が割り当てられるので... :) – Guffa

1

値の型(構造体)は、ヒープに頻繁に割り当てられない型に適しています。つまり、主に別の参照または値の型に含まれています。

あなたが与えたVector3の例は完璧な例です。ほとんどの場合、ヒープ内にあるvector3を含むことはほとんどありません。また、ローカル変数として使用されるタイプの場合、スタックに割り当てられます。

+0

ありがとう、構造体は、ローカル変数として使用される場合にのみ、ヒープ上にあります。 –

+0

いいえ、それは逆です。 structのような値型は、ローカル変数の場合にのみスタックに割り当てられます。クラスのメンバーであれば、ヒープ上のオブジェクトのメモリ領域の一部として割り当てられます。 – Guffa

2

構造体がこのアプリケーションに適しているようです。

「これらの値を保持する必要がある」というのは、おそらくクラスインスタンスの配列フィールドのような、ヒープ上の記憶域を意味することに注意してください。

注意すべきことは、大きなオブジェクトヒープに割り当てが行われることです。そのヒープは、おそらく問題ではない非常に長寿命のオブジェクトの場合、どのようにして、このヒープがどのようにデフラグするのかははっきりしていません。

これらのデータ型の何百万ものクラスを使用すると、このタイプの操作で発生する可能性のある逆参照の剪断量が高くなる可能性があります。

5

答えは、オブジェクト/値が最終的に保存される場所に依存します。それらがArrayListのような型指定されていないコレクションに格納される場合、それらはボクシングを終了します。ボクシングは構造体のオブジェクトラッパーを作成し、フットプリントはクラスオブジェクトと同じです。一方、T []やListなどの型指定された配列を使用する場合、構造体を使用すると、その要素ではなくコレクション全体のフットプリントを持つ各要素の実際のデータのみが格納されます。

構造体はT []配列で使用する方が効率的です。

2

通常、非エイリアシングの大きな配列(つまり、共有されていない)同じタイプのデータは、迂回の数を減らすので、パフォーマンスのために構造体に格納するのが最も適しています。 (when-are-structs-the-answerも参照してください)。クラスと構造体の正確なパフォーマンスの違いは、使用方法によって異なります。 (例えば、操作では、構造体の一部にしかアクセスしませんか?一時的なコピーをたくさん行いますか?構造体が小さい場合は、おそらく常に使用する方が良いでしょうが、大きければ、それを不変にすると、値を変更するためにすべてをコピーする必要があります)。

疑問がある場合は、測定してください。

このような測定では明らかにならない可能性のある長期的な影響に関心があるので、そのような配列はラージオブジェクトヒープに格納されている可能性があり、破棄して再配置する代わりに、割り当てられた。 (CRL Inside Out: Large Object Heap Uncoveredを参照してください)

呼び出しに大きなサイズの構造体を渡すときは、コピーを避けるためにref引数を渡すことができます。