Skip to content

How It Works

Justin Conklin edited this page Aug 17, 2020 · 8 revisions

Note: this is still mostly correct, but as of version 0.4.0, all benchmarks are compiled into a single class.

It is recommended that you first understand how JMH works when used from Java, and have read the examples in the sample file before reading this. Also note that understanding the internals is not important for general usage.

For each :benchmarks element and :states key, a separate class file is generated. Benchmark :options translate to JMH benchmark annotations, and :params are compiled as state class instance fields.

For example, the following data:

{:benchmarks [{:fn some.ns/foo, :args [:state/bar]}]
 :states {:bar {:fn some.ns/bar, :args [:param/quux]}}
 :params {:quux {:data 42}}}

is compiled into the following two classes, which we'll represent with some Java code. The identifiers prefixed with $ represent unique generated names:

package $jmh;

import clojure.lang.IFn;
import org.openjdk.jmh.annotations.*;
import static io.github.jgpc42.jmh.Util.*;

public class $Foo {
    private static final IFn _fn = resolve("some.ns", "foo");

    @Benchmark
    public final Object run ($Bar state) {
        return _fn.invoke(state._value);
    }
}
package $jmh;

import clojure.lang.IFn;
import org.openjdk.jmh.annotations.*;
import static io.github.jgpc42.jmh.Util.*;

@State(Scope.Benchmark)
public class $Bar {
    private static final IFn _trial_setup_fn = resolve("some.ns", "bar");

    @Param({"{:data 42}"})
    private String $_param_quux;

    public Object _value;

    @Setup(Level.Trial)
    public final void trial_setup () {
        Object arg = read($_param_quux);
        _value = _trial_setup_fn.invoke(arg);
    }
}

Note that JMH expects state value and parameter fields to be non-final, and is responsible for maintaining thread-safe access. Also, the public state value fields are only ever accessed from benchmark and state fixture methods in the generated code.

The above is a somewhat simplified view, but is conceptually accurate. Other considerations include whether some.ns/foo takes or returns primitive values, uses parameters or JMH infrastructure arguments, is to be apply-ed, etc.

Clone this wiki locally