StringBuilder の使用が重要ではなくなる、またはオーバーヘッドになるのはどの時点ですか?
-
23-08-2019 - |
質問
最近、大小を問わずすべての文字列連結に StringBuilder を使用していることに気づきましたが、最近のパフォーマンス テストでは、同僚の文字列連結を置き換えました。 stringout = string1 + "。"文字列2 StringBuilder のスタイル連結 (StringBuilder が毎回新しくなる 10000x + ループで使用されています) を使用して、マイナーな連結でどのような違いが生じるかを確認します。
パフォーマンス テストを何度も実行した結果、連結や StringBuilder に関係なく、変化はわずかに大きくも小さくもなったことがわかりました (これを言い換えると、 小さい 連結)。
StringBuilder オブジェクトを「新しくする」と、どの時点で StringBuilder オブジェクトを使用するメリットが無効になりますか?
解決
私が従うルールがある -
連結の数がコンパイル時に不明の場合、のStringBuilderを使用します。
だから、あなたの場合には、それぞれのStringBuilderは、ほんの数回を追加した後に廃棄されています。それは本当にのようなものと同じではありません。
string s = String.Empty;
for (int i = 0; i < 10000; ++i)
{
s += "A";
}
どこがそうでなければ、常に新しいメモリを割り当てることになるため、大幅にパフォーマンスを改善するのStringBuilderを使用します。
他のヒント
私が投稿した場所に別の答えがあると確信しています 私の記事へのリンク それからその概要ですが、ここでもう一度説明します。
必ず使用してください
StringBuilder
重要なループ内で連結している場合、特にループ内で何回反復するか (コンパイル時に) 確実にわからない場合。たとえば、一度に 1 文字ずつファイルを読み取り、+= 演算子を使用して文字列を構築することは、パフォーマンス上の自殺行為となる可能性があります。連結する必要があるすべてを 1 つのステートメントで (読みやすく) 指定できる場合は、必ず連結演算子を使用してください。(連結する配列がある場合は、呼び出しを検討してください。
String.Concat
明示的に - またはString.Join
区切り文字が必要な場合。)リテラルをいくつかの連結されたビットに分割することを恐れないでください。結果は同じになります。たとえば、パフォーマンスを損なうことなく、長いリテラルを複数の行に分割することで読みやすさを向上させることができます。
連結の次の反復にフィードする以外の目的で連結の中間結果が必要な場合は、次のようにします。
StringBuilder
あなたを助けるつもりはありません。たとえば、姓と名からフルネームを作成し、その末尾に 3 番目の情報 (おそらくニックネーム) を追加する場合、次のような利点のみが得られます。StringBuilder
他の目的で (名 + 姓) 文字列が必要ない場合 (例では、Person
物体)。実行する必要のある連結がいくつかあり、実際にそれを別のステートメントで実行したい場合は、どちらの方法を選択してもあまり問題はありません。どちらの方法がより効率的かは、連結数、関係する文字列のサイズ、および連結される順序によって異なります。そのコード部分がパフォーマンスのボトルネックであると本当に確信している場合は、両方の方法でプロファイリングまたはベンチマークを実行します。
時にはそれがドキュメントを見て価値があります:
StringBuilderのはあなたに何を得ていないので、連結のパフォーマンス 文字列のための操作や StringBuilderオブジェクトは、どのように依存します 多くの場合、メモリの割り当てが発生します。 A 常に文字列の連結演算 一方、メモリを割り当て StringBuilderの連結演算 場合のみ、メモリを割り当て StringBuilderオブジェクトバッファがあまりにもあります 新しいデータを収容するために小さな。 その結果、Stringクラスがあります 連結のための好ましいです 文字列の固定数であれば操作 オブジェクトが連結されます。その中で 場合、個々の連結 操作はさえに結合されることがあります コンパイラによって単一の操作。 A StringBuilderオブジェクトが好適です 連結演算の場合 文字列されている任意の数 連結;例えば、もしループ の乱数を連結 ユーザー入力の文字列ます。
あなたの例では、出力文字列ごとに1つだけ連結があります。あなたは、の文字列と同じ文字列の何倍、例えばに追加している場合にのStringBuilderを使用する必要があります。
stringOut = ...
for(...)
stringOut += "."
stringOut += string2
私の経験則は単純です。
- 最終結果を生成する単一の式を合理的に作成できる場合は、次を使用します。
+
. - それができない場合 (サイズまたは変動性のため)、StringBuilder を使用してください。
私の経験では、次のような表現があります。
"Id: " + item.id + " name: " + item.name
以下よりもはるかに簡単に記述して理解することができます。
StringBuilder sb = new StringBuilder();
sb.append("Id: ").append(item.id);
sb.append(" name: ").append(item.name);
(その後に次の文字列を使用します) sb
上記の式が書かれている場所)、同様にうまく機能します (ヒント:その理由はコンパイルされたコードを見てください!)
一方、単一行の式として記述するのが現実的ではない、時間の経過とともに文字列 (プログラムの実行中) またはスペース (コードのさまざまな部分からの値で構成される) を蓄積する必要がある場合は、とすると、StringBuilder は次のオーバーヘッド (時間とメモリの大量消費) を回避します。
String s = somethingExpression;
...
s += someOtherExpression;
...
s += yetAnotherExpression;
...
あなたが繰り返しの一握り以上でループ内で連結している場合、その後のStringBuilderはほとんど常により良いパフォーマンスを提供しますが、確実に知る唯一の方法は、実際のプロファイルにある -[T]彼文字列クラスが好適です 連結演算固定する場合 Stringオブジェクトの数があります 連結。その場合には、 個々の連結演算 でも単一に統合される可能性があります コンパイラによる操作。 A StringBuilderオブジェクトが好適です 連結演算の場合 文字列されている任意の数 連結;例えば、もしループ の乱数を連結 ユーザー入力の文字列ます。
私は、答えは「それが依存」であると思いますます。
コーディングホラーの時にこのオーバーに興味深い記事があります。
:ジェフは、デュアルコア3.5 GHzのCore 2 Duoプロセッサ10万回の繰り返しのために、以下の結果を得ました Simple Concatenation - 606 ms
String.Format - 665 ms
string.Concat - 587 ms
String.Replace - 979 ms
StringBuilder - 588 ms
ドットネットのPerl でから:
のStringBuilderを使用する場合は?
のStringBuilderは完全に最適化され、そしてそれは、その内部実装以外の文字列の連結方式に論理的な改善を提供しています。それはあなたの高性能アプリケーションやWebサイトで正しくそれを使用することが重要である、と述べています。
時には、単純な文字列Concatsで4回の以下の反復の小さなループを使用しても大丈夫です。しかし、エッジの場合、これは悲惨なことができます。 StringBuilderを使用してエッジケースのために計画しています。