JVH(Java Microbenchmark Harness),一个OpenJDK发布的一个面向方法级别的性能测试工具。它封装了性能测试的基础设施,使开发人员更多地关注测试逻辑,减少不必要的开发工作,他使开发人员可以使用类似JUnit的方式运行性能测试,在使用插件后这种优势更加突出。
先看一个简单的示例:
--需添加maven依赖
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.21</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.21</version>
<scope>provided</scope>
</dependency>
--java代码
package com.sht.study.jmh;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.samples.JMHSample_01_HelloWorld;
public class T001HelloWorld {
@Benchmark
public void testNoting(){
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(T001HelloWorld.class.getSimpleName())
.forks(1)
.build();
new Runner(opt).run();
}
}
在上述示例中:
- @Benchmark标签用在方法上,用来标志这是一个性能测试方法,也就是我们要测试的代码逻辑,每个测试中可以有多个Benchmark。
- 构建运行配置项并启动测试。options有很多配置项,
我们会在以后逐渐进行讲解和总结。
接下来,我们分析运行结果:
# JMH version: 1.21
# VM version: JDK 1.8.0_161, Java HotSpot(TM) 64-Bit Server VM, 25.161-b12
# VM invoker: C:\Program Files\Java\jdk1.8.0_161\jre\bin\java.exe
# VM options: -javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.1.3\lib\idea_rt.jar=18856:C:\Program Files\JetBrains\IntelliJ IDEA 2019.1.3\bin -Dfile.encoding=UTF-8
# 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: Throughput, ops/time
# Benchmark: com.sht.study.jmh.T001HelloWorld.testNoting
# Run progress: 0.00% complete, ETA 00:01:40
# Fork: 1 of 1
# Warmup Iteration 1: 3561497106.539 ops/s
# Warmup Iteration 2: 3717422700.770 ops/s
# Warmup Iteration 3: 3673042896.880 ops/s
# Warmup Iteration 4: 3675741454.988 ops/s
# Warmup Iteration 5: 3688327201.989 ops/s
Iteration 1: 3702673032.227 ops/s
Iteration 2: 3603146922.577 ops/s
Iteration 3: 3441450888.242 ops/s
Iteration 4: 3804941548.309 ops/s
Iteration 5: 3622635812.187 ops/s
Result "com.sht.study.jmh.T001HelloWorld.testNoting":
3634969640.709 ±(99.9%) 516935555.554 ops/s [Average]
(min, avg, max) = (3441450888.242, 3634969640.709, 3804941548.309), stdev = 134246522.178
CI (99.9%): [3118034085.154, 4151905196.263] (assumes normal distribution)
# Run complete. Total time: 00:01:41
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
T001HelloWorld.testNoting thrpt 5 3634969640.709 ± 516935555.554 ops/s
Process finished with exit code 0
第一段内容展示测试的基本信息,如JDK的版本及JVM参数等。
还有如下与该测试强相关的配置信息:
Warmup: 5 iterations, 10 s each - - 代表五个热身迭代,每个迭代10固定耗时10秒。
Measurement: 5 iterations, 10 s each --代表五个测试迭代,每个迭代固定耗时10秒。
Timeout: 10 min per iteration --${代表每个迭代的超时时间为10分钟(不是固定10秒钟吗,超时是啥意思)
Threads: 1 thread, will synchronize iterations --使用一个线程,串行每个迭代
Benchmark mode: Throughput, ops/time --${color}${red}${测试模式}是吞吐量
Benchmark: com.sht.study.jmh.T001HelloWorld.testNoting --执行的方法
Run progress: 0.00% complete, ETA 00:01:40 --本次运行的进展,
ETA是什么意思
Fork: 1 of 1 --启动一个jvm,当前是第一个
--预热迭代及其吞吐量,预热的目的是让Java虚拟机对测试代码进行足够多的优化,比如在预热后被预测代码应该得到了充分的
JIT编译和优化
Warmup Iteration 1: 3561497106.539 ops/s
Warmup Iteration 2: 3717422700.770 ops/s
Warmup Iteration 3: 3673042896.880 ops/s
Warmup Iteration 4: 3675741454.988 ops/s
Warmup Iteration 5: 3688327201.989 ops/s
--测试迭代及其吞吐量
Iteration 1: 3702673032.227 ops/s
Iteration 2: 3603146922.577 ops/s
Iteration 3: 3441450888.242 ops/s
Iteration 4: 3804941548.309 ops/s
Iteration 5: 3622635812.187 ops/s
--测试结果汇总,最大、最小、标准差\总耗时等,
平均值、stdev,CI
Result "com.sht.study.jmh.T001HelloWorld.testNoting":
3414697939.411 ±(99.9%) 346560991.541 ops/s [Average]
(min, avg, max) = (3259941224.808, 3414697939.411, 3481613462.859), stdev = 90000788.952
CI (99.9%): [3068136947.870, 3761258930.952] (assumes normal distribution)
Run complete. Total time: 00:01:40
--下面一段话的意思是,数据只是数据,我们需要明白这些数据产生的原理,才能更好地利用他们。
尤其不能武断地认为这些数据就应该如此而应该使用~~profilers ~~、析因实验、执行提供实验控制的极限测试和阴性实验、确保基准环境在jvm/os/hw级别是安全的,并征求领域专家的意见(等各种手段来确保数据的可靠性)。
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
T001HelloWorld.testNoting thrpt 5 3414697939.411 ± 346560991.541 ops/s
JMH关键概念
JMH虽然看起来简单,但是真要用的好还是非常有挑战的,我们需要了解一下各种概念和机制(逐步补充)
- 模式
- 状态
- 伪共享优化
- 循环优化
浙公网安备 33010602011771号