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.