Use JMH for your Java applications with Maven

Previously we had an example on using JMH with Gradle for our benchmarks.
Since maven is the most popular build tool for Java it is worth to setup an example.

Let’s add the dependency to our project

 

    <dependencies>
        <dependency>
            <groupId>org.openjdk.jmh</groupId>
            <artifactId>jmh-core</artifactId>
            <version>1.36</version>
        </dependency>
        <dependency>
            <groupId>org.openjdk.jmh</groupId>
            <artifactId>jmh-generator-annprocess</artifactId>
            <version>1.36</version>
        </dependency>
    </dependencies>

Let’s add a simple benchmark for Array initialization:

import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;

@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@BenchmarkMode(Mode.All)
public class ArrayInitializationBenchmark {

  @Benchmark
  public Integer[] initialize() {
    return new Integer[256];
  }

}

We can run the benchmark in various ways. A main class org.openjdk.jmh.Main is provided. This class can be used to pass arguments and execute the benchmarks of interest. So what we can do is to use the maven shade plugin and generate a binary just for the benchmarks.

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.0</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <finalName>jmh-benchmarks</finalName>
                            <transformers>
                                <transformer
                                  implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>org.openjdk.jmh.Main</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

By running

mvn clean install

We shall have two jars generated. The jar jmh-benchmarks.jar is the one of interest since it will come configured with the org.openjdk.jmh.Main class.

Running the benchmark is simple

java -jar target/jmh-benchmarks.jar ArrayInitializationBenchmark

It we need more customisations we can also create our own main method and define our benchmarks that we shall pass to the JMH Runner.

package com.gkatzioura.concurrency.benchmark;

import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

public class BenchmarkMain {

  public static void main(String[] args) throws RunnerException {
    Options opt = new OptionsBuilder().include(ArrayInitializationBenchmark.class.getSimpleName()).build();
    new Runner(opt).run();
  }

}

The results will be printed on console

Benchmark                                                     Mode      Cnt     Score   Error   Units
ArrayInitializationBenchmark.initialize                      thrpt       25    10.050 ± 1.045  ops/us
ArrayInitializationBenchmark.initialize                       avgt       25     0.116 ± 0.011   us/op
ArrayInitializationBenchmark.initialize                     sample  8122862     0.147 ± 0.003   us/op
ArrayInitializationBenchmark.initialize:initialize·p0.00    sample                ≈ 0           us/op
ArrayInitializationBenchmark.initialize:initialize·p0.50    sample              0.125           us/op
ArrayInitializationBenchmark.initialize:initialize·p0.90    sample              0.208           us/op
ArrayInitializationBenchmark.initialize:initialize·p0.95    sample              0.250           us/op
ArrayInitializationBenchmark.initialize:initialize·p0.99    sample              0.417           us/op
ArrayInitializationBenchmark.initialize:initialize·p0.999   sample              1.374           us/op
ArrayInitializationBenchmark.initialize:initialize·p0.9999  sample             16.608           us/op
ArrayInitializationBenchmark.initialize:initialize·p1.00    sample           2179.072           us/op
ArrayInitializationBenchmark.initialize                         ss        5     2.992 ± 0.593   us/op

We have all the benchmark modes enabled thus each one of them is listed: Throughput, Average, Single Shot and Sample.
For a more detailed view on the benchmark and the options we have you can check the previous blog on JMH.

That’s it! Happy benchmarking

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.