kazuk は null に触れてしまった

C# / .NET 系技術ネタ縛りでお送りしております

タグアーカイブ: PerfMon

.NET CLR2 と CLR4 の StringBuilder のパフォーマンス


以下コードにより計測

    class Program
    {
        static void Main(string[] args)
        {
            Random rnd = new Random();
            Console.WriteLine("Hit enter to start");
            Console.ReadLine();
            var stopwatch = Stopwatch.StartNew();
            for (int i = 0; i < 10000; i++)
            {
                // 64KB のテキストをランダム文字で生成
                StringBuilder sb = new StringBuilder();
                while (sb.Length < 65535)
                {
                    sb.Append('a' + rnd.Next(26));
                }
            }
            stopwatch.Stop();
            Console.WriteLine(stopwatch.ElapsedTicks);
            Console.ReadLine();
        }
    }
まずCLR2 での結果

Hit enter to start

891252599

約90秒程度で完了、その時の特にメモリ系の挙動を PerfMon でとったスクリーンショットの注目点を以下。

CLR2 – #Gen1 Collections

20101207111830

CLR2 – #Gen2 Collections

20101207111945

これが全く同一値という世代別GCにとっては悪夢のシチュエーション。

CLR2 – Large Object Heap Size

20101207113019

LOH の上下からLOHのガベージコレクションが走っている事が解る

CLR2 – % Time in GC

20101207113243

LOH の下がるタイミングで %Time in GC にピンがたっている。

続いてCLR4での結果

Hit enter to start

662106546

数値としては 134%、

パフォーマンスカウンタの差異はこんな感じ

CLR4 – #Gen 1 Collections

20101207114226

CLR4 – #Gen 2 Collections

20101207114336

なんと Gen 2コレクションの発生0。

CLR4 – Large Object Heap Size

20101207114445

LOH もべったり。

CLR4 – % Time in GC

20101207114535

#Gen 1 Collectionsの簡易GCで済むので、コストも低い。

結論、個々のシステムの特性によって感応度に違いはあるかも知れないけど、比較的大きな文字列を編集処理してる場合には CLR2 に対してCLR4の StringBuilder は約3割高速で、主因はStringBuilder内部でのバッファ方式の変更による物。

ReSharper による CLR2 のStringBuilder定義表示

20101207115108

m_StringValue に実体が入ってるので、20KBを超えるとLOH行き、しかもvolatileというCPUに優しくない存在。

同じく ReSharper による CLR4 のStringBuilder 定義表示

20101207115832

m_ChunkPrevious によってチャンク化されていて m_ChunkChars に実体が分割して持たれるという構造、分割されたそれぞれは 8000文字まで、要するに16KBなのでLOHには絶対に行かない。

結論

素晴らしい!アプリケーションをCLR2からCLR4にするだけで数十%のパフォーマンス向上も夢じゃないよね。

夏ぐらいから気付いてたんだけど、正確に分析したくなったので測定してみましたなブログ。