JMH测试工具介绍
介绍 什么是JMH
JMH和jMeter的使用场景还是有很大的不同的,jMeter更多的是对rest api进行压测,而JMH关注的粒度更细,它更多的是发现某块性能槽点代码,然后对优化方案进行基准测试对比。比如json序列化方案对比,bean copy方案对比,文中提高的洗牌算法对比等。
JMH(Java Microbenchmark Harness)是一个专门用于编写和运行 Java 微基准测试的框架。
一般使用流程如下
下面简单介绍一下如何使用 JMH 进行基准测试:
- 添加依赖:在项目中添加 JMH 的依赖,可以通过 Maven 或 Gradle 等构建工具配置。
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.33</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.33</version>
</dependency>
2
3
4
5
6
7
8
9
10
11
- 编写基准测试代码:创建一个类,并在其中编写需要进行基准测试的方法。
- 使用 @Benchmark 注解标记需要进行基准测试的方法。
- 配置基准测试选项:可以使用 @State 注解来配置基准测试的状态,例如共享变量、线程数等。
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
@State(Scope.Thread)
public class MyBenchmark {
int x = 1;
int y = 2;
@Benchmark
public int add() {
return x + y;
}
@Benchmark
public int subtract() {
return x - y;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 运行基准测试:可以使用命令行或者其他可视化工具来运行基准测试,例如使用 Maven 命令
mvn clean install
或mvn clean install -DskipTests=false
- 分析结果:JMH 会输出详细的测试结果,包括每个测试方法的平均时间、吞吐量、方差、标准差等统计数据,可以根据这些数据来分析和比较基准测试的结果。
Benchmark Mode Cnt Score Error Units
MyBenchmark.add thrpt 20 16.313 ± 0.076 ops/s
MyBenchmark.subtract thrpt 20 16.304 ± 0.076 ops/s
2
3
解释一下,这里是测的是吞吐量,那么score表示就是每秒执行了 16 次。后面的error表示的是误差。cnt表示迭代次数。
JMH的使用
- @BenchmarkMode
用来配置 Mode 选项,可用于类或者方法上,这个注解的 value 是一个数组,可以把几种 Mode 集合在一起执行,如:@BenchmarkMode({Mode.SampleTime, Mode.AverageTime})
,还可以设置为 Mode.All
,即全部执行一遍。
Throughput:整体吞吐量,每秒执行了多少次调用,单位为
ops/time
AverageTime:用的平均时间,每次操作的平均时间,单位为
time/op
SampleTime:随机取样,最后输出取样结果的分布
SingleShotTime:只运行一次,往往同时把 Warmup 次数设为 0,用于测试冷启动时的性能
All:上面的所有模式都执行一次
@State
通过 State 可以指定一个对象的作用范围,JMH 根据 scope 来进行实例化和共享操作。@State 可以被继承使用,如果父类定义了该注解,子类则无需定义。由于 JMH 允许多线程同时执行测试,不同的选项含义如下:
Scope.Benchmark:所有测试线程共享一个实例,测试有状态实例在多线程共享下的性能
Scope.Group:同一个线程在同一个 group 里共享实例
Scope.Thread:默认的 State,每个测试线程分配一个实例
@OutputTimeUnit
为统计结果的时间单位,可用于类或者方法注解
- @Warmup
预热所需要配置的一些基本测试参数,可用于类或者方法上。一般前几次进行程序测试的时候都会比较慢,所以要让程序进行几轮预热,保证测试的准确性。参数如下所示:
- iterations:预热的次数
- time:每次预热的时间
- timeUnit:时间的单位,默认秒
- batchSize:批处理大小,每次操作调用几次方法
为什么需要预热? 因为 JVM 的 JIT 机制的存在,如果某个函数被调用多次之后,JVM 会尝试将其编译为机器码,从而提高执行速度,所以为了让 benchmark 的结果更加接近真实情况就需要进行预热。
- @Measurement
实际调用方法所需要配置的一些基本测试参数,可用于类或者方法上,参数和 @Warmup
相同。
- @Threads
每个进程中的测试线程,可用于类或者方法上。
- @Fork
进行 fork 的次数,可用于类或者方法上。如果 fork 数是 2 的话,则 JMH 会 fork 出两个进程来进行测试。
- @Param
指定某项参数的多种情况,特别适合用来测试一个函数在不同的参数输入的情况下的性能,只能作用在字段上,使用该注解必须定义 @State 注解。
在介绍完常用的注解后,让我们来看下 JMH 有哪些陷阱。
可参考文档:
https://www.cnblogs.com/54chensongxia/p/15485421.html