wake-up-neo.com

Wie schreibe ich einen korrekten Micro-Benchmark in Java?

Wie schreibt man einen korrekten Micro-Benchmark in Java?

Ich suche nach Codebeispielen und Kommentaren, die verschiedene Dinge veranschaulichen, über die ich nachdenken muss.

Beispiel: Soll der Benchmark Zeit/Iteration oder Iterationen/Zeit messen und warum?

Verwandte: Ist Stoppuhr-Benchmarking akzeptabel?

821
John Nilsson

Tipps zum Schreiben von Micro-Benchmarks von den Erstellern von Java HotSpot :

Regel 0: Lesen Sie ein seriöses Dokument über JVMs und Micro-Benchmarking. Ein guter ist Brian Goetz, 2005 . Erwarten Sie nicht zu viel von Micro-Benchmarks. Sie messen nur einen begrenzten Bereich von JVM-Leistungsmerkmalen.

Regel 1: Binden Sie immer eine Aufwärmphase ein, in der Ihr Testkernel vollständig ausgeführt wird. Dies reicht aus, um alle Initialisierungen und Kompilierungen vor der (den) Timing-Phase (n) auszulösen. (In der Aufwärmphase sind weniger Iterationen in Ordnung. Als Faustregel gelten mehrere Zehntausend Iterationen der inneren Schleife.)

Regel 2: Immer mit -XX:+PrintCompilation, -verbose:gc Usw. ausführen, damit Sie überprüfen können, ob der Compiler und andere Teile der JVM erledigen während Ihrer Timing-Phase keine unerwarteten Aufgaben.

Regel 2.1: Drucken Sie Nachrichten zu Beginn und am Ende der Zeit- und Aufwärmphase aus, um sicherzustellen, dass während der Zeitphase keine Ausgabe von Regel 2 erfolgt .

Regel 3: Beachten Sie den Unterschied zwischen -client Und -server Sowie OSR und regulären Kompilierungen. Das Flag -XX:+PrintCompilation Meldet OSR-Kompilierungen mit einem Vorzeichen, um den nicht initialen Einstiegspunkt zu kennzeichnen, zum Beispiel: Trouble$1::run @ 2 (41 bytes). Bevorzugen Sie Server gegenüber Client und regelmäßig gegenüber OSR, wenn Sie die beste Leistung erzielen möchten.

Regel 4: Beachten Sie die Initialisierungseffekte. Drucken Sie nicht zum ersten Mal während Ihrer Timing-Phase, da beim Drucken Klassen geladen und initialisiert werden. Laden Sie keine neuen Klassen außerhalb der Aufwärmphase (oder der Abschlussberichtsphase), 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: Beachten Sie die Auswirkungen von Deoptimierung und Neukompilierung. Nehmen Sie zum ersten Mal in der Timing-Phase keinen Codepfad, da der Compiler möglicherweise den Code verschmutzt und neu kompiliert, 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. Prüfen Sie den Code selbst, bevor Sie sich Theorien darüber bilden, 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, Um den Compiler mit der Anwendung zu serialisieren, und setzen Sie -XX:CICompilerCount=1, Um zu verhindern, dass der Compiler parallel zu sich selbst ausgeführt wird. Versuchen Sie, den GC-Overhead so gering wie möglich zu halten, setzen Sie Xmx (groß genug) auf Xms und verwenden Sie UseEpsilonGC , falls verfügbar.

Regel 8: Verwenden Sie eine Bibliothek für Ihren Benchmark, da diese wahrscheinlich effizienter ist und bereits für diesen einzigen Zweck getestet wurde. Wie JMH , Caliper oder Bill und Pauls ausgezeichnete UCSD-Benchmarks für Java .

744
Eugene Kuleshov

Ich weiß, dass diese Frage als beantwortet markiert wurde, aber ich wollte zwei Bibliotheken erwähnen, die uns helfen, Micro-Benchmarks zu schreiben

Caliper von Google

Erste Schritte Tutorials

  1. http://codingjunkie.net/micro-benchmarking-with-caliper/
  2. http://vertexlabs.co.uk/blog/caliper

JMH von OpenJDK

Erste Schritte Tutorials

  1. Vermeiden von Fallstricken beim Benchmarking in der JVM
  2. http://nitschinger.at/Using-JMH-for-Java-Microbenchmarking
  3. http://Java-performance.info/jmh/
234

Wichtige Dinge für Java Benchmarks sind:

  • Wärmen Sie das JIT zuerst auf, indem Sie den Code mehrmals ausführen , bevor Sie ihn zeitlich steuern
  • Stellen Sie sicher, dass Sie es lange genug laufen lassen, um die Ergebnisse in Sekunden oder (besser) zehn Sekunden messen zu können
  • Sie können System.gc() zwar nicht zwischen den Iterationen aufrufen, es ist jedoch eine gute Idee, es zwischen Tests auszuführen, damit jeder Test hoffentlich einen "sauberen" Speicherplatz erhält, mit dem Sie arbeiten können. (Ja, gc() ist eher ein Hinweis als eine Garantie, aber es ist sehr wahrscheinlich, dass es meiner Erfahrung nach wirklich Müll sammelt.)
  • Ich mag es, Iterationen und Zeit anzuzeigen und eine Punktzahl für Zeit/Iteration, die so skaliert werden kann, dass der "beste" Algorithmus eine Punktzahl von 1,0 erhält und andere auf relative Weise bewertet werden. Dies bedeutet, dass Sie all Algorithmen längere Zeit ausführen können, indem Sie sowohl die Anzahl der Iterationen als auch die Zeit variieren und dennoch vergleichbare Ergebnisse erzielen.

Ich bin gerade dabei, über das Design eines Benchmarking-Frameworks in .NET zu bloggen. Ich habe ein Paar von früheren Posts , das Ihnen vielleicht einige Ideen geben kann - natürlich ist nicht alles angemessen, aber einige davon mögen es sein.

83
Jon Skeet

jmh ist eine neue Erweiterung von OpenJDK und wurde von einigen Performance-Ingenieuren von Oracle geschrieben. Auf jeden Fall einen Blick wert.

Das jmh ist ein Java Geschirr zum Erstellen, Ausführen und Analysieren von Nano-/Mikro-/Makro-Benchmarks, geschrieben in Java und anderen Sprachen, die auf die JVM abzielen.

Sehr interessante Informationen vergraben in die Probe testet Kommentare .

Siehe auch:

47
assylias

Sollte der Benchmark Zeit/Iteration oder Iterationen/Zeit messen und warum?

Es hängt von was ab, das 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.

21
Peter Lawrey

Stellen Sie sicher, dass Sie Ergebnisse verwenden, die im Benchmark-Code berechnet wurden. Ansonsten kann dein Code weg optimiert werden.

15

Wenn Sie versuchen, zwei Algorithmen zu vergleichen, führen Sie mindestens zwei Benchmarks durch, wobei Sie die Reihenfolge abwechseln. d. h .:

for(i=1..n)
  alg1();
for(i=1..n)
  alg2();
for(i=1..n)
  alg2();
for(i=1..n)
  alg1();

Ich habe einige bemerkenswerte Unterschiede (manchmal 5-10%) in der Laufzeit des gleichen Algorithmus in verschiedenen Durchläufen festgestellt.

Stellen Sie außerdem sicher, dass n sehr groß ist, damit die Laufzeit jeder Schleife mindestens 10 Sekunden beträgt. Je mehr Iterationen durchgeführt werden, desto wichtiger sind die Zahlen in Ihrer Benchmark-Zeit und desto zuverlässiger sind die Daten.

14
Kip

Es gibt viele mögliche Fallstricke beim Schreiben von Micro-Benchmarks in Java.

Erstens: Sie müssen mit allen möglichen Ereignissen rechnen, die mehr oder weniger zufällig auftreten: Garbage Collection, Caching-Effekte (des Betriebssystems für Dateien und der CPU für Speicher), IO 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 Sie den Benchmark mindestens einmal ausführen, ohne zu messen, dass die JVM Optimierungen ausführen kann). Führen Sie Ihren Benchmark mehrmals aus (vielleicht fünfmal) und nehmen Sie den Medianwert. Führen Sie jeden Micro-Benchmark in einer neuen JVM-Instanz aus (rufen Sie für jeden Benchmark neues Java auf). Andernfalls können Optimierungseffekte der JVM die spätere Ausführung von Tests beeinflussen. 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).

13
Mnementh

Es sollte auch beachtet werden, dass es möglicherweise auch wichtig ist, 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 Implementierung B. A kann jedoch auch eine höhere Streuung aufweisen, sodass der gemessene Leistungsvorteil von A im Vergleich zu B keine Bedeutung hat.

Daher ist es auch wichtig, einen Mikro-Benchmark korrekt zu schreiben und auszuführen, aber auch korrekt zu analysieren.

8
SpaceTrucker

Um die anderen ausgezeichneten Ratschläge 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 aktuell verwendeten Kerne sowie deren Auslastung in Prozent) 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, die alle Kerne verwendet. Dies kann daher den Vergleich der Leistung mit einem und mehreren Threads auf einigen Systemen beeinträchtigen. Bedenken Sie, dass die Temperatur und die Volatagen auch Einfluss darauf haben, wie lange die Turbofrequenz beibehalten wird.

Vielleicht ein grundlegenderer Aspekt, über den Sie direkt die Kontrolle haben: Stellen Sie sicher, dass Sie das Richtige messen! Wenn Sie beispielsweise System.nanoTime() zum Benchmarking eines bestimmten Codebits verwenden, platzieren Sie die Aufrufe der Zuweisung an Orten, die sinnvoll sind, um zu vermeiden, dass Sie Dinge messen, an denen Sie nicht interessiert sind. Beispiel: don nicht tun:

long startTime = System.nanoTime();
//code here...
System.out.println("Code took "+(System.nanoTime()-startTime)+"nano seconds");

Das Problem ist, dass Sie nicht sofort die Endzeit erhalten, wenn der Code fertig ist. Versuchen Sie stattdessen Folgendes:

final long endTime, startTime = System.nanoTime();
//code here...
endTime = System.nanoTime();
System.out.println("Code took "+(endTime-startTime)+"nano seconds");
7
Sina Madani

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.

7
Yuriy