© Excellent backgrounds/Shutterstock.com
Java 8: Microbenchmarking

Performance messen und analysieren


Wir wollen uns in diesem und den folgenden Beiträgen unserer Serie mit Performancemessungen und -analysen befassen. Wir beginnen damit, dass wir über Performancemessungen im Rahmen so genannter Microbenchmarks nachdenken wollen. Warum macht man überhaupt Microbenchmarks? Wie macht man sie? Warum sind sie so ­fehleranfällig?

Ein Benchmark ist eine Performancemessung, die stets dem Vergleich mehrerer Alternativen dient. Beispielsweise möchte man wissen, ob dieser Algorithmus ein bestimmtes Problem performanter löst als jener Algorithmus. Oder man fragt sich, ob eine Lösung auf dem aktuellen Release der JVM schneller oder langsamer läuft als auf einem älteren Release. Es geht stets um den Vergleich. Eine einzelne Messung allein hat keine Bedeutung. Wir wollen uns speziell mit Microbenchmarks befassen. Das sind Performancevergleiche auf einem feingranularen Level. Generell unterscheidet man zwischen Micro- und Macrobenchmarks. Bei einem Ma­cro­benchmark interessiert man sich für die Performance von Transaktionen, Use Cases oder anderen größeren Ablaufeinheiten innerhalb einer Applikation. Beim Microbenchmark liegt das Augenmerk auf kleineren Einheiten: Man will die Performance mehrerer Algorithmen zur Lösung einer kleineren Aufgabe miteinander vergleichen. Oder man möchte wissen, wie die Performance verschiedener Sprachmittel oder JDK-Abstraktionen im Vergleich ist, z. B. Methodenaufruf via Reflection vs. regulärem Methodenaufruf. Die Idee ist die gleiche: Man möchte pro Alternative eine Kennzahl, die die Performance widerspiegelt, um beurteilen zu können, welche Alternative am schnellsten abläuft. Lediglich die Granularität ist zwischen Micro- und Macrobenchmark unterschiedlich. Die Gründe für einen Microbenchmark können vielfältig sein.

Ein Grund kann die Neugier sein: Man hat irgendwo aufgeschnappt, dass zum Beispiel ein neues Feature im jüngsten Java-Release schneller als vergleichbare ältere Features sein soll. Man möchte einfach mal wissen, ob es stimmt und wie viel schneller es ist. Ein Beispiel ist die Performance des Stream-API in Java 8. Es war in einem Konferenzvideo zu hören, dass die Streams angeblich schneller seien als alles bisher Dagewesene. Natürlich wollten wir wissen, ob es in dieser Allgemeinheit stimmt und wie groß der Performance-Boost ist. Also haben wir Microbenchmarks gemacht und festgestellt, dass sequenzielle Streams in der Regel langsamer sind als eine for-Schleife [1], [2]. Das kann bei parallelen Streams anders sein, wobei es von der Hardware, der Stream-Source und vielen anderen Faktoren abhängt – unter anderem auch von der Art, wie man misst.

Die Erkenntnisse, die durch einen Microbenchmark gewonnen werden, können aber auch von praktischem Nutzen für die aktuelle Projektarbeit sein. Bekanntermaßen ist es schwierig, in der Designphase eines Softwareentwicklungsprojekts schon abzuschätzen, wie sich Designentscheidungen auf die Performance der späteren Software auswirken werden. Fehlentscheidungen in dieser frühen Phase des Projekts führen nicht selten zu heftigen Performanceeinbußen, die vielleicht erst in der Testphase erkannt werden und dann kaum noch zu beseitigen sind. Um solche Fehleinschätzungen zu vermeiden, kann man bereits in der Designphase anhand von Prototypen versuchen, die Performance verschiedener Lösungsansätze abzuschätzen. Zwar sind die Ergebnisse solcher Microbenchmarks anhand von Prototypen mit Vorsicht zu genießen, aber eine erste Einschätzung zwecks Vermeidung grober Fehleinschätzungen können sie trotzdem liefern.

Ein weiterer Grund kann die Implementierung sein. In der Implementierungsphase steht der Entwickler immer wieder vor der Frage: Läuft es schneller, wenn ich es so implementiere? Oder ist es günstiger, es anders zu machen? Läuft meine Implementierung mit der JVM-Option -X1 schneller als mit der Option -X2? Auch zu solchen Fragestellungen kann ein Microbenchmark bei der Entscheidung helfen. Allerdings sind Vorsicht und Augenmaß geboten. In den meisten Applikationen liegt nur ein geringer Teil der Software auf dem performancekritischen Pfad. Nur für diesen Teil lohnen sich die Suche nach der schnellsten Lösung und der Aufwand einer vergleichenden Performancemessung. Im größten Teil der Implementierung kommt es nicht darauf an, ob eine Lösungsalternative um ein paar Millisekunden schneller ist als eine andere. Ehe man auf gut Glück an der Performance feilt, sollte man immer erst mit einem Profiler bestimmen, welche Methoden tatsächlich auf dem performancekritischen Pfad der Applikation liegen. Nur in diesen Methoden lohnt sich der Aufwand, alternative Algorithmen bezüglich ihrer Performance zu vergleichen und so die Performance zu optimieren.

Anders sieht es aus, wenn man Bibliotheken oder Frameworks implementiert, die von anderen Entwicklern genutzt werden sollen. Solche Grundbausteine sollten immer so performant wie möglich sein. Das ist eine klassische Situation, in der die Performance der Implementierungsalternativen mittels eines Benchmarks beurteilt wird.

Zuletzt sind Weiterentwicklungen ein Grund für Microbenchmarks. Wenn man Software weiterentwickelt, ist in der Regel die Erwartung, dass das neue Release nicht langsamer sein darf als das alte Release. In der Applikationsentwicklung läuft diese Anforderung eher auf einen Macrobenchmark hinaus.

Für die Entwickler bei Oracle ist es zum Beispiel anders. Für die JDK-Komponenten werden Microbenchmarks gemacht, bei denen die Implementierungen aus der alten Java-Version als Baseline dienen, gegen die die Performance der neuen Implementierungen verglichen wird. Das sind Microbenchmarks, die hohes Expertenwissen verlangen, weil es um Ablaufzeiten im Nanosekundenbereich geht. Ein Beispiel ist die Klasse sun.misc.Unsafe. Sie war ursprünglich als JDK-interne Hilfsklasse für die effiziente Implementierung der JDK-Klassen gedacht. Mittlerweile verwenden sie aber diverse andere Bibliotheken und Frameworks. Die Klasse sun.misc.Unsafe soll zukünftig verschwinden und wird ab Java 9 sukzessive durch andere Mittel ersetzt. Da die Methoden aus sun.misc.Unsafe häufig aus Performancegründen benutzt werden, darf der Ersatz auf gar keinen Fall langsamer sein als das ursprüngliche Unsafe-Feature. Die Perfo...

Neugierig geworden?

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang