Benchmarks, Async profiler and JBang

Max Rydahl Andersen

Ever wanted to share an example benchmark and be able to easily generate async profiler flamegraphs ?

This blog explains how that is trivial with JBang.


The other day Francesco Nigro aka. Mr. Performance Guru posted a message pointing to a gist he made.

This gist has some code that uses Java Microbenchmark Harness(JMH) to make some measurements related to Java 19 virtual threads (aka Loom) + some async profiler generated flamegraphs.

All good and great - but how do you run this ?

You could try:

  1. copy the code to your local disk

  2. setup a maven project

  3. add the dependencies

  4. add the async profiler agent

  5. ?

  6. ?

  7. profit?

Or you (or Francesco) could just made a one-liner to run it with JBang.

jbang --java 19 --deps org.openjdk.jmh:jmh-generator-annprocess:1.36 -m org.openjdk.jmh.Main -R=--enable-preview -R=--add-opens -R=java.base/java.lang=ALL-UNNAMED --javaagent=ap-loader@maxandersen=start,event=cpu,file=profile.html

If you run that, you will get the JMH benchmark numbers…​


…​and a nice flamegraph generated for you in profile.html.


Brilliant, right?

How does it work ?

The command line is a bit of a mouthful, so let’s break it down.

--java 19

This says it must use Java 19. You can use 19+ to use 19 or later.

--deps org.openjdk.jmh:jmh-generator-annprocess:1.36

This is the JMH dependency.

-m org.openjdk.jmh.Main

This is the main class to run; as the snippet is a JMH benchmark, we need to run the JMH main class.

-R=--enable-preview -R=--add-opens -R=java.base/java.lang=ALL-UNNAMED

These are the Java 19 preview flags needed to run the code.

This is the gist to run.

Those are fairly simple to understand, but what about this part:


This loads the async profiler agent and configures it - without you having to download a async-profiler.jar file and add it to your classpath.

It works because I made a jbang-catalog that defines ap-loader alias that uses the async profile loader published to Github releases.

I’ve submitted a PR to the ap-loader project to add this to the project itself, but until that is merged you can use my alias.

Making your own (simple) reproducers

Francesco did not know about this feature of JBang, but if he had known he could have made his gist even simpler to use. By adding a few comments JBang can configure itself to run the code.

//JAVA 19
//DEPS org.openjdk.jmh:jmh-generator-annprocess:1.36
//JAVA_OPTIONS --enable-preview --add-opens java.base/java.lang=ALL-UNNAMED

With that the gist would be directly runnable.

I’ve made b1a6dd2019b359ec32b07f0599ee2d9e301a73e2[my own fork of the gist] to show it:


and to run it with async profiler you would just add the --javaagent part:

jbang --javaagent=ap-loader@maxandersen=start,event=cpu,file=profile.html

Bonus tips

Not only does the above make it possible run the gist, but if you have any of the JBang IDE plugins installed it will also just work with content assist, debugging etc. without further setup. All without having to setup a maven project or anything.

This uses a gist, but could also been any .java or .jar file locally or on the web, a maven artifact or another jbang alias.

For example if you wanted to generate flamegraph for Quarkus CLI you would do:

jbang --javaagent=ap-loader@maxandersen=start,event=cpu,file=profile.html quarkus@quarkusio

This is loading the agent on the quarkus@quarkusio alias which behind the scene points to a jar in maven central.

Furthermore, you can even make a command line that will install JBang and even a Java Development Kit (JDK) for you if you don’t have it installed.

curl -Ls | bash -s - --javaagent=ap-loader@maxandersen=start,event=cpu,file=profile.html quarkus@quarkusio

The combinations and possibilities are endless.

Intrigued ?

See JBang for more details and if you have any questions or feedback, please reach out to me on Twitter or on JBang discussions.

Have fun!