Geschwindigkeitsunterschied für eine einzelne Zeile String-Verkettung

8

Ich bin also davon überzeugt gewesen Das Verwenden des "+" - Operators zum Anhängen von Strings an eine einzelne Zeile war genauso effizient wie das Verwenden eines StringBuilders (und auf jeden Fall viel schöner für die Augen). Heute hatte ich zwar Geschwindigkeitsprobleme mit einem Logger, der Variablen und Strings anhängte, aber er verwendete einen "+" - Operator. Also machte ich einen schnellen Testfall und stellte zu meiner Überraschung fest, dass die Verwendung eines StringBuilders schneller war!

Die Grundlagen sind der Durchschnitt von 20 Läufen für jede Anzahl von Anhängen, mit 4 verschiedenen Methoden (siehe unten).

Ergebnisse, Zeiten (in Millisekunden)

%Vor%

Diagramm des Prozentsatzes Unterschied zum schnellsten Algorithmus.

Ich habe den Byte-Code ausgecheckt. Für jede String-Vergleichsmethode ist das anders.

Hier ist, was ich für die Methoden verwende, und Sie können die gesamte Testklasse hier sehen.

%Vor%

Ich habe es jetzt mit Floats, Ints und Strings versucht. Alle zeigen mehr oder weniger den gleichen Zeitunterschied.

Fragen

  1. Der "+" - Operator wird eindeutig nicht der gleiche Byte-Code, und die Zeit unterscheidet sich sehr von der optimalen. Also was gibt es?
  2. Das Verhalten der Algorithmen zwischen 100 und 10000 Anzahl der Anhänge ist sehr seltsam für mich, also hat jemand eine Erklärung?
greedybuddha 23.05.2013, 19:40
quelle

2 Antworten

3

Die Java-Sprachspezifikation legt nicht fest, wie die Verkettung von Zeichenfolgen ausgeführt wird, aber ich bezweifle, dass Ihr Compiler etwas anderes tut als:

%Vor%

Sie können "javap -c ..." verwenden, um Ihre Klassendatei zu dekompilieren und zu verifizieren.

Wenn Sie einen signifikanten und sich wiederholenden Unterschied in der Laufzeit zwischen Ihren Methoden messen, würde ich viel eher davon ausgehen, dass der Garbage Collector zu unterschiedlichen Zeiten ausgeführt wird, als dass es tatsächlich einen signifikanten Leistungsunterschied gibt. Das Erstellen des StringBuilder s mit unterschiedlichen Anfangskapazitäten kann natürlich einige Auswirkungen haben, sollte jedoch im Vergleich zu dem Aufwand, der z. Formatiere die Floats.

    
jarnbjo 23.05.2013, 20:31
quelle
4

Ich habe zwei Dinge über deinen Testfall nicht gemocht. Zuerst haben Sie alle Tests innerhalb desselben Prozesses ausgeführt. Im Umgang mit "groß" (mehrdeutig, ich weiß), aber wenn Sie sich mit etwas beschäftigen, wo Ihr Prozess mit der Erinnerung interagiert, ist Ihre Hauptsorge, sollten Sie immer Benchmark in einem separaten Lauf. Allein die Tatsache, dass wir den Müll gesammelt haben, kann die Ergebnisse früherer Läufe beeinflussen. Die Art und Weise, wie Sie Ihre Ergebnisse einkalkuliert haben, verwirrte mich irgendwie. Was ich getan habe, war, dass ich jeden einzelnen Run genommen habe und eine Null von der Anzahl der Male, die ich es gefahren bin. Ich lasse es auch für eine Reihe von "Wiederholungen" laufen, wobei jede Wiederholung zeitlich abgestimmt wird. Dann ausgedruckt die Anzahl der Millisekunden, die jeder Lauf dauerte. Hier ist mein Code:

%Vor%

Jetzt meine Ergebnisse:

%Vor%

Wie Sie aus meinen Ergebnissen sehen können, wird bei Werten, die sich in den "mittleren Bereichen" befinden, jeder aufeinanderfolgende Wiederholer schneller. Dies wird, glaube ich, dadurch erklärt, dass die JVM rennt und sich den Speicher aneignet, den sie benötigt. Wenn die "Größe" ansteigt, darf dieser Effekt nicht mehr übernommen werden, da zu viel Speicher für den Garbage Collector vorhanden ist und der Prozess sich wieder einklinken kann. Wenn Sie einen "repetitiven" Benchmark wie diesen verwenden, bei dem der Großteil Ihres Prozesses in niedrigeren Cache-Ebenen statt im RAM-Speicher vorhanden sein kann, ist Ihr Prozess für Branch-Prädiktoren noch empfindlicher. Diese sind sehr schlau und würden herausfinden, was Ihr Prozess macht, und ich kann mir vorstellen, dass die JVM dies verstärkt. Dies hilft auch zu erklären, warum die Werte in anfänglichen Schleifen langsamer sind und warum die Art, wie Sie sich dem Benchmarking nähern, eine schlechte Lösung ist. Deshalb denke ich, dass Ihre Ergebnisse für Werte, die nicht "groß" sind, verzerrt sind und seltsam erscheinen. Wenn der "memory footprint" Ihres Benchmarks erhöht wird, hat diese Verzweigungsvorhersage weniger Auswirkungen (prozentual) als die großen Strings, die Sie anhängen, werden im RAM verschoben.

Vereinfachte Schlussfolgerung: Ihre Ergebnisse für "große" Läufe sind einigermaßen gültig und scheinen mir ähnlich zu sein (obwohl ich immer noch nicht vollständig verstehe, wie Sie Ihre Ergebnisse erhalten haben, aber die Prozentsätze scheinen im Vergleich gut zu sein). Ihre Ergebnisse für kleinere Läufe sind jedoch aufgrund der Art Ihres Tests nicht gültig.

    
ChrisCM 23.05.2013 20:33
quelle

Tags und Links