StringBuilder.Append Vs StringBuilder.AppendFormat
-
22-08-2019 - |
Question
Je me demandais StringBuilder et j'ai une question que j'espérais la communauté serait en mesure d'expliquer.
Oublions à peu près la lisibilité du code, ce qui est de ces 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);
La solution
Il est impossible de dire, ne pas connaître la taille de string1
et string2
.
Avec l'appel à AppendFormat
, il preallocate le tampon une fois donné 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 tampon. Pour les chaînes très grandes, ce sera avantageux par rapport à des appels distincts à Append
qui pourrait provoquer le tampon d'étendre à plusieurs reprises.
Cependant, les trois appels à Append
pourraient ou pourraient ne pas déclencher la croissance de la mémoire tampon et que l'enregistrement est effectué chaque appel. Si les chaînes sont assez petites et aucune expansion de la mémoire tampon est déclenchée, alors il sera plus rapide que l'appel à AppendFormat
car il ne devra pas analyser la chaîne de format pour savoir où faire les remplacements.
Plus de données est nécessaire pour une réponse définitive
Il convient de noter qu'il ya peu de discussion sur l'utilisation statique méthode Concat
sur la classe String
( réponse de Jon à l'aide AppendWithCapacity
m'a rappelé ce sujet). Ses résultats des tests montrent que, pour être le meilleur des cas (en supposant que vous n'avez pas à profiter de spécificateur de format spécifique). String.Concat
fait la même chose en ce qu 'il prédéterminer la longueur des chaînes à concaténer et préallouer la mémoire tampon (avec une charge légèrement plus due à des constructions en boucle à travers les paramètres). Il est la performance va être comparable à la méthode AppendWithCapacity
de Jon.
Ou, juste l'opérateur d'addition simple, car il compile un appel à String.Concat
de toute façon, avec la mise en garde 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 mettre en place un code de test
Vous devez exécuter vos cas de test dans séparés fonctionne (ou au moins, effectuer un GC entre la mesure de test séparé fonctionne). La raison est que si vous dites, 1.000.000 courses, la création d'un nouveau
Autres conseils
casperOne est correcte . Une fois que vous atteignez un certain seuil, la méthode Append()
devient plus lent que AppendFormat()
. Voici les différentes longueurs et les tiques de 100.000 itérations écoulées de chaque méthode:
Longueur: 1
Append() - 50900
AppendFormat() - 126826
Longueur: 1000
Append() - 1241938
AppendFormat() - 1337396
Longueur: 10000
Append() - 12482051
AppendFormat() - 12740862
Longueur: 20.000
Append() - 61029875
AppendFormat() - 60483914
Lorsque les chaînes d'une longueur près de 20 000 sont introduits, la fonction sera AppendFormat()
légèrement surperformer Append()
.
Pourquoi cela? Voir la réponse casperOne .
Modifier
Je rediffusé chaque test individuellement dans la configuration de sortie et mis à jour les résultats.
casperOne est tout à fait exact que cela dépend de la données. Cependant, supposons que vous écrivez cela comme une bibliothèque de classes pour la 3e parties à consommer - qui utiliseriez-vous
Une option serait d'obtenir le meilleur des deux mondes - Calculez combien les données que vous allez réellement avoir à ajouter, et ensuite utiliser StringBuilder.EnsureCapacity pour vous assurer que nous avons seulement besoin d'un seul Redimensionner tampon.
Si je ne aussi dérangeait bien, j'utiliser Append
x3 - il semble « plus probable » pour être plus rapide, comme l'analyse syntaxique des jetons de format de chaîne à chaque appel est de faire le travail clairement .
Notez que je l'ai demandé à l'équipe BCL pour une sorte de « cache formatter » que l'on pourrait créer à l'aide d'une chaîne de format, puis réutiliser à plusieurs reprises. Il est fou que le cadre doit analyser la chaîne de format à chaque fois qu'il est utilisé.
EDIT: D'accord, j'ai édité le code de John un peu de flexibilité et a ajouté un « AppendWithCapacity » qui fonctionne la capacité nécessaire en premier. 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 je 100000. (C'était juste pour obtenir des temps de fonctionnement sensibles.) Toutes les heures sont Millis.
Malheureusement, les tables ne fonctionnent pas vraiment dans SO. Les longueurs étaient de 1, 1000, 10000, 20000
Temps:
- Append: 162, 475, 7997, 17970
- AppendFormat: 392, 499, 8541, 18993
- AppendWithCapacity: 139, 189, 1558, 3085
Alors comme il est arrivé, je ne ai jamais vu AppendFormat battre Append -. Mais je a fait voir AppendWithCapacity gagner par une marge très importante
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 en la plupart des cas parce qu'il ya beaucoup à cette méthode des surcharges qui permettent au compilateur d'appeler la bonne méthode. Puisque vous utilisez Strings
le StringBuilder
peut utiliser la surcharge de String
pour Append
.
AppendFormat
prend un String
et puis un Object[]
ce qui signifie que le format devra être analysé et chaque Object
dans le tableau devront être ToString'd
avant de pouvoir être ajouté au tableau interne StringBuilder's
.
Remarque: Pour point de casperOne - il est difficile de donner une réponse exacte, sans plus de données
. StringBuilder
a également en cascade ajouter ses: Append()
retourne 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 un micro-optimisation).
profil de cours pour en être sûr, dans chaque cas.
Cela dit, je pense qu'en général, il sera le premier parce que vous n'êtes pas à plusieurs reprises la chaîne analysez de format.
Cependant, la différence serait très faible. Au point que vous devriez vraiment envisager d'utiliser la plupart des cas AppendFormat
dans toute façon.
Je suppose que ce fut l'appel qui a fait le moins de travail. Append concaténer juste des chaînes, où AppendFormat fait des substitutions de chaîne. Bien sûr, ces jours-ci, on ne peut jamais dire ...
1 devrait être plus rapide parce qu'il est tout simplement les chaînes annexant alors que 2 doit créer une chaîne basée sur un format, puis ajoutez la chaîne. Donc, il y a une étape supplémentaire là-dedans.
Le plus rapide est 1 dans votre cas mais il n'est pas une comparaison équitable. Vous devriez demander StringBuilder.AppendFormat () vs StringBuilder.Append (string.Format ()) -. Où le premier est plus rapide grâce à un travail interne avec tableau de caractères
Votre deuxième option est plus lisible si.