Angenommen, ich habe einen Stringbuilder in C #, der dies ausführt:
StringBuilder sb = new StringBuilder();
string cat = "cat";
sb.Append("the ").Append(cat).(" in the hat");
string s = sb.ToString();
Wäre das so effizient oder effizienter als:
string cat = "cat";
string s = String.Format("The {0} in the hat", cat);
Wenn ja warum?
BEARBEITEN
Nach einigen interessanten Antworten wurde mir klar, dass ich wahrscheinlich etwas klarer hätte sein sollen, was ich fragte. Ich habe nicht so sehr gefragt, welche schneller eine Saite verketten, aber welche schneller eine Saite in eine andere injizieren können .
In beiden oben genannten Fällen möchte ich eine oder mehrere Zeichenfolgen in die Mitte einer vordefinierten Vorlagenzeichenfolge einfügen.
Entschuldigung für die Verwirrung
string s = "The "+cat+" in the hat";
möglicherweise das schnellste, es sei denn, es wird in einer Schleife verwendet. In diesem Fall ist es am schnellsten, wenn esStringBuilder
außerhalb der Schleife initialisiert wird.Antworten:
HINWEIS: Diese Antwort wurde geschrieben, als .NET 2.0 die aktuelle Version war. Dies gilt möglicherweise nicht mehr für spätere Versionen.
String.Format
verwendet eineStringBuilder
intern:public static string Format(IFormatProvider provider, string format, params object[] args) { if ((format == null) || (args == null)) { throw new ArgumentNullException((format == null) ? "format" : "args"); } StringBuilder builder = new StringBuilder(format.Length + (args.Length * 8)); builder.AppendFormat(provider, format, args); return builder.ToString(); }
Der obige Code ist ein Ausschnitt aus mscorlib, daher lautet die Frage "ist
StringBuilder.Append()
schneller alsStringBuilder.AppendFormat()
"?Ohne Benchmarking würde ich wahrscheinlich sagen, dass das obige Codebeispiel mit schneller ausgeführt werden würde
.Append()
. Aber es ist eine Vermutung, versuchen Sie, die beiden zu vergleichen und / oder zu profilieren, um einen richtigen Vergleich zu erhalten.Dieser Kerl, Jerry Dixon, hat ein Benchmarking durchgeführt:
Aktualisiert:
Leider ist der obige Link inzwischen gestorben. Es gibt jedoch noch eine Kopie auf der Way Back Machine:
Letztendlich hängt es davon ab, ob Ihre Zeichenfolgenformatierung wiederholt aufgerufen wird, dh Sie führen eine ernsthafte Textverarbeitung über 100 Megabyte Text durch oder ob sie aufgerufen wird, wenn ein Benutzer ab und zu auf eine Schaltfläche klickt. Wenn Sie keine große Stapelverarbeitungsaufgabe ausführen, bleibe ich bei String.Format, da dies die Lesbarkeit des Codes verbessert. Wenn Sie einen Perf-Engpass vermuten, kleben Sie einen Profiler auf Ihren Code und sehen Sie, wo er wirklich ist.
quelle
.ToString()
dasStringBuilder
Objekt niemals aufruft . Bei sehr vielen Iterationen macht diese Zeit einen großen Unterschied und bedeutet, dass er Äpfel nicht ganz mit Äpfeln vergleicht. Das ist der Grund, warum er so großartige Leistungen erbringtStringBuilder
und wahrscheinlich für seine Überraschung verantwortlich ist. Ich wiederholte nur die Benchmark diesen Fehler zu korrigieren und bekam die erwarteten Ergebnisse: DerString
+
Betreiber wurde am schnellsten, gefolgt vonStringBuilder
, mitString.Format
der Nachhut.Aus der MSDN-Dokumentation :
quelle
Ich habe einige schnelle Leistungsbenchmarks durchgeführt, und für 100.000 Operationen, die über 10 Läufe gemittelt wurden, dauert die erste Methode (String Builder) fast die Hälfte der zweiten (String-Format).
Wenn dies selten vorkommt, spielt es keine Rolle. Wenn es sich jedoch um eine häufige Operation handelt, möchten Sie möglicherweise die erste Methode verwenden.
quelle
Ich würde erwarten, dass String.Format langsamer ist - es muss den String analysieren und dann verketten.
Einige Anmerkungen:
quelle
Ich denke, in den meisten Fällen sollte diese Klarheit und nicht die Effizienz Ihre größte Sorge sein. Wenn Sie nicht Tonnen von Saiten zerkleinern oder etwas für ein mobiles Gerät mit geringerer Leistung bauen, wird dies Ihre Laufgeschwindigkeit wahrscheinlich nicht wesentlich beeinträchtigen.
Ich habe festgestellt, dass in Fällen, in denen ich Strings ziemlich linear erstelle, entweder direkte Verkettungen oder die Verwendung von StringBuilder die beste Option sind. Ich schlage dies in Fällen vor, in denen der Großteil der von Ihnen erstellten Zeichenfolge dynamisch ist. Da nur sehr wenig Text statisch ist, ist das Wichtigste, dass klar ist, wo jedes dynamische Textstück abgelegt wird, falls es in Zukunft aktualisiert werden muss.
Auf der anderen Seite, wenn Sie über einen großen Teil statischen Textes mit zwei oder drei Variablen sprechen, auch wenn er etwas weniger effizient ist, denke ich, dass die Klarheit, die Sie durch string.Format gewinnen, es wert macht. Ich habe dies Anfang dieser Woche verwendet, als ich ein Stück dynamischen Text in die Mitte eines 4-seitigen Dokuments einfügen musste. Es ist einfacher, diesen großen Textblock zu aktualisieren, wenn er aus einem Stück besteht, als drei Teile zu aktualisieren, die Sie miteinander verketten.
quelle
Wenn auch nur, weil string.Format nicht genau das tut, was Sie vielleicht denken, hier ist eine Wiederholung der Tests 6 Jahre später auf Net45.
Concat ist immer noch am schnellsten, aber es ist wirklich weniger als 30% Unterschied. StringBuilder und Format unterscheiden sich um kaum 5-10%. Ich habe einige Male Abweichungen von 20% bei den Tests festgestellt.
Millisekunden, eine Million Iterationen:
Die Lektion, die ich wegnehme, ist, dass der Leistungsunterschied trivial ist und Sie daher nicht davon abhalten sollten, den einfachsten lesbaren Code zu schreiben, den Sie können. Was für mein Geld oft aber nicht immer ist
a + b + c
.const int iterations=1000000; var keyprefix= this.GetType().FullName; var maxkeylength=keyprefix + 1 + 1+ Math.Log10(iterations); Console.WriteLine("KeyPrefix \"{0}\", Max Key Length {1}",keyprefix, maxkeylength); var concatkeys= new string[iterations]; var stringbuilderkeys= new string[iterations]; var cachedsbkeys= new string[iterations]; var formatkeys= new string[iterations]; var stopwatch= new System.Diagnostics.Stopwatch(); Console.WriteLine("Concatenation:"); stopwatch.Start(); for(int i=0; i<iterations; i++){ var key1= keyprefix+":" + i.ToString(); concatkeys[i]=key1; } Console.WriteLine(stopwatch.ElapsedMilliseconds); Console.WriteLine("New stringBuilder for each key:"); stopwatch.Restart(); for(int i=0; i<iterations; i++){ var key2= new StringBuilder(keyprefix).Append(":").Append(i.ToString()).ToString(); stringbuilderkeys[i]= key2; } Console.WriteLine(stopwatch.ElapsedMilliseconds); Console.WriteLine("Cached StringBuilder:"); var cachedSB= new StringBuilder(maxkeylength); stopwatch.Restart(); for(int i=0; i<iterations; i++){ var key2b= cachedSB.Clear().Append(keyprefix).Append(":").Append(i.ToString()).ToString(); cachedsbkeys[i]= key2b; } Console.WriteLine(stopwatch.ElapsedMilliseconds); Console.WriteLine("string.Format"); stopwatch.Restart(); for(int i=0; i<iterations; i++){ var key3= string.Format("{0}:{1}", keyprefix,i.ToString()); formatkeys[i]= key3; } Console.WriteLine(stopwatch.ElapsedMilliseconds); var referToTheComputedValuesSoCompilerCantOptimiseTheLoopsAway= concatkeys.Union(stringbuilderkeys).Union(cachedsbkeys).Union(formatkeys).LastOrDefault(x=>x[1]=='-'); Console.WriteLine(referToTheComputedValuesSoCompilerCantOptimiseTheLoopsAway);
quelle
String.Format wird
StringBuilder
intern verwendet ... so logisch, dass die Idee entsteht, dass es aufgrund des höheren Overheads etwas weniger performant wäre. Eine einfache String-Verkettung ist jedoch die schnellste Methode, um einen String zwischen zwei anderen zu injizieren ... in erheblichem Maße. Dieser Beweis wurde von Rico Mariani vor Jahren in seinem allerersten Performance Quiz demonstriert. Einfache Tatsache ist, dass Verkettungen ... wenn die Anzahl der String-Teile bekannt ist (ohne Einschränkung ... Sie könnten tausend Teile verketten ... solange Sie wissen, dass es immer 1000 Teile sind) ... immer schneller alsStringBuilder
oder String sind. Format. Sie können mit einer einzelnen Speicherzuordnung und einer Reihe von Speicherkopien durchgeführt werden. Hier ist der BeweisUnd hier ist der eigentliche Code für einige String.Concat-Methoden, die letztendlich FillStringChecked aufrufen, das Zeiger zum Kopieren des Speichers verwendet (extrahiert über Reflector):
public static string Concat(params string[] values) { int totalLength = 0; if (values == null) { throw new ArgumentNullException("values"); } string[] strArray = new string[values.Length]; for (int i = 0; i < values.Length; i++) { string str = values[i]; strArray[i] = (str == null) ? Empty : str; totalLength += strArray[i].Length; if (totalLength < 0) { throw new OutOfMemoryException(); } } return ConcatArray(strArray, totalLength); } public static string Concat(string str0, string str1, string str2, string str3) { if (((str0 == null) && (str1 == null)) && ((str2 == null) && (str3 == null))) { return Empty; } if (str0 == null) { str0 = Empty; } if (str1 == null) { str1 = Empty; } if (str2 == null) { str2 = Empty; } if (str3 == null) { str3 = Empty; } int length = ((str0.Length + str1.Length) + str2.Length) + str3.Length; string dest = FastAllocateString(length); FillStringChecked(dest, 0, str0); FillStringChecked(dest, str0.Length, str1); FillStringChecked(dest, str0.Length + str1.Length, str2); FillStringChecked(dest, (str0.Length + str1.Length) + str2.Length, str3); return dest; } private static string ConcatArray(string[] values, int totalLength) { string dest = FastAllocateString(totalLength); int destPos = 0; for (int i = 0; i < values.Length; i++) { FillStringChecked(dest, destPos, values[i]); destPos += values[i].Length; } return dest; } private static unsafe void FillStringChecked(string dest, int destPos, string src) { int length = src.Length; if (length > (dest.Length - destPos)) { throw new IndexOutOfRangeException(); } fixed (char* chRef = &dest.m_firstChar) { fixed (char* chRef2 = &src.m_firstChar) { wstrcpy(chRef + destPos, chRef2, length); } } }
Also dann:
string what = "cat"; string inthehat = "The " + what + " in the hat!";
Genießen!
quelle
Ach ja, das schnellste wäre:
string cat = "cat"; string s = "The " + cat + " in the hat";
quelle
StringBuilder
wird diese Art der Codierung in erster Linie optimiert.Es kommt wirklich darauf an. Bei kleinen Zeichenfolgen mit wenigen Verkettungen ist es tatsächlich schneller, nur die Zeichenfolgen anzuhängen.
String s = "String A" + "String B";
Bei größeren Zeichenfolgen (sehr, sehr große Zeichenfolgen) ist die Verwendung von StringBuilder jedoch effizienter.
quelle
In diesem Fall würde ich String.Format als das schnellste empfehlen, da es genau für diesen Zweck entwickelt wurde.
quelle
Es hängt wirklich von Ihrem Nutzungsmuster ab.
Eine detaillierte Benchmark zwischen
string.Join
,string,Concat
undstring.Format
kann hier gefunden werden: String.Format ist nicht geeignet für intensive Protokollierungquelle
Ich würde nicht vorschlagen, da String.Format nicht für die Verkettung konzipiert wurde, sondern für die Formatierung der Ausgabe verschiedener Eingaben, z. B. eines Datums.
String s = String.Format("Today is {0:dd-MMM-yyyy}.", DateTime.Today);
quelle