Wie schreibt (und führt) man einen korrekten Mikro-Benchmark in Java aus?
Ich suche nach Codebeispielen und Kommentaren, die verschiedene Dinge veranschaulichen, über die ich nachdenken sollte.
Beispiel: Sollte der Benchmark Zeit / Iteration oder Iterationen / Zeit messen und warum?
Verwandte: Ist Stoppuhr-Benchmarking akzeptabel?
java
jvm
benchmarking
jvm-hotspot
microbenchmark
John Nilsson
quelle
quelle
Antworten:
Tipps zum Schreiben von Mikro-Benchmarks von den Entwicklern von Java HotSpot :
Regel 0: Lesen Sie ein seriöses Papier über JVMs und Mikro-Benchmarking. Ein guter ist Brian Goetz, 2005 . Erwarten Sie nicht zu viel von Mikro-Benchmarks. Sie messen nur einen begrenzten Bereich von JVM-Leistungsmerkmalen.
Regel 1: Schließen Sie immer eine Aufwärmphase ein, in der Ihr Testkernel vollständig ausgeführt wird, sodass alle Initialisierungen und Kompilierungen vor der Timing-Phase (n) ausgelöst werden. (In der Aufwärmphase sind weniger Iterationen in Ordnung. Als Faustregel gelten mehrere Zehntausend Iterationen der inneren Schleife.)
Regel 2: Immer lief mit
-XX:+PrintCompilation
,-verbose:gc
etc., so dass Sie überprüfen können , dass der Compiler und andere Teile der JVM sind nicht unerwartet Arbeit während Taktphase zu tun.Regel 2.1: Drucken Sie Nachrichten zu Beginn und am Ende der Timing- und Aufwärmphase, damit Sie überprüfen können, ob während der Timing-Phase keine Ausgabe von Regel 2 erfolgt.
Regel 3: Beachten Sie den Unterschied zwischen
-client
und-server
, OSR und regelmäßigen Zusammenstellungen. Das-XX:+PrintCompilation
Flag meldet OSR-Kompilierungen mit einem At-Zeichen, um den nicht anfänglichen Einstiegspunkt zu kennzeichnen, zum Beispiel :Trouble$1::run @ 2 (41 bytes)
. Bevorzugen Sie Server gegenüber Client und regulär gegenüber OSR, wenn Sie die beste Leistung erzielen möchten.Regel 4: Beachten Sie die Initialisierungseffekte. Drucken Sie während Ihrer Timing-Phase nicht zum ersten Mal, da beim Drucken Klassen geladen und initialisiert werden. Laden Sie keine neuen Klassen außerhalb der Aufwärmphase (oder der letzten Berichtsphase), es sei denn, Sie testen das Laden von Klassen speziell (und laden in diesem Fall nur die Testklassen). Regel 2 ist Ihre erste Verteidigungslinie gegen solche Effekte.
Regel 5: Achten Sie auf Deoptimierungs- und Neukompilierungseffekte. Nehmen Sie zum ersten Mal in der Timing-Phase keinen Codepfad, da der Compiler den Code möglicherweise verschmutzen und neu kompilieren kann, basierend auf einer früheren optimistischen Annahme, dass der Pfad überhaupt nicht verwendet werden würde. Regel 2 ist Ihre erste Verteidigungslinie gegen solche Effekte.
Regel 6: Verwenden Sie geeignete Tools, um die Gedanken des Compilers zu lesen, und lassen Sie sich von dem von ihm erzeugten Code überraschen. Überprüfen Sie den Code selbst, bevor Sie Theorien darüber aufstellen, was etwas schneller oder langsamer macht.
Regel 7: Reduzieren Sie das Rauschen bei Ihren Messungen. Führen Sie Ihren Benchmark auf einem leisen Computer aus und führen Sie ihn mehrmals aus, wobei Sie Ausreißer verwerfen. Verwenden Sie
-Xbatch
diese Option, um den Compiler mit der Anwendung zu serialisieren, und erwägen Sie die Einstellung-XX:CICompilerCount=1
, um zu verhindern, dass der Compiler parallel zu sich selbst ausgeführt wird. Versuchen Sie nach besten Kräften, den GC-Overhead zu reduzieren, setzen SieXmx
(groß genug) gleichXms
und verwenden Sie ihn,UseEpsilonGC
falls verfügbar.Regel 8: Verwenden Sie eine Bibliothek für Ihren Benchmark, da diese wahrscheinlich effizienter ist und bereits zu diesem alleinigen Zweck getestet wurde. Wie JMH , Caliper oder Bill und Pauls ausgezeichnete UCSD-Benchmarks für Java .
quelle
System.nanoTime()
nicht garantiert werden kann alsSystem.currentTimeMillis()
. Es ist nur garantiert, dass es mindestens genauso genau ist. Es ist jedoch normalerweise wesentlich genauer.System.nanoTime()
stattdessen verwenden muss,System.currentTimeMillis()
ist, dass der erstere garantiert monoton zunimmt. Das Subtrahieren der zurückgegebenen Werte durch zweicurrentTimeMillis
Aufrufe kann tatsächlich zu negativen Ergebnissen führen, möglicherweise weil die Systemzeit von einem NTP-Daemon angepasst wurde.Ich weiß, dass diese Frage als beantwortet markiert wurde, aber ich wollte zwei Bibliotheken erwähnen, die uns beim Schreiben von Mikro-Benchmarks helfen
Bremssattel von Google
Erste Schritte Tutorials
JMH von OpenJDK
Erste Schritte Tutorials
quelle
Wichtige Dinge für Java-Benchmarks sind:
System.gc()
zwischen Iterationen aufrufen , es ist jedoch eine gute Idee, sie zwischen Tests auszuführen, damit jeder Test hoffentlich einen "sauberen" Speicherplatz zum Arbeiten erhält. (Ja,gc()
ist eher ein Hinweis als eine Garantie, aber es ist sehr wahrscheinlich, dass sich meiner Erfahrung nach wirklich Müll ansammelt.)Ich bin gerade dabei, über das Design eines Benchmarking-Frameworks in .NET zu bloggen. Ich habe ein bekam Paar von früheren Beiträgen der in der Lage sein, Ihnen einige Ideen zu geben - nicht alles wird angemessen sein, natürlich, aber ein Teil davon sein kann.
quelle
gc
immer unbenutzter Speicherplatz frei wird.System.gc()
, wie schlagen Sie vor, die Speicherbereinigung in einem Test aufgrund von Objekten zu minimieren, die in früheren Tests erstellt wurden? Ich bin pragmatisch, nicht dogmatisch.jmh ist eine neue Erweiterung von OpenJDK und wurde von einigen Performance-Ingenieuren von Oracle geschrieben. Auf jeden Fall einen Blick wert.
Sehr interessante Informationen, die in den Kommentaren der Beispieltests vergraben sind .
Siehe auch:
quelle
Es hängt davon ab, was Sie testen möchten.
Wenn Sie an Latenz interessiert sind , verwenden Sie Zeit / Iteration und wenn Sie an Durchsatz interessiert sind , verwenden Sie Iterationen / Zeit.
quelle
Wenn Sie versuchen, zwei Algorithmen zu vergleichen, führen Sie jeweils mindestens zwei Benchmarks in abwechselnder Reihenfolge durch. dh:
Ich habe einige bemerkenswerte Unterschiede (manchmal 5-10%) in der Laufzeit des gleichen Algorithmus in verschiedenen Durchgängen festgestellt.
Stellen Sie außerdem sicher, dass n sehr groß ist, damit die Laufzeit jeder Schleife mindestens 10 Sekunden beträgt. Je mehr Iterationen, desto signifikanter die Zahlen in Ihrer Benchmark-Zeit und desto zuverlässiger sind diese Daten.
quelle
Stellen Sie sicher, dass Sie Ergebnisse verwenden, die in Benchmark-Code berechnet werden. Andernfalls kann Ihr Code entfernt werden.
quelle
Es gibt viele mögliche Fallstricke beim Schreiben von Mikro-Benchmarks in Java.
Erstens: Sie müssen mit allen Arten von Ereignissen rechnen, die mehr oder weniger zufällig Zeit benötigen: Speicherbereinigung, Caching-Effekte (des Betriebssystems für Dateien und der CPU für Speicher), E / A usw.
Zweitens: Sie können der Genauigkeit der gemessenen Zeiten für sehr kurze Intervalle nicht vertrauen.
Drittens: Die JVM optimiert Ihren Code während der Ausführung. So werden verschiedene Läufe in derselben JVM-Instanz immer schneller.
Meine Empfehlungen: Lassen Sie Ihren Benchmark einige Sekunden laufen, das ist zuverlässiger als eine Laufzeit über Millisekunden. Erwärmen Sie die JVM (bedeutet, dass der Benchmark mindestens einmal ohne Messung ausgeführt wird, damit die JVM Optimierungen ausführen kann). Führen Sie Ihren Benchmark mehrmals (möglicherweise fünfmal) aus und nehmen Sie den Medianwert. Führen Sie jeden Micro-Benchmark in einer neuen JVM-Instanz aus (fordern Sie für jeden Benchmark neues Java an), da sonst die Optimierungseffekte der JVM die späteren laufenden Tests beeinflussen können. Führen Sie keine Dinge aus, die nicht in der Aufwärmphase ausgeführt werden (da dies das Laden und Neukompilieren von Klassen auslösen könnte).
quelle
Es sollte auch beachtet werden, dass es auch wichtig sein kann, die Ergebnisse des Mikro-Benchmarks zu analysieren, wenn verschiedene Implementierungen verglichen werden. Daher sollte ein Signifikanztest durchgeführt werden.
Dies liegt daran, dass die Implementierung
A
während der meisten Durchläufe des Benchmarks möglicherweise schneller ist als die ImplementierungB
. KannA
aber auch einen höheren Spread aufweisen, sodass der gemessene Leistungsvorteil vonA
im Vergleich zu nicht von Bedeutung istB
.Daher ist es auch wichtig, einen Mikro-Benchmark korrekt zu schreiben und auszuführen, ihn aber auch korrekt zu analysieren.
quelle
Um den anderen ausgezeichneten Rat zu ergänzen, würde ich auch Folgendes beachten:
Bei einigen CPUs (z. B. Intel Core i5 mit TurboBoost) wirkt sich die Temperatur (und die Anzahl der derzeit verwendeten Kerne sowie deren prozentuale Auslastung) auf die Taktrate aus. Da CPUs dynamisch getaktet werden, kann dies Ihre Ergebnisse beeinflussen. Wenn Sie beispielsweise eine Single-Threaded-Anwendung haben, ist die maximale Taktrate (mit TurboBoost) höher als bei einer Anwendung mit allen Kernen. Dies kann daher den Vergleich der Einzel- und Multithread-Leistung auf einigen Systemen beeinträchtigen. Beachten Sie, dass die Temperatur und die Spannungen auch die Dauer der Turbofrequenz beeinflussen.
Vielleicht ein grundlegenderer Aspekt, über den Sie die direkte Kontrolle haben: Stellen Sie sicher, dass Sie das Richtige messen! Wenn Sie beispielsweise
System.nanoTime()
ein bestimmtes Codebit als Benchmark verwenden, platzieren Sie die Aufrufe der Zuweisung an Orten, die sinnvoll sind, um zu vermeiden, dass Dinge gemessen werden, an denen Sie nicht interessiert sind. Führen Sie beispielsweise Folgendes nicht aus:Das Problem ist, dass Sie nicht sofort die Endzeit erhalten, wenn der Code fertig ist. Versuchen Sie stattdessen Folgendes:
quelle
println
, keine separate Kopfzeile oder ähnliches undSystem.nanoTime()
muss als erster Schritt beim Erstellen des Zeichenfolgenarguments für diesen Aufruf ausgewertet werden . Es gibt nichts, was ein Compiler mit dem ersten tun kann, was er mit dem zweiten nicht tun kann, und keiner ermutigt sie, zusätzliche Arbeit zu leisten, bevor sie eine Stoppzeit aufzeichnen.http://opt.sourceforge.net/ Java Micro Benchmark - Steuerungsaufgaben, die erforderlich sind, um die vergleichenden Leistungsmerkmale des Computersystems auf verschiedenen Plattformen zu bestimmen. Kann verwendet werden, um Optimierungsentscheidungen zu treffen und verschiedene Java-Implementierungen zu vergleichen.
quelle