文字列の繰り返し

パフォーマンス関連の小ネタ。IronPythonのソースを読んでいてこんなのを発見。

public static string Multiply(string s, int count) {
  int sz = s.Length;
  if (sz == 1) return new string(s[0], count);

  StringBuilder ret = new StringBuilder(sz*count);
  ret.Insert(0, s, count);
  // the above code is MUCH faster than the simple loop
  //for (int i=0; i < count; i++) ret.Append(s);
  return ret.ToString();
}

Insertメソッドに繰り返し回数を取るオーバーロードがあったんですね。気づいてませんでした。(^^; で、どれくらいパフォーマンスが違うかというと、

using System;
using System.Text;

class Program {
  const int LOOP = 100000;

  // こんなコードはあり得ない(^^;
  public static string MultiplyString(string s, int count) {
    string ret = s;
    for (int i = 0; i < count - 1; ++i)
      ret += s;
    return ret;
  }

  // 単純追加
  public static string MultiplyStringBuilder1(string s, int count) {
    StringBuilder sb = new StringBuilder(s.Length * count);
    for (int i = 0; i < count; ++i)
      sb.Append(s);
    return sb.ToString();
  }    

  // Insertメソッド
  public static string MultiplyStringBuilder2(string s, int count) {
    StringBuilder sb = new StringBuilder(s.Length * count);
    sb.Insert(0, s, count);
    return sb.ToString();
  }    

  static void Main(string[] args) {
    string s1 = "IronPython";
    string s2 = "";

    DateTime t;

    t = DateTime.Now;
    for (int i = 0; i < LOOP; ++i) {
      s2 = MultiplyString(s1, 100);
    }
    Console.WriteLine(DateTime.Now.Subtract(t));

    t = DateTime.Now;
    for (int i = 0; i < LOOP; ++i) {
      s2 = MultiplyStringBuilder1(s1, 100);
    }
    Console.WriteLine(DateTime.Now.Subtract(t));

    t = DateTime.Now;
    for (int i = 0; i < LOOP; ++i) {
      s2 = MultiplyStringBuilder2(s1, 100);
    }
    Console.WriteLine(DateTime.Now.Subtract(t));
  }
}

/* 結果
00:00:09.6718750
00:00:00.7968750
00:00:00.3125000
 */

このように2倍以上違います。(Stringは論外)

ところで、StringBuilderのAppendに

Append(string value, int repeatCount);

というオーバーロードが無いのは何故なんだろうか。charの繰り返しはあるのになぁ。