[.NET] StringBuilderの改善

MSDN blogに.NETの改善についての記事があった。

http://blogs.msdn.com/b/calvin_hsia/archive/2012/09/28/10354300.aspx

The number of Garbage Collections indicate how much memory is used

VS2012が持っている.NET Framework 4.5では、System.Text.StringBuilderが改善されているらしい。VSの起動を含む様々なシナリオでは空文字列のインスタンスが何千と作られていたそうだ。
たとえば、XMLファイルを読み込んで属性をStringBuilderで表現している。属性がないと空のStringBuilderのオブジェクトができる。そのオブジェクトのToStringメソッドはSystem.Stringオブジェクトでから文字列を表現したものを返す。System.Stringの空文字列のメモリ上のサイズは32bitアプリケーションの場合は14byteになる。(CLRオブジェクトClassID4byte + 4byte(オーバーヘッド) + サイズ 4byte (System.String 由来) + 実際のデータ)

ほとんどの属性が空の場合にもオブジェクトが生成されるので、何千というオブジェクトのGCをCLRのガーベジコレクターが行うことになる。

Microsoftはこれを抑制するために空のStringBuilder.ToString()が生成するオブジェクトをString.Emptyに変更する改造を.NET Frameworkで行った。String.Emptyを返すようにすればメモリを消費しないのでパフォーマンス改善が期待できる。また、String.Emptyと ”” を比較すると、一般的には “”はassemblyをまたがって共用できない点でString.Emptyより劣る。

その結果、以下のコードで簡易的に測定したところ、GCの回数がゼロになった。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Reflection;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var thisAsm = System.IO.Path.
                GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location);
            StringBuilder sb = new StringBuilder();
            var emptystr = sb.ToString();
            // define an action to measure the performance of some action
            Action<string, Action> actMeasure = (description, actTarget) =>
            {
                PerformanceCounter perfCounterGC =
                    new PerformanceCounter(
                        ".NET CLR Memory", "# Gen 0 Collections",
                        thisAsm);
                var nGCStart = (int)perfCounterGC.NextValue();
                var dtStart = DateTime.Now;
                for (int i = 0; i < 1000; i++)
                {
                    for (int j = 0; j < 100000; j++)                     {                         actTarget.Invoke();                     }                 }                 var nGCs = (int)perfCounterGC.NextValue() - nGCStart;                 var elapsed = (DateTime.Now - dtStart).TotalSeconds;                 Console.WriteLine("{0,20} Elapsed = {1:n2}  # GCs = {2} ",                      description, elapsed, nGCs);             };             actMeasure.Invoke("String Builder", () =>
            {
                var str = sb.ToString();
            });
            actMeasure.Invoke("DoubleQuote", () =>
            {
                var str = "";
            });

            /*
             * Dev10
                String Builder Elapsed = 4.77  # GCs = 1017
                DoubleQuote Elapsed = 1.67  # GCs = 0
               Dev12
                String Builder Elapsed = 1.55  # GCs = 0
                DoubleQuote Elapsed = 0.79  # GCs = 0 * */
        }
    }
}

手元のVS2010で実行してみるとたしかにStringBuilderではGCが1000回以上発生している。VS2012に変更するとGCがゼロになるということはStringBuilderのToString()を呼ぶソースコードはそのままでも.NET を新しくすると高速になる、ということを意味する。ただし、VS2012をインストールしないと(.NETを新しくしないと)この効果を享受できない。VS2010を使う場合でも、一度VS2012をインストールしておけば新しいランタイムを使うので効果がある。

ということは、古い.NETをside by side インストールせず新しい.NETに対応しましょう、ということになるのではないか。

広告

コメントを残す

コメントを投稿するには、以下のいずれかでログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。