Warum ist StringBuilder langsamer als StringBuffer?

7

In dieses Beispiel , StringBuffer ist eigentlich schneller als StringBuilder, während ich gegenteilige Ergebnisse erwartet hätte.

Hat das etwas mit Optimierungen zu tun, die vom JIT gemacht werden? Weiß jemand, warum StringBuffer schneller als StringBuilder wäre, obwohl seine Methoden synchronisiert sind?

Hier ist der Code und die Benchmark-Ergebnisse:

%Vor%


Benchmark-Ergebnisse:

%Vor%


UPDATE:

Danke an alle. Warmup war in der Tat das Problem. Sobald ein Warmup-Code hinzugefügt wurde, wurden die Benchmarks folgendermaßen geändert:

%Vor%


YMMV, aber zumindest stimmen die Gesamtquoten mit dem überein, was zu erwarten wäre.

    
Parag 26.10.2012, 07:38
quelle

5 Antworten

20

Ich habe mir Ihren Code angesehen, und der wahrscheinlichste Grund dafür, dass StringBuilder it langsamer erscheint, ist, dass Ihr Benchmark die Auswirkungen von JVM-Aufwärmen nicht richtig berücksichtigt. In diesem Fall:

  • Der JVM-Startup erzeugt eine beträchtliche Menge an Müll, mit dem umgegangen werden muss, und
  • Die JIT-Kompilierung kann während des Laufs teilweise ausgelöst werden.

Einer oder beide können zu der Zeit addiert werden, die für den StringBuilder -Teil Ihres Tests gemessen wurde.

Bitte lesen Sie die Antworten zu dieser Frage für weitere Details: Wie schreibe ich einen korrekten Mikro-Benchmark in Java?

    
Stephen C 26.10.2012, 07:41
quelle
5

In beiden Fällen wird der exakt gleiche Code aus java.lang.AbstractStringBuilder verwendet, und beide Instanzen werden mit derselben Kapazität (16) erstellt.

Der einzige Unterschied ist die Verwendung von synchronized beim ersten Aufruf.

Ich schließe, das ist ein Messartefakt.

StringBuilder:

%Vor%

StringBuffer:

%Vor%

AbstractStringBuilder:

%Vor%

(expandCapacity ist nicht überschrieben)

Dieser Blogbeitrag sagt mehr über:

  • die Schwierigkeit beim Micro-Benchmarking
  • die Tatsache, dass Sie nicht "Ergebnisse" eines Benchmarks posten sollten, ohne ein wenig auf das zu schauen, was Sie gemessen haben (hier die gemeinsame Superklasse)

Beachten Sie, dass die "Langsamkeit" von synchronisiert in der letzten JDK als ein Mythos betrachtet werden kann. Alle Tests, die ich gemacht oder gelesen habe, schlussfolgern, dass es im Allgemeinen keinen Grund gibt, viel Zeit zu verlieren, um die Synchronisationen zu vermeiden.

    
Denys Séguret 26.10.2012 07:44
quelle
2

Wenn Sie diesen Code auf sich selbst ausführen, würden Sie ein unterschiedliches Ergebnis sehen. Manchmal ist StringBuffer schneller und manchmal ist StringBuilder schneller. Der wahrscheinlichste Grund hierfür könnte die Zeit sein, die für JVM warmup benötigt wird, bevor% StringBuffer und StringBuilder wie von @ Stephen angegeben verwendet wird, was bei mehreren Läufen variieren kann.

Dies ist das Ergebnis von 4 Läufen, die ich gemacht habe: -

%Vor%

Natürlich können die genauen Zahlen nicht anhand von nur 4 Ausführungen vorhergesagt werden.

    
Rohit Jain 26.10.2012 07:44
quelle
2

Ich schlage vor

  • bricht jede Schleife in eine separate Methode auf, so dass die Optimierung keine andere beeinflusst.
  • Ignoriere die ersten 10K-Iterationen
  • Führen Sie den Test mindestens 2 Sekunden lang durch.
  • Führen Sie den Test mehrmals durch, um sicherzustellen, dass er reproduzierbar ist.

Wenn Sie Code weniger als 10000 Mal ausführen, löst dies möglicherweise nicht aus, dass der Code als Standardcode -XX:CompileThreshold=10000 kompiliert wird. Ein Teil des Grundes ist es, Statistiken darüber zu sammeln, wie der Code am besten zu optimieren ist. Wenn eine Schleife jedoch die Kompilierung auslöst, wird sie für die ganze Methode ausgelöst, wodurch spätere Schleifen entweder a) besser aussehen, wenn sie kompiliert werden, bevor sie beginnen, b) schlimmer, wenn sie kompiliert werden, ohne Statistiken zu sammeln .

Betrachten Sie den folgenden Code

%Vor%

druckt mit Läufen = 1000

%Vor%

jedoch, wenn Sie die Anzahl der Läufe = 10.000

erhöhen %Vor%

und wenn wir die Läufe auf 100.000 erhöhen, bekomme ich

%Vor%

Hinweis: Die + -Operation hat sich verlangsamt, da die Zeitkomplexität der Schleife O (N ^ 2)

ist     
Peter Lawrey 26.10.2012 08:16
quelle
1

Ich habe deinen Code ein wenig modifiziert und die Aufwärmschleifen hinzugefügt. Meine Beobachtungen sind die meiste Zeit konsistent, dass StringBuilder meistens schneller ist.

Ich benutze die Ubuntu12.04-Box, die virtuell auf Windows 7 läuft und der VM 2 GB RAM zugewiesen hat.

%Vor%

}

Ergebnisse sind:

%Vor%     
Snake Eye 26.10.2012 08:08
quelle