DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Using Python Libraries in Java
  • Designing a Java Connector for Software Integrations
  • How to Convert XLS to XLSX in Java
  • Recurrent Workflows With Cloud Native Dapr Jobs

Trending

  • Blue Skies Ahead: An AI Case Study on LLM Use for a Graph Theory Related Application
  • Concourse CI/CD Pipeline: Webhook Triggers
  • Medallion Architecture: Why You Need It and How To Implement It With ClickHouse
  • DGS GraphQL and Spring Boot
  1. DZone
  2. Coding
  3. Java
  4. Java Microbenchmark Harness (JMH)

Java Microbenchmark Harness (JMH)

A quick hands-on lesson to learn about Java Microbenchmark Harness (JMH). The article helps you get started and configure JMH project.

By 
Gaurav Gaur user avatar
Gaurav Gaur
DZone Core CORE ·
Apr. 28, 21 · Tutorial
Likes (5)
Comment
Save
Tweet
Share
8.9K Views

Join the DZone community and get the full member experience.

Join For Free

Introduction

In my previous article, we established that microbenchmarking is hard with jvm. It is not enough to surround the code in a loop with System.out.println() and gather the time measurements. While benchmarking, a developer should consider warm-up cycles, JIT compilations, JVM optimizations, avoiding usual pitfalls, and even more.

Thankfully, OpenJDK has a great tool Java Microbenchmark Harness (JMH) that can help us generated benchmarking stats. In this article, we will discuss how JMH can help us avoid the pitfalls that we have discussed earlier.

Getting Started With JMH

A quick way to start with JMH is to use the Maven archetype. The command below will generate a new Java project benchmark. The project will have com/gaurav/MyBenchmark.java class and pom.xml. The Maven pom.xml includes all the required dependencies to support JMH.

mvn archetype:generate -DarchetypeGroupId=org.openjdk.jmh -DarchetypeArtifactId=jmh-java-benchmark-archetype -DinteractiveMode=false -DgroupId=com.gaurav -DartifactId=benchmark -Dversion=1.0


Good Benchmarks With JMH

Let us discuss how JMH can help us to write better microbenchmarks.

  • JMH by default makes several warm-up cycles before collecting the stats. Thus, it makes sure that the results are not completely random and jvm has performed optimizations.
  • @benchmark runs iteration over the code, and then collects the average. The more runs you make through the code, the better stats you will collect.
  • Use Blackhole class of JMH can avoid deal code elimination by jvm. If you pass the calculated results to blackhole.consume(), it would trick the jvm. jvm will never drop the code, thinking that consume() method uses the result.

Writing First Benchmark

Maven has already provided with us a template in MyBenchmark class to fill in. I am going to utilize the same class.

Java
 




x


 
1
package com.gaurav;
2
import org.openjdk.jmh.annotations.Benchmark;
3
public class MyBenchmark {
4
    @Benchmark
5
    public void testMethod() {
6
        // This is a demo/sample template for building your JMH benchmarks. Edit as needed.
7
        // Put your benchmark code here.
8
    }
9
}



I would like to keep my first benchmark simple. Let us start by iterating over all the elements of a list and sum them up using a conventional for loop. As discussed, I will use Blackhole to fool the compiler and return the result. Here, I am asking JMH to calculate the average time, using @BenchmarkMode, it takes to run the testMethod().

Java
 




x


 
1
@Benchmark
2
@BenchmarkMode(Mode.AverageTime)
3
public static double testMethod(Blackhole blackhole) {
4
  double sum = 0;
5
  for(int i=0; i<list.size(); i++) {
6
    sum += list.get(i);
7
  }
8
  blackhole.consume(sum);
9
  return sum;
10
}



Compiling the JMH Project

You can compile and build the project like any other Maven project, using the below Maven command:

mvn clean install


The command will create a fully executable jar file under benchmark/target the directory. Please note that Maven will always generate a jar file named benchmarks.jar, regardless of the project name.

Next step is to execute the jar using the below command:

java -jar target/benchmarks.jar


It produced the below result for me. It means that test operation is taking approx. 0.053 seconds on the current hardware.

Shell
 




xxxxxxxxxx
1
17


 
1
# Run progress: 80.00% complete, ETA 00:01:41
2
# Fork: 5 of 5
3
# Warmup Iteration   1: 0.052 s/op
4
# Warmup Iteration   2: 0.051 s/op
5
# Warmup Iteration   3: 0.053 s/op
6
# Warmup Iteration   4: 0.056 s/op
7
# Warmup Iteration   5: 0.055 s/op
8
Iteration   1: 0.054 s/op
9
Iteration   2: 0.053 s/op
10
Iteration   3: 0.053 s/op
11
Iteration   4: 0.054 s/op
12
Iteration   5: 0.059 s/op
13
Result "com.example.MyBenchmark.testMethod":
14
  0.053 ±(99.9%) 0.002 s/op [Average]
15
  (min, avg, max) = (0.052, 0.053, 0.061), stdev = 0.002
16
  CI (99.9%): [0.051, 0.055] (assumes normal distribution)
17
# Run complete. Total time: 00:08:27



Benchmark Modes

In the previous example, I used @BenchmarkMode(Mode.AverageTime). If you try to decompile the JMH jar, you will find enum Mode with the below values:

Modes
Throughput("thrpt", "Throughput, ops/time") It will calculate the number of times your method can be executed with in a second
AverageTime("avgt", "Average time, time/op") It will calculate the average time in seconds to execute the test method
SampleTime("sample", "Sampling time") It randomly samples the time spent in the test method calls
SingleShotTime("ss", "Single shot invocation time") It works on single invocation of the method and is useful in calculating cold performance
All("all", "All benchmark modes") Calculates all the above

The default Mode is Throughput. 

Time Measurement

It is evident from the console output above that calculations are in seconds. But, JMH allows you to configure the time units, using @OutputTimeUnit annotation. The @OutputTimeUnit accepts java.util.concurrent.TimeUnit, as shown below:

@OutputTimeUnit(TimeUnit.SECONDS)


The TimeUnit enum has the following values:

NANOSECONDS
MICROSECONDS
MILLISECONDS
SECONDS
MINUTES
HOURS
DAYS

The default TimeUnit is SECONDS. 

Configure Fork, Warmup, and Iterations

The benchmark is currently executing 5 times, with 5 warmup iterations and 5 measurement iterations. JMH even allows to configure these values using @Fork, @Warmup and @Measurement annotations. The code snippet below would execute the test method twice, with a couple of warmup iterations and 3 measurement iterations.

Java
 




xxxxxxxxxx
1


 
1
@Fork(value = 2)
2
@Warmup(iterations = 2)
3
@Measurement(iterations = 3)



@Warmup and @Measurement annotations also accepts parameters:

  • batchSize - configures the number of test method calls to be performed per operation.
  • time - time spent for each iteration.

Practice

You can play around to compare execution times of different for loops i.e. a conventional for loop, a forEach loop and an stream iterator. Something like:

Java
 




xxxxxxxxxx
1
30


 
1
private static final List<Integer> list = IntStream.rangeClosed(1, Integer.MAX_VALUE/100)
2
            .boxed().collect(Collectors.toList());
3
    @Benchmark
4
    @BenchmarkMode(Mode.AverageTime)
5
    public static double conventionalLoop(Blackhole blackhole) {
6
        double sum = 0;
7
        for(int i=0; i<list.size(); i++) {
8
            sum += list.get(i);
9
        }
10
        
11
        blackhole.consume(sum);
12
        return sum;
13
    }
14
    @Benchmark
15
    @BenchmarkMode(Mode.AverageTime)
16
    public static double enhancedForLoop(Blackhole blackhole) throws InterruptedException {
17
        double sum = 0;
18
        for (int integer : list) {
19
            sum += integer;
20
        }
21
        blackhole.consume(sum);
22
        return sum;
23
    }
24
    @Benchmark
25
    @BenchmarkMode(Mode.AverageTime)
26
    public static double streamMap(Blackhole blackhole) {
27
        double sum = list.stream().mapToDouble(Integer::doubleValue).sum();
28
        blackhole.consume(sum);
29
        return sum;
30
    }


Conclusion

In this article, we have gone through a hands-on example of creating a JMH project. We have seen how can we configure our JMH project to suit our needs. You can refer to JMH Github Samples for more in-depth examples.

We have seen that JMH is a jvm tool. In the next article, we will try to explore if it can help us with other jvm based languages.

Java (programming language)

Published at DZone with permission of Gaurav Gaur. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Using Python Libraries in Java
  • Designing a Java Connector for Software Integrations
  • How to Convert XLS to XLSX in Java
  • Recurrent Workflows With Cloud Native Dapr Jobs

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!