JMH to doskonałe narzędzie do benchmarkowania fragmentów aplikacji oparty o JVM, dlatego, że mamy w nim możliwość “rozgrzania” JVM tak aby JIT mógł zrobić swoje, przed faktycznym benchmarkiem na podstawie którego jest robiony pomiar.
Na początek dodajemy zależność do maven’a:
<dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-core</artifactId> <version>1.20</version> <scope>test</scope> </dependency> <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-generator-annprocess</artifactId> <version>1.20</version> <scope>test</scope> </dependency>
dlaczego scope test? osobiście polecam budowanie benchmarków jako JUnit lub MainClass w przestrzeni src/test/java
Tworzymy sobie klasę np: SerializableBenchmark i w niej piszemy metody:
public void ...
W metodzie wpisujemy:
int warmup = 5; int iterations = 5; int forks = 1; int threads = 1; String testClassRegExPattern = ".*Benchmark.*"; ResultFormatType resultsFileOutputType = ResultFormatType.TEXT; String resultFilename = "jmh-result.txt"; Options opt = new OptionsBuilder().include(testClassRegExPattern).warmupIterations(warmup).measurementIterations(iterations) .forks(forks).threads(threads).shouldDoGC(true).shouldFailOnError(true).resultFormat(resultsFileOutputType) .result(resultFilename) .shouldFailOnError(true).jvmArgs("-server").build(); try { Runner runner = new Runner(opt); runner.run(); } catch (Exception e) { throw new RuntimeException(e); }
Gdzie:
testClassRegExPattern – regexp klas jakie ma uruchomić benchmark
I to jest nasza klasa testowa umożliwiająca wykonywanie benchmarków za pomocą testów jednostkowych.
W klasach które maja sie uruchomić jako benchmarki wpisujemy metodę z adnotacją: @Benchmark
Można również uruchomić test z bechmarkiem znajdującym się w tej samej klasie, przykład:
@State(Scope.Benchmark) @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.MICROSECONDS) public class SerializationBenchmark { private ObjectMapper objectMapper = new ObjectMapper(); public static final String JSON = “...”; @Test public void benchmark() { int warmup = 5; int iterations = 5; int forks = 1; int threads = 1; ResultFormatType resultsFileOutputType = ResultFormatType.TEXT; String resultFilename = "jmh-result.txt"; Options opt = new OptionsBuilder().include("SerializationBenchmark") .warmupIterations(warmup).measurementIterations(iterations) .forks(forks).threads(threads) .shouldDoGC(true) .shouldFailOnError(true) .resultFormat(resultsFileOutputType) .result(resultFilename) .shouldFailOnError(true).jvmArgs("-server").build(); try { Runner runner = new Runner(opt); runner.run(); } catch (Exception e) { throw new RuntimeException(e); } } @Benchmark public void streamBuffer() throws Exception { ... } @Benchmark public void fillBuffer() throws Exception { ... } }
Według mnie uruchamianie benchmarów JMH jako test jednostkowy jest dużo bardziej elastyczne niż uruchomienie tego za pomocą pluginu mavenowego.