Wie schreibe ich einen korrekten Mikro-Benchmark in Java?

Feb 03 2009

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 man nachdenken sollte.

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

Verwandte: Ist Stoppuhr-Benchmarking akzeptabel?

Antworten

802 12revs,12users61%EugeneKuleshov Feb 05 2009 at 03:49

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:gcetc., 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 -clientund -server, OSR und regelmäßigen Zusammenstellungen. Das -XX:+PrintCompilationFlag 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: Beachten Sie die Deoptimierungs- und Neukompilierungseffekte. Nehmen Sie zum ersten Mal in der Timing-Phase keinen Codepfad, da der Compiler den Code möglicherweise 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. Ü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 -Xbatchdiese Option, um den Compiler mit der Anwendung zu serialisieren, und ziehen Sie die Einstellung -XX:CICompilerCount=1in Betracht , 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 Sie Xmx(groß genug) gleich Xmsund verwenden Sie ihn, UseEpsilonGCfalls 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 .

244 AravindYarram Dec 19 2010 at 06:35

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

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

JMH von OpenJDK

Erste Schritte Tutorials

  1. Vermeiden von Benchmarking-Fallstricken in der JVM
  2. Verwenden von JMH für Java Microbenchmarking
  3. Einführung in JMH
88 JonSkeet Feb 03 2009 at 00:46

Wichtige Dinge für Java-Benchmarks sind:

  • Erwärmen Sie die JIT zuerst, indem Sie den Code mehrmals ausführen, bevor Sie ihn zeitlich festlegen
  • Stellen Sie sicher, dass Sie es lange genug ausführen, um die Ergebnisse in Sekunden oder (besser) zehn Sekunden messen zu können
  • Sie können zwar nicht 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 zeige gerne Iterationen und Zeit sowie eine Zeit- / Iterationsbewertung an, die so skaliert werden kann, dass der "beste" Algorithmus eine Bewertung von 1,0 erhält und andere relativ bewertet werden. Dies bedeutet, dass Sie alle Algorithmen über einen längeren Zeitraum ausführen können , wobei sowohl die Anzahl der Iterationen als auch die Zeit variiert werden, aber dennoch vergleichbare Ergebnisse erzielt werden.

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.

48 assylias Apr 03 2013 at 19:32

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-Harness zum Erstellen, Ausführen und Analysieren von Nano- / Mikro- / Makro-Benchmarks, die in Java und anderen Sprachen für die JVM geschrieben wurden.

Sehr interessante Informationen, die in den Kommentaren der Beispieltests vergraben sind .

Siehe auch:

  • Vermeiden von Benchmarking-Fallstricken in der JVM
  • Diskussion über die Hauptstärken von jmh .
23 PeterLawrey Feb 03 2009 at 02:54

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

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.

16 Kip Feb 03 2009 at 00:57

Wenn Sie versuchen, zwei Algorithmen zu vergleichen, führen Sie jeweils mindestens zwei Benchmarks in abwechselnder Reihenfolge durch. dh:

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 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.

15 PeterŠtibraný Feb 03 2009 at 01:00

Stellen Sie sicher, dass Sie Ergebnisse verwenden, die in Benchmark-Code berechnet werden. Andernfalls kann Ihr Code entfernt werden.

13 Mnementh Feb 03 2009 at 00:46

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 (von Betriebssystem für Dateien und von 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).

8 SpaceTrucker Jan 21 2013 at 21:04

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 Awährend der meisten Durchläufe des Benchmarks möglicherweise schneller ist als die Implementierung B. Kann Aaber auch einen höheren Spread aufweisen, sodass der gemessene Leistungsvorteil von Aim Vergleich zu nicht von Bedeutung ist B.

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

8 SinaMadani Mar 20 2017 at 02:21

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:

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 Yuriy Dec 19 2010 at 06:22

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.