2. Benchmarking
• Po co nam Benchmarki
• Typowe problemy w mierzeniu wydajności kodu
• Narzędzia pozwalające na porównywanie wydajności
3. Po co nam Benchmarki
• Porównywanie wydajności alternatywnych rozwiązań
• Sprawdzanie wydajności bez konieczności budowy
całego rozwiązania
• Eksperymentowanie z nowymi rozwiązaniami
4. Rodzaje benchmarków
• Makro Benchmarki
• Porównywanie dużych funkcjonalności
• Całe procesy
• Całe systemy
• Cała JVM
• Mikro Benchmarki
• Niewielki kawałki kodu
• Krótki czas wykonania
5. Co w tym trudnego?
long start = System.currentTimeMillis();
work();
System.out.println(
System.currentTimeMillis() - start);
6. Co może pójść nie tak?
• Pomiar czasu
• Optymalizacje kompilatora
• Warm up
• Różnice platform
7. Pomiar czasu
• Ziarnistość pomiaru czasu
(~30 ns Linux, ~300 ns Windows [1t])
• Ukryty narzut System.nanoTime();
• Błąd pomiaru
• różnice w implementacji timerów w systemach
operacyjnych
8. Optymalizacje kompilatora -
pętle
• Rozwijanie pętli
• Piplineing
long start = System.nanoTime();
for (int i = 0; i < 100000; i++) {
work();
}
System.out.println((System.nanoTime() - start)/100000);
10. Optymalizacje kompilatora –
False Sharing
int someCounter;
int completelyDifferentCounter;
// on Thread 1
public int measuredMethod1(){
return work(someCounter);
}
// on Thread 2
public int measuredMethod2(){
return work(completelyDifferentCounter);
}
12. Różnice systemów operacyjnych
• Różnice w implementacji JVM
• Różnice timerów
• Różnice scheduler’ów
• 32 vs 64 bit
• Niektóre optymalizacje dostępne są dla wybranych
OS’ów
• Niektóre bug’i też ;)
14. Czym jest JMH i jak może
pomóc
• Narzędzie do budowania benchmarków
• Pomaga w uniknięciu typowych problemów
• Nie zwalnia z myślenia o nich.
• Java -> org.openjdk.jmh:jmh-java-benchmark-archetype
• Scala -> org.openjdk.jmh:jmh-scala-benchmark-archetype
-> sbt-jmh plugin
• Groovy -> org.openjdk.jmh:jmh-groovy-benchmark-
archetype
• Kotlin -> org.openjdk.jmh:jmh-java-benchmark-archetype
16. # JMH 1.14.1 (released 13 days ago)
# VM version: JDK 1.8.0_77, VM 25.77-b03
# VM invoker: C:Program FilesJavajre1.8.0_77binjava.exe
# VM options: <none>
# Warmup: 20 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: pl.itkontekst.jmhtest.MyBenchmark.testMethod
# Run progress: 0,00% complete, ETA 00:06:40
# Fork: 1 of 10
# Warmup Iteration 1: 3321638210,400 ops/s
# Warmup Iteration 2: 3035709856,963 ops/s
…
Iteration 19: 3292590873,371 ops/s
Iteration 20: 3352591339,138 ops/s
Result "testMethod":
3336940878,008 ?(99.9%) 21593035,964 ops/s [Average]
(min, avg, max) = (2837336846,716, 3336940878,008, 3433712311,191), stdev =
91426266,353
CI (99.9%): [3315347842,045, 3358533913,972] (assumes normal distribution)
# Run complete. Total time: 00:06:43
Benchmark Mode Cnt Score Error Units
MyBenchmark.testMethod thrpt 200 3336940878,008 ? 21593035,964 ops/s
17. Opcje pomiarów
@Fork(1)
@Warmup(iterations = 5)
@Measurement(iterations = 5)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class MyBenchmark {
@Benchmark
public void testMethod() {
}
}
18. Klasy Stanu, setup, parametry,
Black Hole
@State(Scope.Benchmark)
public static class MyState {
@Param({"1","2","3"})
int value;
@Setup
public void init(){}
}
@Benchmark
public void testAdd(Blackhole blackhole,MyState state) {
blackhole.consume(state.value+state.value);
Blackhole.consumeCPU(10);
}
@Benchmark
public void testMethod(Blackhole blackhole) {
Blackhole.consumeCPU(10);
}
19. Wątki, Grupy, Padding
@Threads(4)
public class MyBenchmark {
@State(Scope.Benchmark)
public static class MyState {
@Param({"2"})
int value;
}
@State(Scope.Benchmark)
public static class MyState2 {
@Param({"1"})
int value2;
}
@Benchmark
@Group("add")
public void testAdd(Blackhole blackhole,MyState state) {
blackhole.consume(state.value+state.value);
}
@Benchmark
@Group("add")
public void testAdd2(Blackhole blackhole,MyState2 state) {
blackhole.consume(state.value2+state.value2);
}
}
24. Wyniki
java -jar benchmarks.jar -lrf
Available formats: text, csv, scsv, json, latex
java -jar benchmarks.jar -rff output.csv
25. Podsumowanie
• Pisanie Mikro Benchmarków nie jest trywialne
• JMH pomaga w unikaniu typowych problemów ale nie
zwalnia z myślenia o nich
• JMH nie zastąpi ostatecznych testów wydajnościowych
• JIT to potężny oręż, który może Ci obciąć rękę kiedy nie
będziesz uważny