C#(.NET Framework)の文字列連結について

一般に文字列を+=で連結するのは遅いと言われています。事実そのとおりで、多量の連結を+=で行うと死ぬほど時間がかかります。例えば、以下のようなコードで示されることが多いでしょう。

// ベンチマーク用関数(10万回実行)
Func<Action, TimeSpan> bench = action =>
{
    var sw = Stopwatch.StartNew();
    for (int i = 0; i < 100000; i++)
    {
        action();
    }
    return sw.Elapsed;
};

// StringBuilderの計測(最後のToStringを入れてませんが、あまり変わらないのでスルー)
var sb = new StringBuilder();
var sbTime = bench(() => sb.Append("hoge"));
// stringの+=での計測
var s = "";
var stringTime = bench(() => s += "hoge");

Console.WriteLine(sbTime); // 0.004sec
Console.WriteLine(stringTime); // 14.97sec

0.004secと15secでは話になりません(正確には、StringBuilderでは最後に文字列に変換するToStringを入れるべきですが、それでも1secは超えなくて差は歴然なので省略します)。ならば、文字列を連結する場合は、どのような時でもパフォーマンスのためにStringBuilderを使うべきでしょうか? 答えは違います。

// ILではひとつにまとまる
// IL_0001:  ldstr      "abcde"
var s = "a" + "b" + "c" + "d" + "e";

定数の連結はコンパイル時にひとまとめにされるので、StringBuilderを使うのは愚かな選択となります。この辺はILDASMで見ればわかるし、Reflectorでもひとまとめになって展開されているのが確認できます。では定数ではなく動的に値を返すものは?

static string Get()
{
    return DateTime.Now.ToString();
}

static void Main(string[] args)
{
    // IL_0031:  call       string [mscorlib]System.String::Concat(string[])
    var s = Get() + Get() + Get() + Get() + Get();
}

ILを見ると、s += Get(); s += Get(); みたいな展開のされかたにはならず、String.Concat(string[])が呼ばれることになります。よって、速度を心配してStringBuilderを使う必要は全くありません。測定してみましょう。(ちなみにs+=Get()だとString.Concat(string,string)が大量に呼ばれることになるのが遅い理由)

// benchとGetは上で使ったのと同じものを流用
var sbTime = bench(() =>
{
    var sb = new StringBuilder();
    sb.Append(Get())
      .Append(Get())
      .Append(Get())
      .Append(Get())
      .Append(Get());
    var s = sb.ToString();
});

var stringTime = bench(() =>
{
    var s = Get() + Get() + Get() + Get() + Get();
});

Console.WriteLine(sbTime); // 0.65sec
Console.WriteLine(stringTime); // 0.64sec

速度はほとんど変わりません。StringBuilderとString.Concatでは処理の中身は結構違いますが、速度変わらないのならどっちでもいいよね。なら、記述しやすいほうを選ぶのが良いでしょう。妄信的にパフォーマンスのためにStringBuilder!とか思っている人は、少し考え直してみてください。そんなの当たり前だろ常識的に考えて、と思っていた時期が私にもありました……。世の中は存外StringBuilder神話に溢れているかもですよ? いやほんと。あとC#なら逐語的リテラル文字列もお忘れなく。

Profile

Yoshifumi Kawai

Cysharp, Inc
CEO/CTO

Microsoft MVP for Developer Technologies(C#)
April 2011
|
July 2023

Twitter:@neuecc GitHub:neuecc

Archive