どーいうこと?
昨日、dynamic 経由の値型の取り回しと言うエントリをアップしたのですが、その後気になるというかまぁ調べてみっか程度に、色々と調べてみたら、どうやらコールサイトの構築時に内部でボックス化を含む処理を生成しているらしく高頻度に動かしたときGarbageCollectionが発生していた。
本質的に、値型をボックス化した挙げ句、ボックス化した状態で結構な回数書き換え、読み込みを行うことなんて本来的には問題が多く、改善をすべき状態ではあるけど、ここは一つ偏執狂的に何とか出来ないか追い求めてみたので、おつきあい頂ければ幸い。
尚、今回はパラノイア回なのでほとんど自己満足として、出来ましたと言う事実の提示であって、積極的にお勧めなんてとても出来る代物では無いのでその点十分ご留意の程。
前説
さて、今回使う箱詰めにする構造体は以下の通り
public struct SomeStruct
{
public int Value { get; set; }
public override string ToString() => Value.ToString();
}
で、コレを、以下のように操作したとき、
static void Main()
{
dynamic d = new SomeStruct { Value = 42 };
for (int i = 0; i < 1000000; i++)
{
d.Value += i;
}
Console.WriteLine(d.Value);
}
自分の環境では、
- Generation0:17回
- Generation1:1回
- Generation2:0回
という感じでGarbageCollectionが発生していた。
で、ここのカウントを0にしていきましょうというのが今回のお題です。
dynamic無しでとりあえずどうかしてみる
dynamicの使用せず、ボックス化したままValue
プロパティをGet/Setするにはリフレクションを使えばまぁ何とかなる。
static void Main()
{
object obj = new SomeStruct {Value = 42};
var propInfo = typeof (SomeStruct).GetProperty("Value");
for (int i = 0; i < 1000000; i++)
{
var tmp = i + (int) propInfo.GetValue(obj);
propInfo.SetValue(obj, tmp, null);
}
Console.WriteLine(obj);
}
ただ、propInfo.GetValue
メソッドの戻りはobject
からint
へのUnboxingが発生しているし、propInfo.SetValue
メソッドの第2引数もint
からobject
へのBoxingが発生している。
だもんで、自分の環境ではGeneraton0が26回発生していた。
どーせ行くならパラノイア♪
ということで、基本はリフレクション経由で行くとして、解決すべきはBoxing
とUnboxing
が発生させなきゃ良いので、それは何とかしたい。
で何とかしたのが以下1
static void Main()
{
object obj = new SomeStruct {Value = 42};
var propInfo = typeof (SomeStruct).GetProperty("Value");
var setter = (Action<int>) propInfo.SetMethod.CreateDelegate(typeof (Action<int>), obj);
var getter = (Func<int>) propInfo.GetMethod.CreateDelegate(typeof (Func<int>), obj);
for (int i = 0; i < 1000000; i++)
{
var tmp = i + getter();
setter(tmp);
}
Console.WriteLine(obj);
}
見事、GarbageColletionを発生させないまま完了したけど、一般的なシナリオ2では多分、setter
,getter
の構築コストをペイできないし、そもそも、何れのデリゲートもobj
変数専用となっており、ほぼ使い道は無い。
とはいえ、多分実用的な場面でコンなこと必要なシナリオは多分ないかな~とは思う。でも良いじゃ無いパラノイアなんだから♪