String
とStringBuilder
の違いを理解しています(StringBuilder
は可変です)が、2つの間に大きなパフォーマンスの違いはありますか?
私が取り組んでいるプログラムには、多くのケース駆動型の文字列追加(500以上)があります。 StringBuilder
を使用する方が適切ですか?
はい、パフォーマンスの違いは重要です。 KB記事「 Visual C#で文字列連結のパフォーマンスを向上させる方法 」を参照してください。
私は常に明確にするために最初にコーディングを試み、その後パフォーマンスを最適化することを試みました。それは他の方法で行うよりもはるかに簡単です!しかし、2つのアプリケーションのパフォーマンスに大きな違いが見られたので、今ではもう少し慎重に考えます。
幸いなことに、コードでパフォーマンス分析を実行して時間を費やしている場所を確認し、必要に応じてStringBuilder
を使用するように変更することは比較的簡単です。
次のようなものがある場合、ジリアンが4文字列について言ったことを明確にするために:
string a,b,c,d;
a = b + c + d;
その後、文字列とプラス演算子を使用すると高速になります。これは(Javaのように、Ericが指摘しているように)内部でStringBuilderを自動的に使用するためです(実際、StringBuilderも使用するプリミティブを使用します)
ただし、あなたがしていることが以下に近い場合:
string a,b,c,d;
a = a + b;
a = a + c;
a = a + d;
次に、StringBuilderを明示的に使用する必要があります。 .NetはここではStringBuilderを自動的に作成しません。それは無意味だからです。各行の最後にある「a」は(不変の)文字列でなければならないため、各行にStringBuilderを作成して配置する必要があります。速度を上げるには、構築が完了するまで同じStringBuilderを使用する必要があります。
string a,b,c,d;
StringBuilder e = new StringBuilder();
e.Append(b);
e.Append(c);
e.Append(d);
a = e.ToString();
StringBuilderの方が望ましいIF複数のループを実行している、またはコードパスで分岐している...ただし、PUREパフォーマンスのためにSINGLE =文字列宣言、それははるかに高性能です。
例えば:
string myString = "Some stuff" + var1 + " more stuff"
+ var2 + " other stuff" .... etc... etc...;
よりもパフォーマンスが高い
StringBuilder sb = new StringBuilder();
sb.Append("Some Stuff");
sb.Append(var1);
sb.Append(" more stuff");
sb.Append(var2);
sb.Append("other stuff");
// etc.. etc.. etc..
この場合、StringBuildは保守性が高いと見なされますが、単一の文字列宣言よりもパフォーマンスは高くありません。
ただし、10回のうち9回...文字列ビルダーを使用します。
サイドノート:string + varは、stringBuilderを内部で使用する(一般的に)string.Formatアプローチよりもパフォーマンスが高い(疑わしい場合は...リフレクターをチェックしてください!)
このベンチマークは、3つ以下の文字列を組み合わせると、通常の連結が高速になることを示しています。
http://www.chinhdo.com/20070224/stringbuilder-is-not-always-faster/
StringBuilderは、特に500個の文字列を追加する場合に、メモリ使用量を大幅に改善できます。
次の例を考えてみましょう。
string buffer = "The numbers are: ";
for( int i = 0; i < 5; i++)
{
buffer += i.ToString();
}
return buffer;
メモリ内で何が起こりますか?次の文字列が作成されます。
1 - "The numbers are: "
2 - "0"
3 - "The numbers are: 0"
4 - "1"
5 - "The numbers are: 01"
6 - "2"
7 - "The numbers are: 012"
8 - "3"
9 - "The numbers are: 0123"
10 - "4"
11 - "The numbers are: 01234"
12 - "5"
13 - "The numbers are: 012345"
これらの5つの数字を文字列の最後に追加することで、13個の文字列オブジェクトを作成しました!そして、そのうちの12は役に立たなかった!うわー!
StringBuilderはこの問題を修正します。よく耳にする「可変文字列」ではありません(。NETのすべての文字列は不変です)。内部バッファー、charの配列を保持することで機能します。 Append()またはAppendLine()を呼び出すと、文字配列の最後の空のスペースに文字列が追加されます。配列が小さすぎる場合、新しい大きな配列を作成し、そこにバッファーをコピーします。したがって、上記の例では、StringBuilderは、バッファーのサイズに応じて、ストリングに5つの追加すべてを含めるために単一の配列のみを必要とする場合があります。 StringBuilderに、コンストラクター内のバッファーの大きさを伝えることができます。
String
連結とStringBuilder
を使用した場合の速度の違いを示す簡単な例:
System.Diagnostics.Stopwatch time = new Stopwatch();
string test = string.Empty;
time.Start();
for (int i = 0; i < 100000; i++)
{
test += i;
}
time.Stop();
System.Console.WriteLine("Using String concatenation: " + time.ElapsedMilliseconds + " milliseconds");
結果:
ストリング連結の使用:15423ミリ秒
StringBuilder test1 = new StringBuilder();
time.Reset();
time.Start();
for (int i = 0; i < 100000; i++)
{
test1.Append(i);
}
time.Stop();
System.Console.WriteLine("Using StringBuilder: " + time.ElapsedMilliseconds + " milliseconds");
結果:
StringBuilderの使用:10ミリ秒
その結果、StringBuilder
を使用した2回目の反復に10ミリ秒かかったのに対し、最初の反復には15423ミリ秒かかりました。
StringBuilder
を使用する方がはるかに高速であるように見えます。
はい。StringBuilder
を使用すると、文字列に対して繰り返し操作を実行する際のパフォーマンスが向上します。これは、すべての変更が単一のインスタンスに対して行われるため、String
のような新しいインスタンスを作成する代わりに多くの時間を節約できるためです。
String
System
名前空間の下StringBuilder
(可変ストリング)
System.Text
名前空間の下Dotnet mobの記事を強くお勧めします: C#でのString Vs StringBuilder 。
関連するスタックオーバーフローの質問: C#で文字列が変更されない場合の文字列の可変性 。
StringBuilderは、余分なメモリの使用を犠牲にして、割り当てと割り当ての数を減らします。適切に使用すると、結果が見つかるまで、コンパイラが繰り返しますます大きな文字列を割り当てる必要がなくなります。
string result = "";
for(int i = 0; i != N; ++i)
{
result = result + i.ToString(); // allocates a new string, then assigns it to result, which gets repeated N times
}
vs.
String result;
StringBuilder sb = new StringBuilder(10000); // create a buffer of 10k
for(int i = 0; i != N; ++i)
{
sb.Append(i.ToString()); // fill the buffer, resizing if it overflows the buffer
}
result = sb.ToString(); // assigns once
文字列と文字列ビルダー:
最初に知っておく必要があるのは、これら2つのクラスがどのアセンブリに存在するかということです。
そう、
stringはSystem
名前空間に存在します。
そして
StringBuilderはSystem.Text
名前空間に存在します。
string宣言の場合:
System
名前空間を含める必要があります。このようなもの。 Using System;
そして
StringBuilder宣言の場合:
System.text
名前空間を含める必要があります。このようなもの。 Using System.text;
さて、実際の質問に来てください。
string&StringBuilderの違いは何ですか?
これら2つの主な違いは、
stringは不変です。
そして
StringBuilderは可変です。
それでは、immutableとmutableの違いについて説明しましょう
Mutable::Changableを意味します。
Immutable::Not Changableを意味します。
例:
using System;
namespace StringVsStrigBuilder
{
class Program
{
static void Main(string[] args)
{
// String Example
string name = "Rehan";
name = name + "Shah";
name = name + "RS";
name = name + "---";
name = name + "I love to write programs.";
// Now when I run this program this output will be look like this.
// output : "Rehan Shah RS --- I love to write programs."
}
}
}
したがって、この場合、同じオブジェクトを5回変更します。
明らかな質問はそれです!同じ文字列を5回変更すると、実際に何が起こるかがわかります。
これは、同じ文字列を5回変更したときに何が起こるかです。
図を見てみましょう。
説明:
最初にこの変数「name」を「Rehan」に初期化すると、i-e string name = "Rehan"
この変数はスタック「name」で作成され、その「Rehan」値を指します。この行が実行された後: "name = name +" Shah "。参照変数はそのオブジェクト" Rehan "を指していないため、" Shah "を指しています。
したがって、string
は不変です。つまり、メモリ内にオブジェクトを作成すると、変更できません。
したがって、name
変数を連結すると、前のオブジェクトがメモリ内に残り、別の新しい文字列オブジェクトが作成されます...
したがって、上の図から、5つのオブジェクトがあり、4つのオブジェクトは破棄され、まったく使用されていません。彼らはまだメモリに残り、メモリの量を占有します。 「ガベージコレクター」は、メモリからリソースをきれいにするために責任を負います。
そのため、文字列を何度も操作するときはいつでも、作成されたansがメモリ内に残っている多くのオブジェクトがあります。
これが文字列変数の話です。
それでは、StringBuilderオブジェクトを見てみましょう。 例:
using System;
using System.Text;
namespace StringVsStrigBuilder
{
class Program
{
static void Main(string[] args)
{
// StringBuilder Example
StringBuilder name = new StringBuilder();
name.Append("Rehan");
name.Append("Shah");
name.Append("RS");
name.Append("---");
name.Append("I love to write programs.");
// Now when I run this program this output will be look like this.
// output : "Rehan Shah Rs --- I love to write programs."
}
}
}
したがって、この場合、同じオブジェクトを5回変更します。
明らかな質問はそれです!同じStringBuilderを5回変更すると、実際に何が起こるかがわかります。
これは、同じStringBuilderを5回変更したときに発生します。
説明:StringBuilderオブジェクトの場合。新しいオブジェクトは取得できません。同じオブジェクトがメモリ内で変更されるため、オブジェクトを変更して10,000回言っても、stringBuilderオブジェクトは1つしかありません。
ガベージオブジェクトまたは参照されていないstringBuilderオブジェクトがたくさんあるのは、なぜ変更できるのかという理由からです。それは時間の経過とともに変化するという意味ですか?
違い:
StringまたはStringBuilderオブジェクトの連結操作のパフォーマンスは、メモリ割り当てが発生する頻度に依存します。 String連結操作は常にメモリを割り当てますが、StringBuilder連結操作は、StringBuilderオブジェクトバッファが小さすぎて新しいデータを収容できない場合にのみメモリを割り当てます。そのため、一定数のStringオブジェクトが連結される場合、連結操作にはStringクラスが適しています。その場合、個々の連結操作は、コンパイラーによって単一の操作に結合されることさえあります。 StringBuilderオブジェクトは、任意の数の文字列が連結される場合の連結操作に適しています。たとえば、ループがランダムな数のユーザー入力の文字列を連結する場合。
ソース: MSDN
StringBuilder
は、多くの非定数値から文字列を構築するのに適しています。
HTMLまたはXMLドキュメントの複数行の値やその他のテキストチャンクなど、多くの定数値から文字列を構築している場合、ほとんどすべてのコンパイラーが同じ文字列に追加するだけで済みます。 「定数フォールディング」、大量の定数操作がある場合に解析ツリーを縮小するプロセス(int minutesPerYear = 24 * 365 * 60
のようなものを記述するときにも使用されます)。また、定数以外の値が互いに追加されている単純なケースでは、.NETコンパイラーは、コードをStringBuilder
の動作に似たものに減らします。
ただし、コンパイラによって追加を簡単なものに減らすことができない場合は、StringBuilder
が必要になります。 fizchが指摘しているように、それはループ内で発生する可能性が高くなります。
「 マイクロ最適化シアターの悲しい悲劇 」を検討してください。
連結に文字列を使用すると、O(n^2)
のオーダーでランタイムが複雑になる可能性があります。
StringBuilder
を使用すると、実行する必要のあるメモリのコピーがはるかに少なくなります。 StringBuilder(int capacity)
を使用すると、最終的なString
の大きさを見積もることができれば、パフォーマンスを向上させることができます。正確ではない場合でも、おそらくStringBuilder
の容量を数回増やすだけでパフォーマンスが向上します。
文字列ストレージに使用する前にStringBuilder
のインスタンスでEnsureCapacity(int capacity)
メソッド呼び出しを使用すると、パフォーマンスが大幅に向上します。私は通常、インスタンス化後にコード行でそれを呼び出します。次のようにStringBuilder
をインスタンス化する場合と同じ効果があります。
var sb = new StringBuilder(int capacity);
この呼び出しは、必要なメモリを事前に割り当てます。これにより、複数のAppend()
操作中のメモリ割り当てが少なくなります。必要なメモリ量について経験に基づいた推測を行う必要がありますが、ほとんどのアプリケーションではこれはそれほど難しくないはずです。私は通常、少し多すぎる記憶の側でエラーを起こします(私たちは1k程度話しています)。
4つ以上の文字列を追加する必要がある場合、StringBuilderの方が高速だと思います。さらに、AppendLineのようなクールなことができます。
.NETでは、StringBuilderは文字列を追加するよりも高速です。 Javaでは、文字列を追加するときに内部でStringBufferを作成するだけなので、実際には違いはありません。なぜ.NETでこれを行っていないのか、私にはわかりません。
StringとStringBuilderは、実際には両方とも不変であり、StringBuilderにはサイズをより効率的に管理できるバッファーが組み込まれています。 StringBuilderのサイズを変更する必要があるのは、ヒープに再割り当てされたときです。デフォルトでは、16文字のサイズになっていますが、コンストラクターでこれを設定できます。
例えば。
StringBuilder sb = new StringBuilder(50);
前の回答に加えて、このような問題を考えるときに私が最初に行うことは、小さなテストアプリケーションを作成することです。このアプリ内で、両方のシナリオのタイミングテストを実行し、自分で確認してください。
私見では、500以上の文字列エントリを追加する場合は必ずStringBuilderを使用する必要があります。
文字列の連結にはさらにコストがかかります。 Javaでは、必要に応じてStringBufferまたはStringBuilderを使用できます。同期された、スレッドセーフな実装が必要な場合は、StringBufferを使用します。これは、文字列の連結よりも高速です。
同期またはスレッドセーフな実装が必要ない場合は、StringBuilderを使用してください。これは、文字列の連結よりも高速であり、同期化のオーバーヘッドがないため、StringBufferよりも高速です。
StringBuilder
は非常に効率的ですが、大量の文字列を変更しない限り、そのパフォーマンスは見られません。
以下に、パフォーマンスの例を示す簡単なコードを示します。ご覧のとおり、大規模な反復を実行した場合にのみ、大幅なパフォーマンスの向上が見られます。
ご覧のとおり、StringBuilder
を使用した100万回の反復はほとんど瞬時でしたが、200,000回の反復には22秒かかりました。
string s = string.Empty;
StringBuilder sb = new StringBuilder();
Console.WriteLine("Beginning String + at " + DateTime.Now.ToString());
for (int i = 0; i <= 50000; i++)
{
s = s + 'A';
}
Console.WriteLine("Finished String + at " + DateTime.Now.ToString());
Console.WriteLine();
Console.WriteLine("Beginning String + at " + DateTime.Now.ToString());
for (int i = 0; i <= 200000; i++)
{
s = s + 'A';
}
Console.WriteLine("Finished String + at " + DateTime.Now.ToString());
Console.WriteLine();
Console.WriteLine("Beginning Sb append at " + DateTime.Now.ToString());
for (int i = 0; i <= 1000000; i++)
{
sb.Append("A");
}
Console.WriteLine("Finished Sb append at " + DateTime.Now.ToString());
Console.ReadLine();
上記のコードの結果:
開始文字列+ 2013年1月28日16時55分40秒。
2013年1月28日16時55分40秒のストリング+.
開始文字列+ 2013年1月28日16時55分40秒。
2013年1月28日16:56:02にストリング+を完成。
2013年1月28日16:56:02にSbの追加を開始します。
28/01/2013 16:56:02にSbの追加を終了しました。
一般的な経験則として、文字列の値を複数回設定する必要がある場合、または文字列に追加がある場合は、文字列ビルダーである必要があります。過去に書いたアプリケーションを見たことがありますが、文字列ビルダーは、成長し続けているように見える巨大なメモリフットプリントを持っています。文字列ビルダーを使用するようにこれらのプログラムを変更すると、メモリ使用量が大幅に削減されます。今、私は文字列ビルダーを誓います。
私のアプローチは、常に4つ以上の文字列を連結するときにStringBuilderを使用することでしたOR連結がどのように行われるかわからないとき。
StringBuilderがおそらく望ましいです。理由は、将来必要な追加のためのスペースを空けるために、現在必要な(文字数を設定する)よりも多くのスペースを割り当てるためです。その後、現在のバッファーに収まる将来の追加では、メモリの割り当てやガベージコレクションは必要ありません。一般に、複雑な文字列の連結または複数の書式設定にStringBuilderを使用し、データが完了したら通常の文字列に変換し、不変オブジェクトが再び必要になります。
文字列の連結を多数行う場合は、StringBuilderを使用します。文字列と連結すると、毎回新しい文字列が作成され、メモリが消費されます。
アレックス