J'ai une question relative aux performances concernant l'utilisation de StringBuilder. Dans une très longue boucle, je manipule un StringBuilder
et le passe à une autre méthode comme celle-ci:
for (loop condition) {
StringBuilder sb = new StringBuilder();
sb.append("some string");
. . .
sb.append(anotherString);
. . .
passToMethod(sb.toString());
}
L'instanciation StringBuilder
à chaque cycle de boucle est-elle une bonne solution? Et est-il préférable d'appeler une suppression, comme suit?
StringBuilder sb = new StringBuilder();
for (loop condition) {
sb.delete(0, sb.length);
sb.append("some string");
. . .
sb.append(anotherString);
. . .
passToMethod(sb.toString());
}
java
performance
string
stringbuilder
Pier Luigi
la source
la source
Dans la philosophie de l'écriture de code solide, il est toujours préférable de mettre votre StringBuilder dans votre boucle. De cette façon, il ne sort pas du code pour lequel il est destiné.
Deuxièmement, la plus grande amélioration de StringBuilder vient de lui donner une taille initiale pour éviter qu'elle ne s'agrandisse pendant que la boucle tourne
la source
Toujours plus vite:
Dans la philosophie de l'écriture de code solide, le fonctionnement interne de la méthode doit être caché des objets qui utilisent la méthode. Ainsi, il ne fait aucune différence du point de vue du système si vous redéclarez le StringBuilder dans la boucle ou en dehors de la boucle. Comme le déclarer en dehors de la boucle est plus rapide et que cela ne complique pas la lecture du code, réutilisez l'objet plutôt que de le réinstituer.
Même si le code était plus compliqué et que vous saviez avec certitude que l'instanciation d'objet était le goulot d'étranglement, commentez-le.
Trois courses avec cette réponse:
Trois courses avec l'autre réponse:
Bien que cela ne soit pas significatif, le réglage de la
StringBuilder
taille initiale du tampon donnera un petit gain.la source
D'accord, je comprends maintenant ce qui se passe, et cela a du sens.
J'avais l'impression que
toString
je venais de passer le sous-jacentchar[]
dans un constructeur String qui ne prenait pas de copie. Une copie serait alors faite lors de la prochaine opération "d'écriture" (par exempledelete
). Je pense que c'était le casStringBuffer
dans certaines versions précédentes. (Ce n'est pas le cas maintenant.) Mais non -toString
passe simplement le tableau (ainsi que l'index et la longueur) auString
constructeur public qui prend une copie.Donc, dans le cas "réutiliser le
StringBuilder
", nous créons véritablement une copie des données par chaîne, en utilisant le même tableau de caractères dans le tampon tout le temps. Évidemment, créer un nouveau àStringBuilder
chaque fois crée un nouveau tampon sous-jacent - puis ce tampon est copié (quelque peu inutile, dans notre cas particulier, mais fait pour des raisons de sécurité) lors de la création d'une nouvelle chaîne.Tout cela conduit à ce que la deuxième version soit définitivement plus efficace - mais en même temps, je dirais toujours que c'est un code plus laid.
la source
Comme je ne pense pas que cela ait encore été souligné, en raison des optimisations intégrées au compilateur Sun Java, qui crée automatiquement StringBuilders (StringBuffers pré-J2SE 5.0) lorsqu'il voit des concaténations de chaînes, le premier exemple de la question est équivalent à:
Ce qui est plus lisible, l'OMI, la meilleure approche. Vos tentatives d'optimisation peuvent entraîner des gains sur certaines plates-formes, mais potentiellement en perdre d'autres.
Mais si vous rencontrez vraiment des problèmes de performances, alors optimisez-les. Je commencerais par spécifier explicitement la taille de la mémoire tampon du StringBuilder, par Jon Skeet.
la source
La JVM moderne est vraiment intelligente pour ce genre de choses. Je ne le devinerais pas et ne ferais pas quelque chose de piraté qui soit moins maintenable / lisible ... à moins que vous ne fassiez de bons repères avec des données de production qui valident une amélioration de performance non triviale (et la documentent;)
la source
Sur la base de mon expérience avec le développement de logiciels sur Windows, je dirais que l'effacement de StringBuilder pendant votre boucle a de meilleures performances que l'instanciation d'un StringBuilder à chaque itération. L'effacer libère cette mémoire pour être écrasée immédiatement sans allocation supplémentaire requise. Je ne suis pas assez familier avec le ramasse-miettes Java, mais je pense que la libération et aucune réallocation (à moins que votre prochaine chaîne ne développe le StringBuilder) est plus bénéfique que l'instanciation.
(Mon opinion est contraire à ce que tout le monde suggère. Hmm. Il est temps de le comparer.)
la source
La raison pour laquelle faire un 'setLength' ou 'delete' améliore les performances est principalement le code 'apprenant' la bonne taille du tampon, et moins pour faire l'allocation de mémoire. En général, je recommande de laisser le compilateur faire les optimisations de chaîne . Cependant, si les performances sont critiques, je pré-calcule souvent la taille attendue du tampon. La taille par défaut de StringBuilder est de 16 caractères. Si vous vous développez au-delà de cela, alors il doit être redimensionné. Le redimensionnement est l'endroit où les performances se perdent. Voici un autre mini-benchmark qui illustre cela:
Les résultats montrent que la réutilisation de l'objet est environ 10% plus rapide que la création d'un tampon de la taille attendue.
la source
LOL, la première fois que j'ai vu des gens comparer les performances en combinant une chaîne dans StringBuilder. Pour cela, si vous utilisez "+", cela pourrait être encore plus rapide; D. Le but d'utiliser StringBuilder pour accélérer la récupération de la chaîne entière en tant que concept de «localité».
Dans le scénario où vous récupérez fréquemment une valeur de chaîne qui ne nécessite pas de modification fréquente, Stringbuilder permet de meilleures performances de récupération de chaîne. Et c'est le but de l'utilisation de Stringbuilder .. s'il vous plaît ne pas MIS-Test le but principal de cela.
Certaines personnes ont dit, l'avion vole plus vite. Par conséquent, je l'ai testé avec mon vélo et j'ai constaté que l'avion se déplaçait plus lentement. Savez-vous comment je règle les paramètres de l'expérience; D
la source
Pas beaucoup plus rapide, mais d'après mes tests, il montre en moyenne quelques millis de temps plus rapide en utilisant 1.6.0_45 64 bits: utilisez StringBuilder.setLength (0) au lieu de StringBuilder.delete ():
la source
Le moyen le plus rapide est d'utiliser "setLength". Cela n'impliquera pas l'opération de copie. La façon de créer un nouveau StringBuilder devrait être complètement abandonnée . La lenteur de StringBuilder.delete (int start, int end) est due au fait qu'il recopiera le tableau pour la partie redimensionnée.
Après cela, StringBuilder.delete () mettra à jour StringBuilder.count à la nouvelle taille. Alors que StringBuilder.setLength () simplifie simplement la mise à jour de StringBuilder.count à la nouvelle taille.
la source
Le premier est meilleur pour les humains. Si la seconde est un peu plus rapide sur certaines versions de certaines JVM, et alors?
Si les performances sont si critiques, contournez StringBuilder et écrivez les vôtres. Si vous êtes un bon programmeur et que vous tenez compte de la façon dont votre application utilise cette fonction, vous devriez pouvoir la rendre encore plus rapide. Digne d'intérêt? Probablement pas.
Pourquoi cette question est-elle considérée comme "question préférée"? Parce que l'optimisation des performances est tellement amusante, qu'elle soit pratique ou non.
la source
Je ne pense pas qu'il soit logique d'essayer d'optimiser les performances comme ça. Aujourd'hui (2019), les deux états fonctionnent environ 11sec pour 100.000.000 de boucles sur mon ordinateur portable I5:
==> 11000 ms (déclaration à l'intérieur de la boucle) et 8236 ms (déclaration à l'extérieur de la boucle)
Même si je suis en train d'exécuter des programmes de dédoublement d'adresses avec quelques milliards de boucles, une différence de 2 sec. pour 100 millions de boucles ne fait aucune différence car ces programmes fonctionnent pendant des heures. Sachez également que les choses sont différentes si vous n'avez qu'une seule instruction d'ajout:
==> 3416 ms (boucle intérieure), 3555 ms (boucle extérieure) La première instruction qui crée le StringBuilder dans la boucle est plus rapide dans ce cas. Et, si vous changez l'ordre d'exécution, c'est beaucoup plus rapide:
==> 3638 msec (boucle extérieure), 2908 msec (boucle intérieure)
Cordialement, Ulrich
la source
Déclarez une fois et attribuez-les à chaque fois. C'est un concept plus pragmatique et réutilisable qu'une optimisation.
la source