MENU

JMH——Java微基准测试工具

December 1, 2021 • 默认分类

JMH(Java Microbenchmark Harness)是专门用于进行代码的微基准测试的一套工具套件,主要是基于方法层面的基准测试,精度可达微秒级。

使用场景

1、想准确知道某个方法的执行时间,以及执行时间和输入变量的相关性。

2、对热点函数进行优化时,对优化的效果进行定量分析。

3、比较一个函数多种实现方式

注意事项

  • 测试前需要预热
  • 防止无用代码进入测试方法中
  • 并发测试
  • 测试结果呈现

使用

1、添加依赖org.openjdk.jmh:jmh-core:${版本}、org.openjdk.jmh:jmh-generator-annprocess:${版本}

2、

@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode(Mode.Throughput)
public class JMHBenchMarkTest {

    private static final int SIZE = 10000;

    private List<String> list = new LinkedList<>();

    @Setup
    public void setUp() {
        for(int i = 0; i < SIZE; i++) {
            list.add(String.valueOf(i));
        }
    }

    @Benchmark
    public void forIndexIterate() {
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i));
        }
    }

    @Benchmark
    public void forEachIterate() {
        for (String s : list) {
            System.out.print(s);
        }
    }

    public static void main(String[] args) throws RunnerException {
        Options build = new OptionsBuilder().include(JMHBenchMarkTest.class.getSimpleName())
                .forks(1) // 做1轮测试
                .warmupIterations(2) // 预热2轮
                .measurementIterations(2) // 正式计量测试做2轮
                .output("D:/Benchmark.log")
                .build();

        new Runner(build).run();
    }
}

结果

Benchmark Mode Cnt Score Error Units
JMHBenchMarkTest.forEachIterate thrpt 2 19.686 ops/s
JMHBenchMarkTest.forIndexIterate thrpt 2 5.913 ops/s

注解介绍

@BenchmarkMode

基准测试类型,用于类或方法上,这个注解的value是个数组,可以把集中Mode集合一起执行。

Throughput整体吞吐量,每秒可以执行次数,单位为ops/time
AverageTime调用的平均时间,每次调用的平均耗时,单位为time/op
SampleTime随机取样,最后输出取样结果的分布,如99%的调用在xxx毫秒以内
SingleShotTime只运行一次,往往同时把 Warmup 次数设为 0,用于测试冷启动时的性能
All所有模式

@Warmup

预热轮数,用于类或方法上,一般前几次进行程序测试的时候都会比较慢, 要让程序进行几轮预热,保证测试的准确性。

参数如下所示:

  1. iterations:预热的次数
  2. time:每次预热的时间
  3. timeUnit:时间的单位,默认秒
  4. batchSize:批处理大小,每次操作调用几次方法

@Measurement

基本测试参数,用于类或方法上,参数和@Warmup相同。

@Benchmark

用于方法上,表示该方法是需要进行基准测试的对象,用法和Junit的@Test类型

@State

当使用@Setup参数时,必须在类上加入这个参数,否则提示无法运行。

State用于声明表明某个类时一个”状态“,接受一个Scope参数表示该状态共享范围,因为很多 benchmark 会需要一些表示状态的类,JMH 允许你把这些类以依赖注入的方式注入到 benchmark 函数里。Scope 主要分为三种:

  1. Scope.Benchmark:该状态在所有线程间共享
  2. Scope.Group:同一个线程在同一个 组里线程共享
  3. Scope.Thread:默认的 State,该状态为每个线程独享

@OutputTimeUnit

基准测试结果的时间类型,一般选择秒、毫秒、微秒,用于类或方法上

@Fork

进行fork的次数,用于类或方法上,如果 fork 数是3的话,则 JMH 会 fork 出3个进程来进行测试。

@Threads

每个进程中的测试线程,用于类或方法上

@Setup

用于方法上,用于在测试之前进行的一些准备工作,比如对一些数据的初始化

@TearDown

用于方法上,用于在测试之后进行的一些结束工作,比如关闭线程池、数据库连接等资源回收操作

@Param

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