菊池 Blog

移転しました 続・菊池 和彦の足跡

AILight Banner
AILight Blog

プロフィール

菊池 Blog

目次

Blog 利用状況

記事分類

過去の記事

タグ

取り出しちゃったら負け

ルールはそれだけだ。

といっても何か解らんかもしれんだろう。

.NET Framework には参照型と値型がある。参照型には絶対に一つ一つにGCHandleがありガベージコレクションの対象となる、値型はアロケーション単位ごとにGCHandleが割り当てられる。

要するに値型の配列はGCHandleが一つだけなのだ、仮にインスタンスを100万個扱うために new T[1000000] と確保すればGCHandleは1つだけだ。

参照型の場合にはarray=new T[x]; for( int i=0;i<array.length;i++ ) array[i]=new T(); と記述することでx個のインスタンスを扱う事ができるが、その場合にはGCHandleはx+1個(インスタンスの数+配列)必要だ。

GCHandleの数はすなわちメモリ管理上のオーバーヘッドと言っても過言ではない、ガベージコレクタがそれらを一個一個チェックして行く負荷もあるし、GCHandleの個数分メモリも消費される。

このため、.NET Frameworkでは大型の多体問題のシミュレーションを行うプログラムを書く事ができる。

ValueType を記述するのには struct で型を宣言するだけでいい、メソッドももてるしインターフェースももてる、イベントも使える、すばらしい。

struct Person : IPerson
{
    IPerson father;     // 父親
    IPerson mother;   // 母親
    IPerson partner;   // 結婚相手
    その他諸条件のフィールド
}

上記のように宣言された構造体の配列は、子供から両親、そして結婚相手へのリンクを持ち家計図の構造を持つ

Person[] array = new Person[100000];

こうすれば100万人分の家計図を保持するデータ構造を簡単に作る事ができる。(離婚再婚はおいとくとして)

インデックスA、Bで示される人が結婚した場合には以下のコードを実行すればいい。

array[A].partner = array[B];
array[B].partner = array[A];

IParsonにIParson型のPartner プロパティがあれば以下でもいい

IParson a = array[A];
IParson b = array[B];
a.Partner = b;
b.Partner = a;

でも、以下はダメだ

Parson a = array[A];
Parson b = array[B];
a.partner = b;
b.partner = a;

なぜか?

Personは値型だから代入はコピーを意味する、要するに一番下のコードでは作業用変数 a と bに管理しようとしているインスタンスがコピーされ、コピーロボット同士が結婚した事になる。結婚したのはコピーロボット同士だから本人達は結婚していない。

さて、結婚済みのA,B に子供ができた、配列のインデックスCが空いてるとしてそこに子供を設定しよう

array[C].father = array[A];
array[C].mother = array[B];

とすればいいし、同様に IParson に Father / Mother を示すプロパティがあれば以下でもいい。

array[C].Father = array[A];
array[C].Mother = array[B];

または

IParson a = array[A];
IParson b = array[B];
IParson c = array[C];
c.Father = a;
c.Mother = b;

コピーの意味するところとしては、単純にコピーだから、以下でもいい。

IParson a = array[A];
IParson b = array[B];
Parson c;
c.father = a;
c.mother = b;
array[C] = c;

でも、以下はダメだ。

Parson a = array[A];
Parson b = array[B];
Parson c;
c.father = a;
c.mother = b;
array[C] = c;

なぜならA,B に対してコピーを作って、コピーに子供ができた事になってしまう。

現状のクローン技術では高等生物においてクローンが繁殖に成功するのは稀だからだ(違う

要するに、こういうWrapperがあるといい

public class ValueTypeStorage< T, TImplInterface > where T : struct, TImplInterface
{
    T[] realArray;
    TImplInterface this [ int index ]
    {
        get { return realArray[index]; }
    }
}

これがあれば、OKのパターンの以下以外はコンパイルが通らない。

array[C].Father = array[A];
array[C].Mother = array[B];

IParson a = array[A];
IParson b = array[B];
IParson c = array[C];
c.Father = a;
c.Mother = b;

しかし参照型的に使えるようにしようとして Nullable を導入しようとするとまた嫌なことになる。

ValueTypeStorage< Nullable<Parson>, IParson > とやると、Nullable<Parson> は IParson を実装してないからジェネリックの制約に引っかかるのだ。

なのでこうなる

public NullableValueTypeStorage< T : TImplInterface >  where T: struct, TImplInterface
{
    Nullable<T>[] realArray;
    TImplInterface this [ int index ]
    {
        get { 
            if( realArray[index].HasValue ) return realArray[index].Value;
            return null;
        }
    }
}

getの中で realArray[index]が2回出てくるからって、ここに変数を導入しちゃダメ

Nullable<T> x = realArray[index];
if( x.HasValue ) return x.Value;

は x にコピーされたValueが帰るから。

 

要するに取り出しちゃったら負け、これがルール。

ルールは説明しましたって事で、皆さんも数十万個のオブジェクトが絡み合う多体問題のシミュレーションにCPUぶん回してみてはいかがでしょうか。

投稿日時 : 2007年12月18日 14:31


コメントを追加

#  re: 取り出しちゃったら負け 2007年12月19日 15:57 菊池

インターフェースにする変換するときにボックス化がかかってコピーされちゃう悪寒…
タイトル
名前
URL
コメント