Java基准测试工具之JMH

深入学习Java基础知识
2021-05-17 14:29 · 阅读时长6分钟
小课

Java Microbenchmark Harness (JMH)是一个适用于JVM平台的微基准测试工具,它封装简化了一些基准测试的通用配置和功能,使用起来非常简单,下面介绍一下JMH的一些用法,示例的完整项目在文章末尾。

首先在gradle或者maven配置中引入JMH CoreJMH Generators

implementation 'org.openjdk.jmh:jmh-core:1.35'
implementation 'org.openjdk.jmh:jmh-generator-annprocess:1.35'

看一个最简单的用法,用于测试helloWorld方法的执行时长,主要代码如下。

1public class HelloWorld {
2
3    @Benchmark
4    @BenchmarkMode(Mode.AverageTime)
5    public void helloWorld() {
6        try {
7            Thread.sleep(10);
8        } catch (InterruptedException e) {
9            e.printStackTrace();
10        }
11    }
12
13    public static void main(String[] args) throws RunnerException {
14        Options opts = new OptionsBuilder()
15                .forks(1)
16                .build();
17        new Runner(opts).run();
18    }
19}

@Benchmark用于标注该方法需要基准测试,@BenchmarkMode用于标注基准测试的模式,可以标注方法和类,标注类表示对类中所有的基准方法生效,目前有4个模式。

  • Throughput 吞吐量,表示单位时间内可完成执行次数,比如说1s内能够执行100次。
  • AverageTime 平均耗时,表示执行1次平均需要花费的时长,比如说执行1次需要100ms。
  • SampleTime 采样耗时,表示在多次执行中随机采样记录耗时,最终会得到一份采样耗时的分布。
  • SingleShotTime 单次执行耗时,主要用于测试冷启动的情况执行的耗时。

OptionsBuilder用于配置一些默认或者全局的参数,后面用到其它的参数和配置再介绍,上面的示例执行完成后得到如下输出,下面一步步解释。

...
Blackhole mode: full + dont-inline hint (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
Warmup: 5 iterations, 10 s each
Measurement: 5 iterations, 10 s each
Timeout: 10 min per iteration
Threads: 1 thread, will synchronize iterations
Benchmark mode: Average time, time/op
Benchmark: com.zhixing.demo.HelloWorld.helloWorld

以上输出是针对helloWorld方法基准测试的一些参数,

  • # Warmup: 5 iterations, 10 s each,表示执行5轮Warmup,每轮执行时长10s。
  • # Measurement: 5 iterations, 10 s each,表示执行5轮测试,每轮执行时长10s。
  • # Timeout: 10 min per iteration,表示一轮测试的超时限制。
1Run progress: 0.00% complete, ETA 00:01:40
2Fork: 1 of 1
3Warmup Iteration   1: 0.011 s/op
4Warmup Iteration   2: 0.012 s/op
5Warmup Iteration   3: 0.012 s/op
6Warmup Iteration   4: 0.012 s/op
7Warmup Iteration   5: 0.011 s/op
8Iteration   1: 0.011 s/op
9Iteration   2: 0.011 s/op
10Iteration   3: 0.011 s/op
11Iteration   4: 0.012 s/op
12Iteration   5: 0.011 s/op

以上输出是对helloWorld方法进行Warmup和测试得到的平均耗时,Warmup和真正测试各5轮,

  • Iteration   1:  0.011 s/op,表示在第1轮测试中,执行一次helloWorld方法的平均耗时是0.011s。
Result "com.zhixing.demo.HelloWorld.helloWorld":
  0.011 ±(99.9%) 0.001 s/op [Average]
  (min, avg, max) = (0.011, 0.011, 0.012), stdev = 0.001
  CI (99.9%): [0.011, 0.012] (assumes normal distribution)

Run complete. Total time: 00:01:40

Benchmark              Mode  Cnt  Score   Error  Units
HelloWorld.helloWorld  avgt    5  0.011 ± 0.001   s/op

以上输出是对helloWorld方法进行基准测试的整体数据,比如最大耗时、最小耗时、平均耗时、误差、标准差以及执行耗时的分布范围。

JMH支持将基准测试结果导出为不同类型的文件,通过在OptionsBuilder中设置文件名和输出格式。

Options opts = new OptionsBuilder()
        ...
        .output(name)
        .resultFormat(ResultFormatType.JSON)
        .build();

将输出的文件导入到 JMH Visualizer中可以将测试结果可视化展示。

文件大小修改时间
build.gradle
415 B 2022年04月29日
gradle/wrapper
2022年04月29日
gradlew
6 kB 2022年04月29日
gradlew.bat
3 kB 2022年04月29日
settings.gradle
26 B 2022年04月29日
src
2022年04月29日
基准测试javajmh