2012-09-11 15 views
32

オープンソースSignalRプロジェクトのソースコードを参照しています。this diff codeと表示されています。このホットコードではStringBuilderまたはforeachを使用しないでくださいパス」"このホットコードパスでStringBuilderまたはforeachを使用しないでください"

-   public static string MakeCursor(IEnumerable<Cursor> cursors) 
+   public static string MakeCursor(IList<Cursor> cursors) 
      { 
-    var sb = new StringBuilder(); 
-    bool first = true; 
-    foreach (var c in cursors) 
+    var result = ""; 
+    for (int i = 0; i < cursors.Count; i++) 
       { 
-     if (!first) 
+     if (i > 0) 
        { 
-      sb.Append('|'); 
+      result += '|'; 
        } 
-     sb.Append(Escape(c.Key)); 
-     sb.Append(','); 
-     sb.Append(c.Id); 
-     first = false; 
+     result += Escape(cursors[i].Key); 
+     result += ','; 
+     result += cursors[i].Id; 
       } 
-    return sb.ToString(); 
+    return result; 
      } 

私はforeachのは時々あまり効率的ではなかった理由を理解し、なぜそれがために置き換えられます。

しかし、私はStringBuilderが文字列を連結する最も効率的な方法であることを知り、経験しました。だから、私はなぜそれを標準的な連結に置き換えることにしたのだろうと思っています。

ここで、そして一般的にStringBuilderの使用については何が間違っていますか?

+8

何?それは実装に依存します。そして、はい、反復された文字列の連結を使用することは、ここでひどい考え方に見えます - 特にカーソルがたくさんある場合。 –

+1

Jonと強く同意します。 StringBuilderのバージョンが連結バージョンより悪いと私は驚いています。ループ反復回数に応じて、より大きな起動容量でStringBuilderを初期化すると、隠された暗黙オブジェクトの初期化を防ぐのに役立ちます。両方のバージョンを測定/プロファイルしてまだ比較していますか? –

+4

あなたは[@dfowler](http://stackoverflow.com/users/45091/dfowler)に問い合わせてください:) –

答えて

30

私はコードの変更を行いました。はい、割り当ての数に大きな違いがありました(GetEnumerator())がvsではないことを示しています。このコードが毎秒何百万回もあると想像してください。割り当てられた列挙子の数はばかげているので避けることができます。

編集: 現在任意の割り当て(直接ライターへの書き込み)を避けるために、コントロールを反転:あなたは `foreach`がfor``未満効率的であることを思わせる https://github.com/SignalR/SignalR/blob/2.0.2/src/Microsoft.AspNet.SignalR.Core/Messaging/Cursor.cs#L36

+0

foreachをやっても、まだStringBuilderを使用していますか? –

+2

フォローアップhttps://gist.github.com/3703926 – davidfowl

+0

これらの変更のメリットを証明するためにプロトタイプを作成していただきありがとうございます。私が正しく理解していれば、StringBuilderは短いループを長いループで処理するのではなく、長い文字列を処理する方が適しています。 – Larry

1

あなたは何回連結していますか?数が多い場合は、StringBuilderを使用します。ほんの少数の場合は、StringBuilderを作成するオーバーヘッドがすべての利点を上回ります。

+0

注: は、新しいバージョンをチェックアウトこの場合、7つの文字列の連結で終わるには2つのカーソルに移動する必要があります。これはかなり厄介です。 –

+0

必須ではありません - サイズが不明なため、内部バッファの複数の再割り当てが行われる可能性があります。それは高価かもしれません。 – Oded

+0

正しい方法は、カーソルの量を見て、ほんのわずかのために1つのパス(String)を行い、多くは(StringBuilder)のために他のパスを行うことでしょうか? –

3

これを変更した人が実際に違いを測定してくれることを願っています。

  • 毎回新しいストリングビルダをインスタンス化する際にオーバーヘッドがあります。 これはメモリ/ガーベジコレクションにも圧力をかけます。
  • コンパイラが簡単なの連結
  • それはコンパイラが、彼らは境界内にある「知っている」としてforeach文で行われていない れる境界のチェックが必要な場合がありますので、FORが実際に遅くなるかもしれないための「stringbuilderlike」コードを生成することができます。
+2

プロジェクトを知り、David FowlerとJabbRの進捗状況に従っていますが、これはまさに変化を促したものです。彼はCLRProfilerなどで測定し、作成されたすべてのゴミに対処するために多くの時間を費やしていました。うまくいけば、彼は具体的なスレッドで体重を計るだろう。 –

1

は私が

  StringBuilder sb = new StringBuilder(); 
      bool first = true; 
      foreach (Cursor c in cursors) 
      { 
       if (first) 
       { 
        first = false; // only assign it once 
       } 
       else 
       { 
        sb.Append('|'); 
       } 
       sb.Append(Escape(c.Key) + ',' + c.Id); 
      } 
      return sb.ToString(); 

に私のお金を置くしかし、私は私のお金とdfowlerからアップデートを置きます。彼の答えの中のリンクを見てください。

2

これは、機能に提供されたCursorの数によって異なります。

4つの文字列をconcatinatingするとき、2つのapporaches間のほとんどの比較は、文字列連結よりもStringBuilderを好むようです。明白な理由がない場合は、私の問題/アプリケーションの2つのアプローチの性能比較など、あまり良い結果が得られなかった場合は、StringBuilderをお勧めします。私は、(多くの)再割り当てを避けるために、StringBuilderにバッファをあらかじめ割り当てることを検討します。

件名の説明については、String concatenation vs String Builder. PerformanceおよびConcatenating with StringBuilders vs. Stringsを参照してください。

+1

情報をありがとう。この関連するSOの質問はまた非常にinterrestです:http://stackoverflow.com/questions/550702/at-what-point-does-using-a-stringbuilder-become-insignificant-or-an-overhead – Larry

関連する問題