2008年06月12日
川俣晶の縁側ソフトウェア技術雑記 total 6649 count

C# 3.0のラムダ式のオーバーヘッドは多いか少ないか? 湯水のごとく使えるといっても限度はあるか?

Written By: 川俣 晶連絡先

 ちょっと気になったので、簡単に検証。C# 3.0のラムダ式は、大量に使いたくなる誘惑に駆られますが、メモリ消費量的なオーバーヘッドはどの程度なのでしょうか?

 なお、以下の検証は甘い内容なので、厳密さを要する場合は当てにしないように。

検証環境 §

  • Windows Vista Ultimate
  • Visual Studio 2008 Professional
  • .NET Framework 3.5

検証1 §

 1万個のラムダ式を配列に入れてみます。

 ラムダ式を入れる前と後で一時止めて、その時点でタスクマネージャを使ってプロセスの使用メモリ量を調べます。プロセスのメモリ量表示はKB単位なので、1バイト消費であっても明確に見えるよう10000個確保するようにしています。

using System;

delegate void TGT();

class Program

{

    static void Main(string[] args)

    {

        const int size = 10000;

        TGT[] tgt = new TGT[size];

        Console.WriteLine("ready");

        Console.ReadLine();

        for (int i = 0; i < size; i++)

        {

            tgt[i] = () => { ;};

        }

        Console.WriteLine("alloced");

        Console.ReadLine();

    }

}

 結果は「表示メモリ使用量に変化無し」。

 このケースでは、何万個であろうと、ほとんどオーバーヘッドを意識せずにラムダ式を湯水のごとく使って構わないようです。

検証2 §

 同じラムダ式を代入するだけなら使用メモリ量が増えないというのなら、違うラムダ式ならどうだ、というわけでループを展開。

using System;

delegate void TGT();

class Program

{

    static void Main(string[] args)

    {

        const int size = 10000;

        TGT[] tgt = new TGT[size];

        Console.WriteLine("ready");

        Console.ReadLine();

        tgt[0] = () => { ;};

        tgt[1] = () => { ;};

        tgt[2] = () => { ;};

        tgt[3] = () => { ;};

(中略)

        tgt[9997] = () => { ;};

        tgt[9998] = () => { ;};

        tgt[9999] = () => { ;};

        Console.WriteLine("alloced");

        Console.ReadLine();

    }

}

 まず、readyが表示されるまでの待ち時間が非常に長い結果になりました。JITへの負担は大のようですが、ラムダ式だから負担が増えた訳ではないような気がします。

 使用メモリ量は8768KB→9156KBへ拡大。

 1個あたりの所用バイト数は、(9156-8768)*1024/10000=39.73というわけで約40バイト。1個あたり40バイトなら、割と気楽に使えそうです。

あてにならないまとめ §

  • ソースコード上の1つのラムダ式は何回代入してもそれで使用メモリ量は増えない (ただし変数をキャプチャするとその分だけ増えると思われるので、「増えない」と思いこんではいけない)
  • ソースコード上に別個に書かれたラムダ式をそれぞれ代入すると、1つあたり最低40バイト程度のメモリを消費する (が、もちろん中身が空のラムダ式など通常使わないので、中身相応のメモリ増があることは要覚悟)