4. 4
Microbenchmarks
Ein Microbenchmark ist ein kleiner künstlicher Benchmark
welcher dazu gedacht ist, eine Methode oder einen
Algorithmus zu testen und verschiedene Szenarien
und/oder Implementierungen miteinander zu vergleichen
5. 5
Microbenchmarks
• Regel Nr. 1: Traue niemals deinen Ergebnissen!
• Regel Nr. 2: Benchmarks lügen mehr als Statistiken!
• Regel Nr. 3: Überprüfe immer deine Ergebnisse!
6. 6
Microbenchmarks
• Einfacher Ansatz
List<Integer> aList = new ArrayList<>();
//fill list with values
long t1 = System.currentTimeMillis();
Collections.sort(aList);
long t2 = System.currentTimeMillis();
System.out.println("Time for using Collections.sort() "+(t2-t1)+"ms");
7. 7
Microbenchmarks
• Besser
int loopCount = 1_000_000;
List<List<Integer>> allLists = new ArrayList<>();
//create and fill lists
long start = System.nanoTime();
for (int i = 0; i < loopCount; i++) {
Collections.sort(allLists.get(i));
}
long end = System.nanoTime();
System.out.println("Duration: " + (end - start)/loopCount);
9. 9
Hinter den Kulissen der JVM
HotSpot
• Ist der Name der Java Virtual Machine von SUN
– Wird jetzt von Oracle gewartet
– andere waren z.B. JRockit, Apache Harmony
– Aktuell: OpenJ9, Zulu & GraalVM
• Standard seit Java 1.3
• Code wird während der Ausführung optimiert
10. 10
Hinter den Kulissen der JVM
JIT – Just in Time Compiler
• Optimiert den Code während der Laufzeit
• Optimierung wird erst nach mehreren tausend Durchläufen gestartet
• Namensgebend für die HotSpot-Engine
11. 11
Hinter den Kulissen der JVM
Was macht HotSpot?
• Bytecode wird zur Laufzeit von der JVM interpretiert
– Langsamste Form der Ausführung
• Häufige Aufrufe von Methoden führen zur Optimierung
– Diese Methoden werden dann direkt in Maschinencode übersetzt
– müssen danach nicht mehr interpretiert werden
– Kompilierter Code wird im Code Cache gespeichert
• Optimierung findet mehrstufig statt (tiered compilation)
13. 13
Hinter den Kulissen der JVM
Deoptimierung
• Ausführungspfad ändert sich plötzlich
Calculator calculator;
if (x == 3) {
calculator = new SpecialCalculator();
}
else {
calculator = new StandardCalculator();
}
calculator.calculate();
14. 14
Hinter den Kulissen der JVM
Inlining
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = 0;
for (int i = 0; i < 100; i++) {
c = addMe(a, b);
}
System.out.println("result is: " + c);
}
public static int addMe(int a, int b) {
return a+b;
}
15. 15
Hinter den Kulissen der JVM
JIT–Compiler
<task_queued compile_id='34' method='com/lucanet/training/benchmark/JitTest addMe (II)I'
bytes='3' count='128' iicount='128' level='1' blocking='1' stamp='0.330' comment='tiered'
hot_count='128’/>
334 35 % b 3 com.lucanet.training.benchmark.JitTest::main @ 11 (56 bytes)
@ 20 com.lucanet.training.benchmark.JitTest::addMe (3 bytes)
@ 37 java/lang/StringBuilder::<init> (not loaded) not inlineable
@ 42 java/lang/StringBuilder::append (not loaded) not inlineable
@ 46 java/lang/StringBuilder::append (not loaded) not inlineable
@ 49 java/lang/StringBuilder::toString (not loaded) not inlineable
@ 52 java/io/PrintStream::println (not loaded) not inlineable
356 37 % b 4 com.lucanet.training.benchmark.JitTest::main @ 11 (56 bytes)
@ 20 com.lucanet.training.benchmark.JitTest::addMe (3 bytes) inline (hot)
16. 16
Hinter den Kulissen der JVM
JIT-Compiler Optimierungen
• Method Inlining
• Dead Code Elimination
• Loop Unrolling
• Escape Analysis
18. 18
Garbage Collection
• Garbage Collectoren verwalten den Speicherbedarf eines Programms
• Es wird zur Laufzeit versucht, nicht mehr benötigte Speicherbereiche zu identifizieren
und dann freizugeben
• Es gibt mehrere Garbage Collectoren, welche aber derzeit alle nach dem Prinzip der
Generational Garbage Collection funktionieren
22. 6
Microbenchmarks
• Einfacher Ansatz
List<Integer> aList = new ArrayList<>();
//fill list with values
long t1 = System.currentTimeMillis();
Collections.sort(aList);
long t2 = System.currentTimeMillis();
System.out.println("Time for using Collections.sort() "+(t2-t1)+"ms");
23. 7
Microbenchmarks
• Besser
int loopCount = 1_000_000;
List<List<Integer>> allLists = new ArrayList<>();
//create and fill lists
long start = System.nanoTime();
for (int i = 0; i < loopCount; i++) {
Collections.sort(allLists.get(i));
}
long end = System.nanoTime();
System.out.println("Duration: " + (end - start)/loopCount);
24. 24
Microbenchmarks
//sorted using Collections.sort()
List<Integer> aList = new ArrayList<>();
for(int i =10000000;i>=0;i--)
{
aList.add(i);
}
long t1 = System.currentTimeMillis();
Collections.sort(aList);
long t2 = System.currentTimeMillis();
System.out.println("Time Required using Collections.sort() :: "+(t2-t1)+"ms");
//sorted using Arrays.sort()
List<Integer> bList = new ArrayList<>();
for(int i =10000000;i>=0;i--)
{
bList.add(i);
}
long t3 = System.currentTimeMillis();
Arrays.sort(bList.toArray());
long t4 = System.currentTimeMillis();
System.out.println("Time Required using Arrays.sort() :: "+(t4-t3)+"ms");
25. 9
Hinter den Kulissen der JVM
HotSpot
• Ist der Name der Java Virtual Machine von SUN
– Wird jetzt von Oracle gewartet
– andere waren z.B. JRockit, Apache Harmony
– Aktuell: OpenJ9, Zulu & GraalVM
• Standard seit Java 1.3
• Code wird während der Ausführung optimiert
26. 26
Java Microbenchmarking Harness
Die Rettung: JMH (Java Microbenchmarking Harness)
• Das einfachste Setup funktioniert mit einem Maven-Projekt
• Den Benchmark nicht in der IDE ausführen!
• Immer das gebaute JAR auf der Kommandozeile ausführen
32. 32
Java Microbenchmarking Harness
Resultate des ersten Testlaufs
Benchmark Mode Cnt Score Error Units
SortBenchmark.testCollec_Sort avgt 10 16,832 ± 0,691 ms/op
SortBenchmark.testArrays_Sort avgt 10 128,085 ± 5,362 ms/op
33. 33
Java Microbenchmarking Harness
Was ist die Ursache?
@Benchmark
public List<Integer> testArrays_Sort(MyState myState) {
Arrays.sort(myState.bList.toArray());
return myState.bList;
}
36. 36
Java Microbenchmarking Harness
JMH - Tipps:
Verwendet keine Schleifen in der Benchmark-Methode
Gebt immer das Ergebnis der Berechnung zurück (der JMH
kümmert sich dann darum)
Benutzt die @Setup Mechanismus für die Erstellung der
notwendigen Ausgangswerte
NoOp-GC nur für Tests verwenden – Niemals in Produktion!
37. 37
Microbenchmarks
Die Fallen
• Microbenchmarks haben immer den Nachteil, dass das zu messende
Problem häufig sehr simplifiziert wird und der JIT-Compiler mehr
Möglichkeiten hat, Optimierungen vorzunehmen.
• Die Live-Umgebung hat eventuell Seiteneffekte, die bei
Microbenchmarks nicht zum Tragen kommen.
• Die Ergebnisse können deshalb nur zwischen Microbenchmarks
verglichen werden und NICHT mit Zahlen aus einer
Produktivumgebung.
38. 18
Garbage Collection
• Garbage Collectoren verwalten den Speicherbedarf eines Programms
• Es wird zur Laufzeit versucht, nicht mehr benötigte Speicherbereiche zu identifizieren
und dann freizugeben
• Es gibt mehrere Garbage Collectoren, welche aber derzeit alle nach dem Prinzip der
Generational Garbage Collection funktionieren
39. 20
Garbage Collection
• Serial GC
• Parallel GC
• (mostly) Concurrent Mark and Sweep GC
• Garbage First Garbage Collector (G1GC)