StringBuilder.Ajouter Vs StringBuilder.AppendFormat
je me demandais à propos de StringBuilder et j'ai une question que j'espérais que la communauté serait en mesure d'expliquer.
oublions la lisibilité du code, lequel est plus rapide et pourquoi?
StringBuilder.Append
:
StringBuilder sb = new StringBuilder();
sb.Append(string1);
sb.Append("----");
sb.Append(string2);
StringBuilder.AppendFormat
:
StringBuilder sb = new StringBuilder();
sb.AppendFormat("{0}----{1}",string1,string2);
9 réponses
impossible à dire, ne connaissant pas la taille de string1
et string2
.
avec l'appel à AppendFormat
, il va préallouer le buffer juste une fois en donnant la longueur de la chaîne de format et les chaînes qui seront insérées, puis concaténer tout et l'insérer dans le buffer. Pour les très grandes cordes, ceci sera avantageux par rapport aux appels séparés à Append
qui pourraient faites que le tampon se dilate plusieurs fois.
cependant, les trois appels à Append
pourraient ou non déclencher la croissance du tampon et ce contrôle est effectué à chaque appel. Si les chaînes sont suffisamment petites et qu'aucune extension de tampon n'est déclenchée, alors ce sera plus rapide que l'appel à AppendFormat
parce qu'il n'aura pas à analyser la chaîne de format pour trouver où effectuer les remplacements.
plus de données sont nécessaires pour une réponse
il est à noter qu'il y a peu de discussion sur l'utilisation de la méthode statique Concat
sur la String
classe ( réponse de Jon en utilisant AppendWithCapacity
m'a rappelé de ceci). Ses résultats de test montrent que pour être le meilleur cas (en supposant que vous n'avez pas à tirer parti de spécificateur de format spécifique). String.Concat
fait la même chose en ce qu'il déterminera la longueur des cordes à concaténer et préallouer le tampon (avec un peu plus de surélévation en raison de constructions en boucle à travers les paramètres). Sa performance sera comparable à celle de la méthode AppendWithCapacity
de Jon.
Ou, simplement de la plaine de l'opérateur d'addition, puisqu'il compile un appel à String.Concat
de toute façon, avec la réserve que tous les ajouts sont dans la même expression:
// One call to String.Concat.
string result = a + b + c;
PAS
// Two calls to String.Concat.
string result = a + b;
result = result + c;
pour tous ceux qui mettent en place le code de test
vous devez exécuter vos cas de test dans essais séparés (ou au moins, effectuer un CG entre la mesure des essais séparés). La raison en est que si vous dites, 1.000.000 exécute, créant un nouveau StringBuilder
dans chaque itération de la boucle pour un test, et puis vous exécutez le test suivant qui boucle le le même nombre de fois, créant des instances supplémentaires 1,000,000 StringBuilder
, le GC interviendra plus que probablement pendant le deuxième essai et en entravera le déroulement.
casperOne est correct . Une fois que vous atteignez un certain seuil, la méthode Append()
devient plus lente que AppendFormat()
. Voici les différentes longueurs et les tiques de 100.000 itérations de chaque méthode:
Longueur: 1
Append() - 50900
AppendFormat() - 126826
Longueur: 1000
Append() - 1241938
AppendFormat() - 1337396
Longueur: 10,000
Append() - 12482051
AppendFormat() - 12740862
Longueur: 20,000
Append() - 61029875
AppendFormat() - 60483914
quand les cordes d'une longueur proche de 20.000 sont introduites, la fonction AppendFormat()
sera légèrement surperform Append()
.
pourquoi cela arrive-t-il? Voir réponse de casperOne .
Edit:
I reran chaque test individuellement sous la configuration de libération et mis à jour les résultats.
casperOne est tout à fait exact qu'il dépend des données . Cependant, supposons que vous écriviez ceci comme une bibliothèque de classe pour les tiers à consommer - qui utiliseriez-vous?
une option serait d'obtenir le meilleur des deux mondes - calculer combien de données vous allez réellement avoir à ajouter, puis utiliser StringBuilder.EnsureCapacity pour s'assurer que nous n'avons besoin que d'un seul tampon redimensionné.
si Je n'étais pas trop gêné cependant, j'utiliserais Append
x3 - il semble "plus probable" d'être plus rapide, comme l'analyse du format de chaîne de caractères sur chaque appel est clairement make-work.
notez que j'ai demandé à L'équipe BCL une sorte de "formatteur en cache" que nous pourrions créer en utilisant une chaîne de format puis réutiliser à plusieurs reprises. C'est fou que le cadre doit analyser la chaîne de format à chaque fois qu'il est utilisé.
EDIT: Bon, j'ai édité John code un peu pour la flexibilité et a ajouté un "Appendicwithcapacity" qui vient de travailler sur la capacité nécessaire d'abord. Voici les résultats pour les différentes longueurs - pour la longueur 1 j'ai utilisé 1.000.000 itérations; pour toutes les autres longueurs j'ai utilisé 100.000. (Ceci était juste pour obtenir des temps de course raisonnables.) Toutes les heures sont en millis.
malheureusement les tables ne fonctionnent pas vraiment ainsi. Les longueurs étaient 1, 1000, 10000, 20000
Times:
- annexe: 162, 475, 7997, 17970
- AppendFormat: 392, 499, 8541, 18993
- Appendicavec capacité: 139, 189, 1558, 3085
ainsi comme il s'est produit, je n'ai jamais vu AppendFormat beat Ajouter - mais je did voir Appendicwithcapacity gagner par une marge très substantielle.
voici le code complet:
using System;
using System.Diagnostics;
using System.Text;
public class StringBuilderTest
{
static void Append(string string1, string string2)
{
StringBuilder sb = new StringBuilder();
sb.Append(string1);
sb.Append("----");
sb.Append(string2);
}
static void AppendWithCapacity(string string1, string string2)
{
int capacity = string1.Length + string2.Length + 4;
StringBuilder sb = new StringBuilder(capacity);
sb.Append(string1);
sb.Append("----");
sb.Append(string2);
}
static void AppendFormat(string string1, string string2)
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("{0}----{1}", string1, string2);
}
static void Main(string[] args)
{
int size = int.Parse(args[0]);
int iterations = int.Parse(args[1]);
string method = args[2];
Action<string,string> action;
switch (method)
{
case "Append": action = Append; break;
case "AppendWithCapacity": action = AppendWithCapacity; break;
case "AppendFormat": action = AppendFormat; break;
default: throw new ArgumentException();
}
string string1 = new string('x', size);
string string2 = new string('y', size);
// Make sure it's JITted
action(string1, string2);
GC.Collect();
Stopwatch sw = Stopwatch.StartNew();
for (int i=0; i < iterations; i++)
{
action(string1, string2);
}
sw.Stop();
Console.WriteLine("Time: {0}ms", (int) sw.ElapsedMilliseconds);
}
}
Append
sera plus rapide dans la plupart des cas parce qu'il y a beaucoup de surcharges à cette méthode qui permettent au compilateur d'appeler la bonne méthode. Puisque vous utilisez Strings
le StringBuilder
peut utiliser le String
surcharge pour Append
.
AppendFormat
prend un String
puis un Object[]
, ce qui signifie que le format devra être analysé et chaque Object
dans le tableau devra être ToString'd
avant d'être ajoutée à la StringBuilder's
tableau interne.
Note: à la pointe de casperOne - il est difficile de donner une réponse exacte sans plus de données.
StringBuilder
a aussi des appendices en cascade: Append()
renvoie le StringBuilder
lui-même, de sorte que vous pouvez écrire votre code comme ceci:
StringBuilder sb = new StringBuilder();
sb.Append(string1)
.Append("----")
.Append(string2);
propre, et il génère moins de IL-code (bien que ce soit vraiment une micro-optimisation).
de profil de cours pour en être sûr dans chaque cas.
cela dit, je pense qu'en général, il sera l'ex parce que vous n'êtes pas à plusieurs reprises l'analyse de la chaîne de format.
Cependant, la différence serait très faible. Au point que vous devriez vraiment envisager d'utiliser AppendFormat
dans la plupart des cas de toute façon.
je suppose que c'était l'appel que fait le moins de travail. Append ne fait que concaténer les cordes, où AppendFormat fait des substitutions de cordes. Bien sûr, ces jours, vous ne pouvez jamais dire...
1 devrait être plus rapide car il s'agit simplement d'ajouter les chaînes alors que 2 doit créer une chaîne basée sur un format et ensuite ajouter la chaîne. Donc, il y a une étape supplémentaire.
plus rapide est 1 dans votre cas mais ce n'est pas une comparaison équitable. Vous devriez demander à StringBuilder.AppendFormat () vs StringBuilder.Append(string.Format ()) - où le premier est plus rapide en raison du travail interne avec le tableau de char.
Votre deuxième option est plus lisible.