1 Testowanie Wydajności Kodu za pomocą Narzędzia JMHWojciech Oczkowski
2 O czym będziemy mówić Czym są benchmarki i w czym mogą nam pomóc Jakie są problemy z mierzeniem wydajności kodu Javy Jak JMH pozwala uniknąć typowych błędów Praktyczne przykłady
3 Wojciech Oczkowski >> 20 lat programowania dla przyjemności>> 10 lat zawodowego programowania w Javie Branże: Obronna, telco / call-center, finansowa Szkolenia Wydajność, Architektura, Integracja Właściciel IT Kontekst Lider Bydgoszcz JUG Aktywny członek Toruń JUG Ojciec, mąż, żeglarz
4 Benchmarki Porównywanie wydajności alternatywnych rozwiązańSprawdzanie wydajności bez konieczności budowy całego rozwiązania Eksperymentowanie z nowymi rozwiązaniami Tuning Benchmarki - testy rozwiązań skupione na mierzeniu i porównywaniu ich wydajności.
5 Co w tym trudnego? long start = System.currentTimeMillis(); work(); System.out.println( System.currentTimeMillis() - start); Nawet najlepsi się na tym wykładali Choć trzeba przyznać że czasy wtedy były inne. (wersja javy, wydajność sprzętu)
6 what could possibly go wrong? Pomiar CZASUZiarnistość 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 - Najczęściej badanie wydajności opieramy o pomiar czasu Dokładność pomiaru czasu jest jednak ograniczona Chcąc podejść do sprawy profesjonalnie nie możemy skupić się na pojedynczym pomiarze Trzeba określić granice niepewności, odchylenie standardowe itp., jest do tego cała teoria opisana np. w monografii GUM.
7 what could possibly go wrong? Optymalizacje kompilatora - pętleRozwijanie pętli Piplineing long start = System.nanoTime(); for (int i = 0; i < ; i++) { work(); } System.out.println((System.nanoTime() - start)/100000);
8 what could possibly go wrongwhat could possibly go wrong? Optymalizacje kompilatora – Dead Code ELIMINATION public void measuredMethod(){ // start measurement int result = work(); // end of measurement }
9 what could possibly go wrongwhat could possibly go wrong? Optymalizacje kompilatora – COnSTANT FOLDING public int measuredMethod(){ // start measurement return work(); // end of measurement }
10 what could possibly go wrong? FALSE Sharingint someCounter; int completelyDifferentCounter; // on Thread 1 public int measuredMethod1(){ return work(someCounter); } // on Thread 2 public int measuredMethod2(){ return work(completelyDifferentCounter); }
11 what could possibly go wrong? Warm up-XX:CompileThreshold=[1500,2000,10000] -XX:-PrintCompilation -XX:MaxInlineSize=35 -XX:+PrintInlining -XX:LoopUnrollLimit=n -zależy od charakterystyki aplikacji -czasami interesuje nas worst case bo spodziewamy się że nie będzie czasu na warmup
12 what could possibly go wrong? Różnice systemów operacyjnychRóż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ż ;)
13 Czym jest JMH i jak może pomócNarzę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
14 Nowy benchmark package pl.itkontekst.jmhtest; import org.openjdk.jmh.annotations.Benchmark; public class MyBenchmark public void testMethod(){ }
15 # JMH 1.14.1 (released 13 days ago)# VM version: JDK 1.8.0_77, VM b03 # VM invoker: C:\Program Files\Java\jre1.8.0_77\bin\java.exe # VM options:
16 Opcje pomiarów = public class MyBenchmark { @Benchmark public void testMethod() { } }
17 Klasy Stanu, setup, parametry, Black Hole@State(Scope.Benchmark) public static class MyState { @Param({"1","2","3"}) int value; @Setup public void init(){} public void testAdd(Blackhole blackhole,MyState state) { blackhole.consume(state.value+state.value); Blackhole.consumeCPU(10); public void testMethod(Blackhole blackhole) { Blackhole.consumeCPU(10); }
18 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); } }
19 Kontrola pracy kompilatora@CompilerControl(CompilerControl.Mode.DONT_INLINE) public void testDontInline(){} @CompilerControl(CompilerControl.Mode.INLINE) public void testForceInline(){} @CompilerControl(CompilerControl.Mode.EXCLUDE) public void testDontCompile(){}
20 Programowa kontrola uruchamianiapublic static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() include(MyBenchmark.class.getSimpleName()) warmupIterations(1) measurementIterations(3) forks(1) build(); new Runner(opt).run(); }
21 Profilery java -jar benchmarks.jar -lprof Supported profilers:cl: Classloader profiling via standard MBeans comp: JIT compiler profiling via standard MBeans gc: GC profiling via standard MBeans hs_cl: HotSpot (tm) classloader profiling via implementation-specific MBeans hs_comp: HotSpot (tm) JIT compiler profiling via implementation-specific MBeans hs_gc: HotSpot (tm) memory manager (GC) profiling via implementation-specific MBeans hs_rt: HotSpot (tm) runtime profiling via implementation-specific MBeans hs_thr: HotSpot (tm) threading subsystem via implementation-specific MBeans pauses: Pauses profiler perf: Linux perf Statistics perfasm: Linux perf + PrintAssembly Profiler perfnorm: Linux perf statistics, normalized by operation count stack: Simple and naive Java stack profiler Unsupported profilers: xperfasm:
22 Profilery Stack profiler:....[Thread state distributions] 97,2% RUNNABLE 2,8% WAITING ....[Thread state: RUNNABLE] 56,8% 58,5% pl.itkontekst.jmhtest.MyBenchmark.testAdd 39,8% 41,0% pl.itkontekst.jmhtest.generated.MyBenchmark_add_jmhTest.testAdd_thrpt_jmhStub 0,3% 0,3% sun.misc.Unsafe.compareAndSwapInt 0,1% 0,1% java.lang.Thread.currentThread 0,1% 0,1% sun.misc.Unsafe.unpark ....[Thread state: WAITING] 2,8% 100,0% sun.misc.Unsafe.park
23 Wyniki java -jar benchmarks.jar -lrfAvailable formats: text, csv, scsv, json, latex java -jar benchmarks.jar -rff output.csv
24 PODSUMOWANIE Pisanie Benchmarków nie jest trywialneJMH 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
25 Warto posłuchać / poczytaćJMH Samples Aleksey Shipilëv – dzieła wybrane Jarosław Pałka – dzieła wybrane Charlie Hunt – dzieła wybrane Scott Oaks – dzieła wybrane Kirk Pepperdine – dzieła wybrane
26 Pytania?