使用JMH做基准测试

JMH 的全称是 Java Microbenchmark Harness ,是一个 open JDK 中用来做性能测试的套件。

快速入门

添加依赖

<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>1.19</version>
</dependency>
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>1.19</version>
</dependency>

添加测试代码

简单写一个测试代码,测试一下 StringBuilderString 做字符串拼接的性能差异。

@BenchmarkMode(Mode.Throughput)
@Warmup(iterations = 3)
@Measurement(iterations = 10, time = 5)
@Threads(8)
@Fork(2)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class StringBuilderBenchmark {

    @Benchmark
    public void string() {
        String a = "";
        for (int i = 0; i < 10; i++) {
            a += i;
        }
    }

    @Benchmark
    public void stringBuilder() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 10; i++) {
            sb.append(i);
        }
    }

    public static void main(String[] args) throws RunnerException {
        Options options = new OptionsBuilder()
                .include(StringBuilderBenchmark.class.getSimpleName())
                .build();
        new Runner(options).run();
    }
}

运行代码

执行 main 方法,运行完成之后,会生成一个测试报告。

Result "com.lyennon.StringBuilderBenchmark.stringBuilder":
  72545.543 ±(99.9%) 3588.622 ops/ms [Average]
  (min, avg, max) = (63846.713, 72545.543, 80526.887), stdev = 4132.663
  CI (99.9%): [68956.921, 76134.166] (assumes normal distribution)

# Run complete. Total time: 00:03:35

Benchmark                              Mode  Cnt      Score      Error   Units
StringBuilderBenchmark.string         thrpt   20  20298.874 ± 1278.400  ops/ms
StringBuilderBenchmark.stringBuilder  thrpt   20  72545.543 ± 3588.622  ops/ms

JMH API

@BenchmarkMode

用于声明基准测试运行时候默认的模式,JMH 支持的基准测试类型包括:

  • Throughput - 整体吞吐量。类似于 QPS ,例如 “1秒内执行了多少次”。
  • AverageTime - 平均耗时。例如 “执行1次, 用了多少秒”。
  • SampleTime - 直方图统计。随机采样耗时,最后会得到采样结果的直方图.,例如 “执行的p995, p99 耗时是多少”。
  • SingleShotTime - 运行一次的时间。可以用于测试冷启动时的性能。
  • All - 所有模式。

@Warmup

运行基准测试之前,首先要进行预热,以保证测试结果的准确性。

为什么需要预热?程序在冷启动之后,执行速度一般会比较慢,运行一段时间预热之后,执行速度会有一些提高,测试结果会更准确。

  • iterations - 预热的轮次。
  • time - 每轮进行的时长。
  • timeUnit - 时长的单位。
  • batchSize - 每次执行调用测试代码的次数。

@Measurement

运行基准测试的一些参数,和 @Warmup 的参数类似。

@Threads

每个进程中的测试线程数,根据具体情况选择,一般为 cpu 乘以 2。

@Fork

运行基准测试的进程数。

@OutputTimeUnit

输出的测试报告的时间单位。

@Benchmark

标注在方法上,表示这是一个需要做性能测试的方法。

@Param

标注在属性上,可以用来指定某项参数的多种情况。特别适合用来测试一个函数在不同的参数输入的情况下的性能。例如

		@Param({"1", "31", "65", "101", "103"})
    public int arg;

    @Param({"0", "1", "2", "4", "8", "16", "32"})
    public int certainty;

    @Benchmark
    public boolean bench() {
        return BigInteger.valueOf(arg).isProbablePrime(certainty);
    }

arg 的参数有5种情况,certainty 的参数有7中情况,加起来总共有 5*7=35 中情况。

@Setup

标注在方法上,可以用来在执行测试之前做一些准备工作。@Setup 只能标注在 @State 标注的 class 里面的方法上。

@TearDown

标注在方法上,可以用来在执行测试之后进行一些结束工作,比如关闭线程池、数据库连接等的,主要用于资源的回收等。

@State

标注在类上,用来表示这个类是一个状态类,可以通过 Scope 参数用来表示该状态的共享范围。Scope 有三种:

  • Benchmark - 所有线程间共享。
  • Group - 同一个组里面所有线程共享。
  • Thread - 每个线程独享。

参考资料

  • JMH 官方示例代码: [https://github.com/openjdk/jmh/tree/master/jmh-samples/src/main/java/org/openjdk/jmh/samples](
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值