- PR -

構造体とクラスの選択

投稿者投稿内容
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2008-09-28 03:21
引用:

(B)インスタンスのサイズが 16 バイト未満である。


わたしには16 バイト未満というのは目安だと感じられます。
値型のサイズが大きくなればなるほど、コピーのコストが大きくなるし、
知らない間に大量のメモリを消費していたりしますから。(先の投稿の「2.List(初期量指定なし)」のように)

引用:

(D)頻繁にボックス化する必要がない。


ジェネリックを使っていればボックス化が発生するような場面はそうないですよね!?


引用:

ひろしさんの書き込み (2008-09-27 16:17) より:
★classArrayの場合
(1)ヒープ上に参照先のアドレスが格納された400byteの領域(アドレス4byte×100要素)が1個確保される。
(2)ヒープ上に各クラスのフィールドの格納場所として32byteの領域が100個確保される。


先のプロファイルから、いい線いっていたようです。

引用:

(4)GCは(1)と(2)の合計101個の領域を管理する。


「管理ヒープ」上にオブジェクトが割り当てられるだけで、全部を管理しているわけではないようです。
ガベージコレクション入門: Microsoft .NET Framework の自動メモリ管理 Part I
GCハンドル(強い参照(ルート)・弱い参照(長い、短い)・ピン)はハンドルテーブルにて管理されています。

引用:

フィールドにアクセスするためにはclassArrayの方がstructArrayより参照が1回余計にかかります。


パフォーマンスにどれだけ影響あるでしょう?私には完全に無視できる程度だと思いますが。

引用:

また、GCにとってメモリ上で領域が要素の数だけ分散しているclassArrayの方が1箇所で済んでいるstructArrayより負荷が大きいように思えます。


ガベージコレクション入門: Microsoft .NET Framework の自動メモリ管理 Part IIより引用
引用:

ほとんどのヒープ (Cランタイムヒープなど) は、フリースペースを見つけた場所に、オブジェクトを割り当てていく。そのため、連続して複数のオブジェクトを作成したとき、これらのオブジェクトは数MBも離れた位置に配置される可能性がある。
しかし、管理ヒープでは、連続して確保されたオブジェクトは、メモリの連続領域に配置されることが保証されている。


まあ、連続領域に配置されても、フラグメンテーションは発生するでしょうが・・・GCがメモリコンパクションしてくれるわけですし。しかも世代別GCで。

引用:

あるいは各ジェネリックコレクション特有の実装を反映した個別のルールが必要になるのでしょうか?


構造体かクラスか/値型か参照型か を格納するコレクションで決めるなんてナンセンスだと思いますけど!?
.NETクラスライブラリのコレクションの実装は、いたってフツーな実装ですよ。

コレクションの特徴(追加挿入の得意不得意、ランダムアクセスの得意不得意・・・)を知らずに使うほうが
よっぽど問題だと思います。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2008-09-30 22:00
引用:

Tdnr_Symさんの書き込み (2008-09-28 03:21) より:
ジェネリックを使っていればボックス化が発生するような場面はそうないですよね!?


ジェネリック コレクション クラスが参照型なので、発生していると思います→2008-09-24 22:02


引用:

ひろしさんの書き込み (2008-09-27 16:17) より:
 さて、クラスと構造体の振るまいの違いが問題にならない次のような場面で、パフォーマンスを考慮しなければならない場合、構造体とクラスのどちらするべきか迷います。


 「パフォーマンス」と一口に言っても、いろいろあります。どのような、パフォーマンスでしょう?
ガーベッジコレクションは別スレッドで行われるので、メモリの要求を出したときにコレクトが行われるような状況でなければ、特に問題はないと思われます。
構造体が大きくなれば、Boxing - UnBoxing でコピーにかかる時間がそれだけ多くなりますから、参照を2回たどることとどちらがパフォーマンス的に不利かというと、構造体のような気がします。

 ところで、ご質問の背景は、「コーディング規約を策定中。できるだけ、実行速度的なパフォーマンスがよいコードとなるような規約にしたい。」でしょうか?
 もしそうなら、コーディング規約には、まずは「一貫性」を求めるべきかと思います。この一貫性は、「どのようなレベルの人でも、基本的に同じコードを書ける。より高技術の人は、技術力のない人にも読み解ける範囲で、高度な書き方を選べる。」ということです。実行速度的なパフォーマンスについては、「構造体だと云々。クラスだと云々。」という Tips として選択できるようにしておく方がよいと思います。
 もし、「この場合はクラス、この場合は構造体を用いること」と規定すると、どちらにしようか迷うケースが出てきます。この、「迷う」状態は、コードの一貫性を保つことを難しくし、また、読みにくくさせます。そして、クラスや構造体のメンバーを変更したとき、規約によって、クラスと構造体を入れ替えなければならなくなるかもしれません。このとき、修正に伴うコストは、とても大きなものとなるでしょう。
 ここで、「パフォーマンス」を「実行速度的な」と修飾していることに注意してください。「コード ライティングのパフォーマンス」を見ると、統一してある方が良くなるでしょう
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2008-10-01 05:33
おはようございます。

引用:

Jittaさんの書き込み (2008-09-30 22:00) より:
引用:

Tdnr_Symさんの書き込み (2008-09-28 03:21) より:
ジェネリックを使っていればボックス化が発生するような場面はそうないですよね!?


ジェネリック コレクション クラスが参照型なので、発生していると思います→2008-09-24 22:02



ちょっと意味がわからないのですが、
2008-09-24 22:02のJittaさんの投稿についてですが、…

引用:

Jittaさんの書き込み (2008-09-24 22:02) より:
これの謎は、ボックス化とボックス化解除 (C# プログラミング ガイド)<microsoft.com>あたりで。


ここで示されたコードって、ボックス化とは無関係ですよね!?
ひろしさんがすでに回答されているとおり、
「構造体は値型なので、値そのものがコピーされます。」
が、謎の答えでしょう。


一応、確認しておきますが、「ボックス化」とは…
ボックス化とボックス化解除 (C# プログラミング ガイド)より引用
引用:
ボックス化とは値型から object 型、またはその値型によって実装されている任意のインターフェイス型へ変換するプロセスのことです。CLR により値型がボックス化されるとき、値は System.Object 内部にラップされ、マネージ ヒープに格納されます


ということです。
通常、値型はそれ単体ではマネージ ヒープに割り当てられることはありません。
(配列やクラスのフィールドという形ではマネージ ヒープ上に乗っかります。)
しかし、ボックス化が発生した場合は、上記の引用の通りマネージ ヒープに格納されます。

つまりマネージ ヒープに値型が単体で割り当てられたかどうかを(プロファイラで)調べることによって、
ボックス化が発生したかどうかを確認することが可能です。


JittaさんのコードでCLR Profilerで検証してみると、
構造体Aのヒープ上の割り当ては見当たりません。
ボックス化は発生していないと見て取れます。

このコードを非ジェネリックのArrayListに変更すると…
120bytes - 10 instances, 12byte
の割り当てを確認できます。
この場合はボックス化が発生しています。

コード:
public class AList : List<A>
  ↓
public class AList : ArrayList


Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2008-10-01 07:48
オオボケしてました。すみません。
ひろし
ぬし
会議室デビュー日: 2002/09/16
投稿数: 390
お住まい・勤務地: 兵庫県
投稿日時: 2008-10-02 17:39
Tdnr_SymさんJittaさん

良いアドバイスありがとうございます。Tdnr_SymさんやJittaさんの議論を拝見する中で視点が広げることができました。プロファイリングの大切さも改めて認識しました。

スキルアップ/キャリアアップ(JOB@IT)