JMH and CDI made easy


JMH is a great tool to write microbenchmarks but when you need to integrate it with CDI it can be a bit tricky or you need to break the “common” concept of shade JMH really loves.

However OpenEJB ApplicationComposer provides a built-in solution to this issue pretty neat.

Note on used version: this blog post has been written using OpenEJB 4.7.2.

The idea there is to use ApplicationComposers to handle the application description and lifecycle in a JMH benchmark scoped state.

Basic wiring looks like:

@State(Scope.Benchmark)
public class MyEEBench {
    private final ApplicationComposers applicationComposers = new ApplicationComposers(MyEEBench.class);

    @Setup
    public void setup() throws Exception {
        applicationComposers.before(this);
    }

    @TearDown
    public void tearDown() throws Exception {
        applicationComposers.after();
    }

    // ...
}

Then you can describe your application as usual with ApplicationComposer using @Module in MyEEBench:

@State(Scope.Benchmark)
public class MyEEBench {
    // ...

    @Module
    @Classes(cdi = true, innerClassesAsBean = true)
    public WebApp app() {
        return new WebApp();
    }

    // ...
}

And also get injections in the state:

@State(Scope.Benchmark)
public class MyEEBench {
    // ...

    @Inject
    private BeanManager bm;

    @Resource
    private DataSource ds;

    // ...
}

And finally you can write your benchmark using the injected beans/resources/services:

@State(Scope.Benchmark)
public class MyEEBench {
    // ...

    @Benchmark
    public void beanManagerFireEvent() {
        bm.fireEvent(new Evt());
    }

    // ...
}

Put all together it looks like:

import org.apache.openejb.jee.WebApp;
import org.apache.openejb.testing.ApplicationComposers;
import org.apache.openejb.testing.Classes;
import org.apache.openejb.testing.Module;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
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;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Event;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.BeanManager;
import javax.inject.Inject;
import java.util.logging.Logger;

@State(Scope.Benchmark)
public class CdiEvent {
    @Inject
    private BeanManager bm;

    @Inject
    private Event<Evt> evt;

    private final ApplicationComposers applicationComposers = new ApplicationComposers(CdiEvent.class);

    @Setup
    public void setup() throws Exception {
        applicationComposers.before(this);
    }

    @TearDown
    public void tearDown() throws Exception {
        applicationComposers.after();
    }

    @Benchmark
    public void beanManagerFireEvent() {
        bm.fireEvent(new Evt("-"));
    }

    @Benchmark
    public void eventFire() {
        evt.fire(new Evt("-"));
    }

    @Module
    @Classes(cdi = true, innerClassesAsBean = true)
    public WebApp app() {
        return new WebApp();
    }

    public static class Evt {
        private final String evt;

        public Evt(String evt) {
            this.evt = evt;
        }

        public String getEvt() {
            return evt;
        }
    }

    @ApplicationScoped
    public static class See {
        public void listen(@Observes final Evt e) {
            // no-op
        }
    }

    public static void main(final String[] args) throws RunnerException {
        final Options opt = new OptionsBuilder()
            .include(CdiEvent.class.getSimpleName())
            .forks(1)
            .warmupIterations(5)
            .measurementIterations(5)
            .threads(30)
            .build();
        new Runner(opt).run();
    }
}

Finally easier than expected isn’t it? 😉

No more reason to not microbenchmark and validate any part of your framework now :).

Advertisement

1 thought on “JMH and CDI made easy

  1. Pingback: Java Weekly 40/15: JMH and CDI, MVC, Microservice Truths

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s