970 Stimmen

Wie schreibe ich einen korrekten Mikro-Benchmark in Java?

Wie schreibt man einen korrekten Mikro-Benchmark in Java (und führt ihn aus)?

Ich bin auf der Suche nach einigen Codebeispielen und Kommentaren, die verschiedene Dinge veranschaulichen, über die man nachdenken sollte.

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

Verwandt: Ist das Benchmarking mit Stoppuhr akzeptabel?

0 Stimmen

Siehe [diese Frage][1] von vor ein paar Minuten für einige verwandte Informationen. edit: Entschuldigung, dies sollte keine Antwort sein. Ich hätte es als Kommentar posten sollen. [1]: stackoverflow.com/questions/503877/

0 Stimmen

Nachdem ich vorhatte, den Fragesteller auf eine Frage wie diese zu verweisen, stellte ich fest, dass es diese Frage nicht gab. Hier ist sie also, und hoffentlich werden im Laufe der Zeit einige gute Tipps gesammelt.

6 Stimmen

Java 9 könnte einige Funktionen für Micro-Benchmarking bieten: openjdk.java.net/jeps/230

16voto

Kip Punkte 102702

Wenn Sie versuchen, zwei Algorithmen zu vergleichen, führen Sie mindestens zwei Benchmarks für jeden durch, und zwar in abwechselnder Reihenfolge, 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 merkliche Unterschiede (5-10% manchmal) in der Laufzeit des gleichen Algorithmus in verschiedenen Durchläufen gefunden

Stellen Sie außerdem sicher, dass n ist sehr groß, so dass die Laufzeit jeder Schleife mindestens 10 Sekunden oder so beträgt. Je mehr Iterationen, desto mehr signifikante Zahlen in Ihrer Benchmark-Zeit und desto zuverlässiger sind diese Daten.

6 Stimmen

Eine Änderung der Reihenfolge beeinflusst natürlich die Laufzeit. JVM-Optimierungen und Caching-Effekte kommen hier zum Tragen. Besser ist es, die JVM-Optimierung "aufzuwärmen", mehrere Durchläufe zu machen und jeden Test in einer anderen JVM zu benchmarken.

0 Stimmen

Eigentlich würde ich sagen, dass Sie für die meisten Benchmarking Sie die aufgewärmte Version wollen, würde ich vorschlagen, dass, wenn Sie für 10 Sekunden laufen (wie pro oben Empfehlung), zählen Sie nur die letzten 5 Sekunden - werfen Sie die ersten 5. Denken Sie daran, dass Java irgendwann Code kompiliert.

16voto

Peter Štibraný Punkte 31886

Achten Sie darauf, dass Sie in irgendeiner Weise Ergebnisse verwenden, die in Benchmarking-Code berechnet wurden. Andernfalls kann Ihr Code wegoptimiert werden.

13voto

Mnementh Punkte 48509

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 in Anspruch nehmen: Garbage Collection, Caching-Effekte (des Betriebssystems für Dateien und der CPU für Speicher), IO usw.

Zweitens: Bei sehr kurzen Intervallen kann man sich nicht auf die Genauigkeit der gemessenen Zeiten verlassen.

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. Wärmen Sie die JVM auf (d.h. lassen Sie den Benchmark mindestens einmal ohne Messung laufen, damit die JVM Optimierungen durchführen kann). Führen Sie Ihren Benchmark mehrmals aus (vielleicht 5 Mal) und ermitteln Sie den Medianwert. Führen Sie jeden Mikro-Benchmark in einer neuen JVM-Instanz aus (rufen Sie für jeden Benchmark eine neue Java-Instanz auf), da sonst Optimierungseffekte der JVM die später laufenden Tests beeinflussen können. Führen Sie keine Dinge aus, die nicht in der Warmup-Phase ausgeführt werden (da dies zu einem Class-Load und einer Neukompilierung führen könnte).

9voto

SpaceTrucker Punkte 12246

Es sollte auch darauf hingewiesen werden, dass es wichtig sein könnte, die Ergebnisse des Mikro-Benchmarks zu analysieren, wenn verschiedene Implementierungen verglichen werden. Daher ist eine Signifikanztest vorgenommen werden sollte.

Der Grund dafür ist die Umsetzung A könnte bei den meisten Durchläufen des Benchmarks schneller sein als die Implementierung B . Aber A kann auch eine höhere Spanne haben, so dass der gemessene Leistungsvorteil von A nicht von Bedeutung sein, wenn man sie mit B .

Es ist also wichtig, einen Mikro-Benchmark richtig zu schreiben und auszuführen, aber auch, ihn richtig zu analysieren.

9voto

Sina Madani Punkte 1132

Zusätzlich zu den anderen ausgezeichneten Ratschlägen würde ich auch auf Folgendes achten:

Bei einigen CPUs (z. B. der Intel Core i5-Reihe 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-Thread-Anwendung haben, ist die maximale Taktrate (mit TurboBoost) höher als bei einer Anwendung, die alle Kerne nutzt. Dies kann daher auf einigen Systemen den Vergleich von Single- und Multi-Thread-Leistung beeinträchtigen. Denken Sie daran, dass auch die Temperatur und die Volatilität Einfluss darauf haben, wie lange die Turbofrequenz aufrechterhalten wird.

Ein vielleicht noch wichtigerer Aspekt, auf den Sie direkten Einfluss haben: Stellen Sie sicher, dass Sie das Richtige messen! Zum Beispiel, wenn Sie Folgendes verwenden System.nanoTime() um ein bestimmtes Stück Code zu bewerten, platzieren Sie die Aufrufe der Zuweisung an sinnvollen Stellen, um zu vermeiden, dass Dinge gemessen werden, an denen Sie nicht interessiert sind. Zum Beispiel, tun Sie das nicht:

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 beendet ist. Versuchen Sie stattdessen das Folgende:

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

1 Stimmen

Ja, es ist wichtig, dass innerhalb der zeitlich begrenzten Region keine unverbundene Arbeit stattfindet, aber Ihr erstes Beispiel ist immer noch in Ordnung. Es gibt nur einen Aufruf an println und nicht eine separate Kopfzeile oder ähnliches, und System.nanoTime() muss als die erste Schritt bei der Konstruktion der Zeichenkette arg für diesen Aufruf. Es gibt nichts, was ein Compiler mit dem ersten Schritt tun kann, was er nicht auch mit dem zweiten tun kann, und keiner der beiden Schritte ermutigt sie, zusätzliche Arbeit zu leisten, bevor sie eine Stoppzeit aufzeichnen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X