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);
Était-ce utile?

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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top