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.