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);
35
demandé sur Samuel 2009-04-02 20:29:02

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.

39
répondu casperOne 2017-05-23 11:53:53

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.

21
répondu John Rasch 2017-05-23 12:25:12

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);
    }
}
12
répondu Jon Skeet 2017-05-23 12:31:52

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.

5
répondu Andrew Hare 2009-04-02 16:49:16

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).

2
répondu Tommy Carlier 2011-08-20 20:42:24

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.

1
répondu Joel Coehoorn 2011-08-20 20:42:33

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...

0
répondu Paul W Homer 2009-04-02 16:31:30

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.

0
répondu Micah 2009-04-02 16:31:50

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.

0
répondu Miha Markic 2009-04-02 16:35:34