Frage

Angenommen, ich habe ein stringbuilder-Objekt in C#, das dies tut:

StringBuilder sb = new StringBuilder();
string cat = "cat";
sb.Append("the ").Append(cat).(" in the hat");
string s = sb.ToString();

wäre das so effizient und mehr effiziente als:

string cat = "cat";
string s = String.Format("The {0} in the hat", cat);

Wenn ja, warum?

BEARBEITEN

Nach einige interessante Antworten, merkte ich, dass ich wahrscheinlich hätte ein wenig klarer, was ich gefragt wurde.Ich war nicht so viel zu Fragen, für die war schneller bei der Verkettung von string, aber die ist schneller bei der Injektion ein string in einem anderen.

In beiden Fällen oben ich wollen zu injizieren eine oder mehrere Zeichenfolgen in der Mitte von einer vordefinierten Vorlage string.

Sorry für die Verwirrung

War es hilfreich?

Lösung

HINWEIS: Diese Antwort wurde geschrieben, wenn .NET 2.0 wurde die aktuelle version.Dies kann nicht mehr gelten, die in späteren Versionen.

String.Format verwendet StringBuilder 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, so wird die Frage "ist StringBuilder.Append() schneller als StringBuilder.AppendFormat()"?

Ohne benchmarking würde ich wohl sagen, dass die code-Beispiel oben würde schneller ausgeführt werden, mit .Append().Aber es ist eine Vermutung, versuchen Sie benchmarking-und/oder Profilerstellung die beiden, um einen vernünftigen Vergleich.

Dieser Kerl, Jerry Dixon, habe einige benchmarking:

http://jdixon.dotnetdevelopersjournal.com/string_concatenation_stringbuilder_and_stringformat.htm

Aktualisiert:

Leider ist der link oben hat inzwischen verstorben.Aber es ist immer noch eine Kopie auf dem Weg Zurück Maschine:

http://web.archive.org/web/20090417100252/http://jdixon.dotnetdevelopersjournal.com/string_concatenation_stringbuilder_and_stringformat.htm

Am Ende des Tages hängt es ab, ob Ihr string-Formatierung genannt wird wiederholt, d.h.Sie machen einige ernsthafte Textverarbeitung über 100 Megabyte text, oder ob wird aufgerufen, wenn ein Benutzer auf eine Schaltfläche klickt, und nun schon wieder.Es sei denn, Sie machen einige riesige batch-Verarbeitung job würde ich stick mit String.- Format unterstützt es die Lesbarkeit des Codes.Wenn Sie vermuten, dass ein perf Engpass dann kleben Sie einen profiler auf Ihren code und sehen, wo er wirklich ist.

Andere Tipps

Aus der MSDN-Dokumentation:

Die Leistung einer Verkettung Betrieb für einen String oder StringBuilder-Objekt hängt davon ab, wie oft eine Speicherzuweisung erfolgt.String-Verkettung Betrieb immer Speicher zuweist, während StringBuilder verkettungsoperation nur Speicher reserviert, wenn der StringBuilder-Objekt Puffer ist zu klein, um die neuen Daten aufzunehmen.Folglich ist die String-Klasse ist besser für eine Verkettung operation, wenn eine Feste Anzahl von String-Objekten verkettet werden.In diesem Fall werden die einzelnen Verkettung von Operationen könnten auch kombiniert werden in eine einzige Bedienung durch den compiler.Ein StringBuilder-Objekt wird bevorzugt für eine Verkettung Betrieb, wenn eine beliebige Anzahl von Zeichenfolgen verkettet werden;zum Beispiel, wenn eine Schleife verbindet Sie eine zufällige Anzahl von Zeichenfolgen des Benutzer-Eingabe.

Ich lief einige schnelle performance-benchmarks, und für 100.000 Operationen gemittelt über 10 läuft, wird die erste Methode (String-generator) nimmt fast die Hälfte der Zeit von der zweiten (String-Format).

Also, wenn dies zu Häufig vorkommt, ist es egal.Aber wenn es eine gemeinsame operation, dann können Sie die erste Methode verwenden.

Ich würde erwarten String.Format langsamer zu sein - Sie muss analysiert die Zeichenfolge und dann verketten Sie.

Paar Anmerkungen:

  • Format ist der Weg zu gehen für den Benutzer sichtbaren strings in professionellen Anwendungen;dies vermeidet Fehler-Lokalisierung
  • Wenn Sie wissen, die Länge der resultierenden Zeichenfolge vorher, verwenden Sie die StringBuilder(Int32) Konstruktor Definition der Kapazität

Wenn auch nur, weil string.Format nicht genau das tun, was Sie vielleicht denken, hier ist eine Wiederholung des tests 6 Jahre später auf Net45.

Concat ist immer noch der Schnellste, aber eigentlich ist es weniger als 30% Unterschied.StringBuilder und Format unterscheiden sich um knapp 5-10%.Ich habe Abweichungen von 20% mit der Ausführung der tests ein paar mal.

Millisekunden, eine million Iterationen:

  • Verkettung:367
  • New stringBuilder für jede Taste:452
  • Cache StringBuilder:419
  • string.Format:475

Die Lektion, die ich mitnehmen ist, dass der performance-Unterschied ist trivial und so sollte es nicht stoppen, schreiben Sie den einfachsten lesbaren code, den Sie können.Was für mein Geld ist oft, aber nicht immer 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);

Ich denke, in den meisten Fällen wie diesem Klarheit, und nicht Effizienz, sollte Ihre größte Sorge.Es sei denn, du bist Brech zusammen Tonnen von strings oder etwas zu bauen für einen niedrigeren powered mobile Gerät, das wahrscheinlich nicht viel von einer delle in Ihrem Lauf Geschwindigkeit.

Ich habe herausgefunden, dass in Fällen, in denen ich Gebäude strings, die in einem ziemlich linear, entweder tun, geraden und / oder Verkettungen mit StringBuilder ist Ihre beste option.Ich schlage vor, diese in Fällen, in denen die Mehrheit der Zeichenfolge, die Sie erstellen, ist dynamisch.Da sehr wenig von dem text ist statisch, das wichtigste ist, dass es klar ist, wo jedes Stück dynamischer text wird im Fall muss man es in Zukunft aktualisiert.

Auf der anderen Seite, wenn Sie sind reden über einen großen Teil des statischen text mit zwei oder drei Variablen, auch wenn es ein bisschen weniger effizient, ich denke, die Klarheit, die Sie gewinnen aus string.- Format macht es lohnt sich.Ich habe diese früher in dieser Woche, wenn man statt einem wenig dynamischen text in der Mitte einer 4-seitigen Dokument.Es wird einfacher sein, zu aktualisieren, dass die großen Brocken an text, wenn Ihr in einem Stück, als das update drei Stücke, die Sie verketten Sie zusammen.

String.- Format verwendet StringBuilder intern...so logisch das führt zu der Idee, dass es ein bisschen weniger performant durch mehr Aufwand.Jedoch, eine einfache string-Verkettung ist die Schnellste Methode der Injektion eine Schnur zwischen zwei others...by einen bedeutenden Grad.Dieser Nachweis wurde demonstriert, indem Rico Mariani in seiner ersten Performance-Quiz, Jahren.Die einfache Tatsache ist, dass die Verkettungen...wenn die Anzahl der string-teilen bekannt ist (ohne Beschränkung. man konnte verketten tausend Teile...solange Sie wissen, dass es immer 1000 Teile)...sind immer schneller als StringBuilder oder String.Format.Sie können durchgeführt werden mit einem einzigen Speicher-Zuweisung ein, eine Reihe von Speicher kopiert. Hier ist der Beweis

Und hier ist der eigentliche code für einige String.Concat Methoden, die letztlich call FillStringChecked, die verwendet Verweise zu kopieren-Speicher (extrahiert via Reflektor):

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!

Oh, auch, das Schnellste wäre:

string cat = "cat";
string s = "The " + cat + " in the hat";

Es hängt wirklich.Für kleine Zeichenfolgen mit wenigen Verkettungen, es ist tatsächlich schneller, einfach zu hängen Sie strings.

String s = "String A" + "String B";

Aber für das größere string (sehr, sehr große Zeichenfolgen), ist es dann effizienter, verwenden Sie StringBuilder.

In beiden Fällen oben ich wollen zu injizieren eine oder mehrere Zeichenfolgen in der Mitte von einer vordefinierten Vorlage string.

In dem Fall würde ich vorschlagen, String.Formatieren ist der Schnellste, weil es ist design für die genaue Zweck.

Es hängt wirklich von Ihrer Nutzung Muster.
Eine detaillierte benchmark zwischen string.Join, string,Concat und string.Format kann hier gefunden werden: String.Format Ist nicht Geeignet für Intensive Logging

Ich würde vorschlagen, nicht, da String.- Format wurde nicht entworfen, für Verkettung, war es design für die Formatierung der Ausgabe von verschiedenen Eingänge wie ein Datum.

String s = String.Format("Today is {0:dd-MMM-yyyy}.", DateTime.Today);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top