Question

Supposons que j'ai un constructeur de chaînes en C# qui fait ceci :

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

serait-ce aussi efficace ou plus efficace que d'avoir :

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

Si oui, pourquoi?

MODIFIER

Après quelques réponses intéressantes, j'ai réalisé que j'aurais probablement dû être un peu plus clair dans ce que je demandais.Je ne demandais pas vraiment ce qui était le plus rapide pour concaténer une chaîne, mais lequel était le plus rapide pour injection une chaîne dans une autre.

Dans les deux cas ci-dessus, je souhaite injecter une ou plusieurs chaînes au milieu d'une chaîne de modèle prédéfinie.

Désolé pour la confusion

Était-ce utile?

La solution

NOTE: Cette réponse a été écrite lorsque .NET 2.0 était la version actuelle.Cela peut ne plus s'appliquer aux versions ultérieures.

String.Format utilise un StringBuilder intérieurement:

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();
}

Le code ci-dessus est un extrait de mscorlib, donc la question devient "est-ce que StringBuilder.Append() plus rapide que StringBuilder.AppendFormat()"?

Sans analyse comparative, je dirais probablement que l'exemple de code ci-dessus s'exécuterait plus rapidement en utilisant .Append().Mais ce n'est qu'une supposition, essayez d'effectuer une analyse comparative et/ou de profiler les deux pour obtenir une comparaison appropriée.

Ce type, Jerry Dixon, a fait quelques analyses comparatives :

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

Mis à jour:

Malheureusement, le lien ci-dessus est mort depuis.Cependant, il y a toujours une copie sur la Way Back Machine :

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

En fin de compte, cela dépend si votre formatage de chaîne sera appelé de manière répétitive, c'est-à-direvous effectuez un traitement de texte sérieux sur plus de 100 mégaoctets de texte, ou s'il est appelé lorsqu'un utilisateur clique de temps en temps sur un bouton.À moins que vous n'effectuiez un énorme travail de traitement par lots, je m'en tiendrai à String.Format, cela facilite la lisibilité du code.Si vous soupçonnez un goulot d'étranglement en termes de performances, collez un profileur sur votre code et voyez où il se trouve réellement.

Autres conseils

Du Documentation MSDN:

Les performances d'une opération de concaténation pour un objet String ou StringBuilder dépendent de la fréquence à laquelle une allocation de mémoire se produit.Une opération de concaténation de chaînes alloue toujours de la mémoire, tandis qu'une opération de concaténation de chaînes StringBuilder alloue de la mémoire uniquement si le tampon de l'objet StringBuilder est trop petit pour accueillir les nouvelles données.Par conséquent, la classe String est préférable pour une opération de concaténation si un nombre fixe d’objets String sont concaténés.Dans ce cas, les opérations de concaténation individuelles pourraient même être combinées en une seule opération par le compilateur.Un objet StringBuilder est préférable pour une opération de concaténation si un nombre arbitraire de chaînes sont concaténées ;par exemple, si une boucle concatène un nombre aléatoire de chaînes d'entrée utilisateur.

J'ai effectué quelques tests de performances rapides, et pour 100 000 opérations réparties en moyenne sur 10 exécutions, la première méthode (String Builder) prend presque la moitié du temps de la seconde (String Format).

Donc, si cela n’est pas fréquent, cela n’a pas d’importance.Mais s’il s’agit d’une opération courante, vous souhaiterez peut-être utiliser la première méthode.

je m'attendrais Chaîne.Format pour être plus lent - il doit analyser la chaîne et alors concaténer le.

Quelques remarques :

  • Format est la voie à suivre pour les chaînes visibles par l'utilisateur dans les applications professionnelles ;cela évite les bugs de localisation
  • Si vous connaissez à l'avance la longueur de la chaîne résultante, utilisez la Générateur de chaînes (Int32) constructeur pour prédéfinir la capacité

Ne serait-ce que parce que string.Format ne fait pas exactement ce que vous pourriez penser, voici une reprise des tests 6 ans plus tard sur Net45.

Concat est toujours le plus rapide mais en réalité c'est moins de 30% de différence.StringBuilder et Format diffèrent d’à peine 5 à 10 %.J'ai obtenu des variations de 20 % en exécutant les tests à plusieurs reprises.

Millisecondes, un million d'itérations :

  • Enchaînement:367
  • Nouveau stringBuilder pour chaque clé :452
  • StringBuilder mis en cache :419
  • chaîne.Format :475

La leçon que je retiens est que la différence de performances est insignifiante et ne devrait donc pas vous empêcher d'écrire le code lisible le plus simple possible.Ce qui pour mon argent est souvent mais pas toujours 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);

Je pense que dans la plupart des cas comme celui-ci, la clarté, et non l’efficacité, devrait être votre plus grande préoccupation.À moins que vous n'écrasiez des tonnes de cordes ou que vous construisiez quelque chose pour un appareil mobile moins puissant, cela n'aura probablement pas d'impact majeur sur votre vitesse de course.

J'ai constaté que, dans les cas où je construis des chaînes de manière assez linéaire, faire des concaténations droites ou utiliser StringBuilder est votre meilleure option.Je suggère cela dans les cas où la majorité de la chaîne que vous construisez est dynamique.Étant donné que très peu de texte est statique, le plus important est qu'il soit clair où chaque morceau de texte dynamique est placé au cas où il aurait besoin d'être mis à jour dans le futur.

D'un autre côté, si vous parlez d'un gros morceau de texte statique contenant deux ou trois variables, même si c'est un peu moins efficace, je pense que la clarté que vous obtenez de string.Format en vaut la peine.Je l'ai utilisé plus tôt cette semaine pour placer un morceau de texte dynamique au centre d'un document de 4 pages.Il sera plus facile de mettre à jour ce gros morceau de texte s'il est en un seul morceau plutôt que de devoir mettre à jour trois morceaux que vous concaténez ensemble.

Utilisations de String.Format StringBuilder en interne... donc logiquement, cela conduit à l'idée qu'il serait un peu moins performant en raison de plus de frais généraux.Cependant, une simple concaténation de chaînes est la méthode la plus rapide pour injecter une chaîne entre deux autres... dans une mesure significative.Cette preuve a été démontrée par Rico Mariani dans son tout premier Performance Quiz, il y a des années.Le simple fait est que les concaténations... lorsque le nombre de parties de chaîne est connu (sans limitation... vous pouvez concaténer mille parties... tant que vous savez qu'il y a toujours 1000 parties)... sont toujours plus rapides que StringBuilder ou String.Format.Ils peuvent être effectués avec une seule allocation de mémoire et une série de copies de mémoire. Ici est la preuve

Et voici le code réel de certaines méthodes String.Concat, qui appellent finalement FillStringChecked qui utilise des pointeurs pour copier la mémoire (extraite via 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);
        }
    }
}

Donc alors:

string what = "cat";
string inthehat = "The " + what + " in the hat!";

Apprécier!

Oh aussi, le plus rapide serait :

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

Cela dépend vraiment.Pour les petites chaînes avec peu de concaténations, il est en fait plus rapide de simplement ajouter les chaînes.

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

Mais pour les chaînes plus grandes (très très grandes chaînes), il est alors plus efficace d'utiliser StringBuilder.

Dans les deux cas ci-dessus, je souhaite injecter une ou plusieurs chaînes au milieu d'une chaîne de modèle prédéfinie.

Dans ce cas, je suggérerais que String.Format est le plus rapide car il est conçu dans ce but précis.

Cela dépend vraiment de votre modèle d'utilisation.
Un benchmark détaillé entre string.Join, string,Concat et string.Format peut être trouvé ici: String.Format ne convient pas à la journalisation intensive

Je suggérerais que non, puisque String.Format n'a pas été conçu pour la concaténation, il a été conçu pour formater la sortie de diverses entrées telles qu'une date.

String s = String.Format("Today is {0:dd-MMM-yyyy}.", DateTime.Today);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top