Java Microbenchmark Harness (JMH)
This article dives into the Java Microbenchmark Harness aka. JMH. To keep it simple and focused on JHM, I’ll benchmark two search algorithms: linear and binary search on sorted arrays of different length.
Setting Up a Project
The JMH is pretty simple to setup, just follow the command line usage described on the Github page. I used this:
1 mvn archetype:generate \
2 -DinteractiveMode=false \
3 -DarchetypeGroupId=org.openjdk.jmh \
4 -DarchetypeArtifactId=jmh-java-benchmark-archetype \
5 -DgroupId=dk.anno1980 \
6 -DartifactId=java-microbenchmark-harness \
7 -Dversion=1.0
Next import the project into you favorite IDE.
The Example Code To Benchmark
You can find the full example code on Github. That also contains some comments on the possible configuration annotation that can be used.
1public class SearchBenchmark {
2
3 // the size of the arrays to sort, generated in setup
4 // the @Benchmark methods will run once per element in this array
5 @Param({"1000", "1000000"})
6 int size;
7
8 // the generated array
9 int[] array;
10 // a random int to find in the array
11 int toFind;
12
13 @Setup
14 public void setup() {
15 // generate data in setup, to avoid constant folding
16 // (as opposed to having it generated once and reused, a clever compiler might recognize that)
17 RandomDataGenerator randomDataGenerator = new RandomDataGenerator();
18 array = randomDataGenerator.nextPermutation(size * 10, size);
19 Arrays.sort(array);
20 toFind = randomDataGenerator.nextInt(0, size * 10);
21 }
22
23 @Benchmark
24 public int binarySearch() {
25 // make sure to return the result, to avoid dead code elimination
26 return BinarySearch.find(array, toFind);
27 }
28
29 @Benchmark
30 public int linearSearch() {
31 // make sure to return the result, to avoid dead code elimination
32 return LinearSearch.find(array, toFind);
33 }
34}
Running a JMH Project
Simply build and then run the benchmarks.jar
1mvn clean verify
2# You can also consider using the -t parameter, to specify the number of threads to use (default is 1)
3# This will not make the benchmark run faster, but it will run the benchmark thread number times more.
4java -jar target/benchmarks.jar
It takes approximately 20 minutes to run on my machine.
The Output
Human readable output from running the benchmarks.jar with -t 3
looks like this (machine readable output is also an
option):
1Benchmark (size) Mode Cnt Score Error Units
2SearchBenchmark.binarySearch 1000 thrpt 15 66545,321 ± 4187,760 ops/ms
3SearchBenchmark.binarySearch 1000000 thrpt 15 34420,445 ± 2167,309 ops/ms
4SearchBenchmark.linearSearch 1000 thrpt 15 8539,415 ± 830,537 ops/ms
5SearchBenchmark.linearSearch 1000000 thrpt 15 13,226 ± 19,281 ops/ms
It should be no surprise that binary search is faster than linear search. On an array with 1.000 elements binary search is about 8 times faster. For an array with 1.000.000 elements, binary search is 2602 times faster on my machine.
Note that microbenchmarks are difficult to get right
- just read the samples and their comments in the JMH repository , as there are many, many things that can influence the result. So have them reviewed by a peer and interpret the results with caution.