如何从JUnit测试中运行JMH?

如何使用JUnit测试在现有项目中运行JMH基准测试? 官方文档建议使用Maven shade插件创建一个单独的项目,并在main方法中启动JMH。 这是必要的,为什么建议?

我一直在使用JUnit在我现有的Maven项目中运行JMH而没有明显的不良影响。 我无法回答为什么作者建议采取不同的做法。 我没有观察到结果的差异。 JMH启动一个单独的JVM来运行基准来隔离它们。 这是我做的:

  • 将JMH依赖项添加到POM:

      org.openjdk.jmh jmh-core 1.21 test   org.openjdk.jmh jmh-generator-annprocess 1.21 test  

    请注意,我已将它们放在范围test

    在Eclipse中,您可能需要手动配置注释处理器。 NetBeans自动处理此问题。

  • 创建您的JUnit和JMH类。 我选择将两者合二为一,但这取决于你。 请注意, OptionsBuilder.include实际上决定了从JUnit测试运行哪些基准测试!

     import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.TimeUnit; import org.junit.Test; import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.infra.Blackhole; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.options.*; public class TestBenchmark { @Test public void launchBenchmark() throws Exception { Options opt = new OptionsBuilder() // Specify which benchmarks to run. // You can be more specific if you'd like to run only one benchmark per test. .include(this.getClass().getName() + ".*") // Set the following options as needed .mode (Mode.AverageTime) .timeUnit(TimeUnit.MICROSECONDS) .warmupTime(TimeValue.seconds(1)) .warmupIterations(2) .measurementTime(TimeValue.seconds(1)) .measurementIterations(2) .threads(2) .forks(1) .shouldFailOnError(true) .shouldDoGC(true) //.jvmArgs("-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintInlining") //.addProfiler(WinPerfAsmProfiler.class) .build(); new Runner(opt).run(); } // The JMH samples are the best documentation for how to use it // http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/ @State (Scope.Thread) public static class BenchmarkState { List list; @Setup (Level.Trial) public void initialize() { Random rand = new Random(); list = new ArrayList<>(); for (int i = 0; i < 1000; i++) list.add (rand.nextInt()); } } @Benchmark public void benchmark1 (BenchmarkState state, Blackhole bh) { List list = state.list; for (int i = 0; i < 1000; i++) bh.consume (list.get (i)); } } 
  • JMH的注释处理器似乎不适用于NetBeans中的compile-on-save。 每当修改基准时,您可能需要执行完整的Clean and Build 。 (任何建议表示赞赏!)

  • 运行launchBenchmark测试并观察结果!

     ------------------------------------------------------- TESTS ------------------------------------------------------- Running com.Foo # JMH version: 1.21 # VM version: JDK 1.8.0_172, Java HotSpot(TM) 64-Bit Server VM, 25.172-b11 # VM invoker: /usr/lib/jvm/java-8-jdk/jre/bin/java # VM options:  # Warmup: 2 iterations, 1 s each # Measurement: 2 iterations, 1 s each # Timeout: 10 min per iteration # Threads: 2 threads, will synchronize iterations # Benchmark mode: Average time, time/op # Benchmark: com.Foo.benchmark1 # Run progress: 0.00% complete, ETA 00:00:04 # Fork: 1 of 1 # Warmup Iteration 1: 4.258 us/op # Warmup Iteration 2: 4.359 us/op Iteration 1: 4.121 us/op Iteration 2: 4.029 us/op Result "benchmark1": 4.075 us/op # Run complete. Total time: 00:00:06 REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial experiments, perform baseline and negative tests that provide experimental control, make sure the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts. Do not assume the numbers tell you what you want them to tell. Benchmark Mode Cnt Score Error Units Foo.benchmark1 avgt 2 4.075 us/op Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 7.013 sec 
  • Runner.run甚至返回RunResult对象,您可以在其上执行断言等。