写在前面: JMH指的是Java Microbenchmark Harness即为基准测试。本文简单介绍JMH的使用,只是作为一个入门,更详细的介绍最好参考官网:openjdk.java.net/projects/co…
JMH测试项目创建
- 添加依赖
jmh-core和jmh-generator-annprocess:
<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>test</scope>
</dependency>
- 安装idea安装插件:
- 允许JMH能够怼注解进行处理
compiler -> Annotation Processors -> Enable Annotation Processing
- 定义测试类
public class TestParallelStream {
static List<Integer> nums = new ArrayList<>();
static {
Random r = new Random();
for (int i = 0; i < 1000; i++) {
nums.add(1000000 + r.nextInt(1000000));
}
}
static void foreach() {
nums.forEach(TestParallelStream::isPrime);
}
static void parallel() {
nums.parallelStream().forEach(TestParallelStream::isPrime);
}
static boolean isPrime(int num) {
for (int i = 2; i <= num / 2; i++) {
if (num % i == 0) {
return false;
}
}
return true;
}
}
`
5. 单元测试
```java
@Benchmark
@Warmup(iterations = 5, time = 3)
@Fork(5)
@BenchmarkMode(Mode.Throughput)
@Measurement(iterations = 5, time = 3)
public void testParallel() {
TestParallelStream.parallel();
}
@Warmup(iterations = 5, time = 3):表示调用5次,每次调用间隔3秒。预热后期望由动态编译转变为JIT,这样可以更好地模拟线上环境;@Fork(5):5个线程去执行程序;@BenchmarkMode(Mode.Throughput):基准测试模式,此处为吞吐量模式。该配置模式如下:Throughput:吞吐量,即QPS或日志中的ops/timeAverageTime:平均调用时间,即每次调用平均耗时SampleTime:随机取样,最后输出取样结果分布。例如百分之多少的调用再多少毫秒内完成等等;SingleShotTime:以上模式都是默认一次iteration是1s,唯有SingleShotTime是只运行一次,往往同时把@Warmup次数设为0,用于测试冷启动时的性能。
@Measurement(iterations = 5, time = 3):整个测试需要测试5次,每次间隔3秒;
- Windows系统注意事项
老版本的idea运行单元测试时可能会出现如下错误:
ERROR: org.openjdk.jmh.runner.RunnerException: ERROR: Exception while trying to acquire the JMH lock (C:\WINDOWS\/jmh.lock): C:\WINDOWS\jmh.lock (拒绝访问。), exiting. Use -Djmh.ignoreLock=true to forcefully continue.
at org.openjdk.jmh.runner.Runner.run(Runner.java:216)
at org.openjdk.jmh.Main.main(Main.java:71)
需要通过如下配置:
RunConfiguration -> Environment Variables -> include system environment viables
阅读测试报告
# JMH version: 1.21
# VM version: JDK 11.0.13, Java HotSpot(TM) 64-Bit Server VM, 11.0.13+10-LTS-370
# VM invoker: D:\Work\Program\java11\bin\java.exe
# VM options: -javaagent:D:\Work\Program\idea\IntelliJ IDEA 2021.2.2\lib\idea_rt.jar=63840:D:\Work\Program\idea\IntelliJ IDEA 2021.2.2\bin -Dfile.encoding=UTF-8
# Warmup: 5 iterations, 3 s each // 预热5次,每次间隔3秒
# Measurement: 5 iterations, 3 s each // 测试5次,每次间隔3秒
# Timeout: 10 min per iteration // 每次运行需要在10分钟以内
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time // 测试模式为吞吐量
# Benchmark: org.wzy.jmh.ParallelTest.testParallel // 测试代码
# Run progress: 0.00% complete, ETA 00:02:30 // 第一个线程
# Fork: 1 of 5
// 预热时吞吐量
# Warmup Iteration 1: 61.897 ops/s
# Warmup Iteration 2: 61.725 ops/s
# Warmup Iteration 3: 61.163 ops/s
# Warmup Iteration 4: 61.048 ops/s
# Warmup Iteration 5: 62.912 ops/s
// 测试时吞吐量
Iteration 1: 64.143 ops/s
Iteration 2: 62.319 ops/s
Iteration 3: 63.610 ops/s
Iteration 4: 63.671 ops/s
Iteration 5: 63.535 ops/s
# Run progress: 20.00% complete, ETA 00:02:03 // 第二个线程
# Fork: 2 of 5
# Warmup Iteration 1: 76.065 ops/s
# Warmup Iteration 2: 76.243 ops/s
# Warmup Iteration 3: 76.381 ops/s
# Warmup Iteration 4: 76.604 ops/s
# Warmup Iteration 5: 75.955 ops/s
Iteration 1: 75.314 ops/s
Iteration 2: 76.458 ops/s
Iteration 3: 76.929 ops/s
Iteration 4: 76.739 ops/s
Iteration 5: 76.487 ops/s
# Run progress: 40.00% complete, ETA 00:01:32 // 第三个线程
# Fork: 3 of 5
# Warmup Iteration 1: 70.835 ops/s
# Warmup Iteration 2: 71.576 ops/s
# Warmup Iteration 3: 70.918 ops/s
# Warmup Iteration 4: 68.401 ops/s
# Warmup Iteration 5: 67.818 ops/s
Iteration 1: 71.080 ops/s
Iteration 2: 68.981 ops/s
Iteration 3: 68.563 ops/s
Iteration 4: 69.042 ops/s
Iteration 5: 68.757 ops/s
# Run progress: 60.00% complete, ETA 00:01:01 // 第四个线程
# Fork: 4 of 5
# Warmup Iteration 1: 69.396 ops/s
# Warmup Iteration 2: 70.910 ops/s
# Warmup Iteration 3: 70.832 ops/s
# Warmup Iteration 4: 70.124 ops/s
# Warmup Iteration 5: 69.749 ops/s
Iteration 1: 70.402 ops/s
Iteration 2: 70.044 ops/s
Iteration 3: 70.150 ops/s
Iteration 4: 69.973 ops/s
Iteration 5: 70.583 ops/s
# Run progress: 80.00% complete, ETA 00:00:30 // 第五个线程
# Fork: 5 of 5
# Warmup Iteration 1: 60.714 ops/s
# Warmup Iteration 2: 60.338 ops/s
# Warmup Iteration 3: 59.768 ops/s
# Warmup Iteration 4: 59.520 ops/s
# Warmup Iteration 5: 58.724 ops/s
Iteration 1: 59.783 ops/s
Iteration 2: 59.664 ops/s
Iteration 3: 59.773 ops/s
Iteration 4: 59.951 ops/s
Iteration 5: 60.768 ops/s
Result "org.wzy.jmh.ParallelTest.testParallel":
67.869 ±(99.9%) 4.371 ops/s [Average]
(min, avg, max) = (59.664, 67.869, 76.929), stdev = 5.835
CI (99.9%): [63.498, 72.240] (assumes normal distribution)
# Run complete. Total time: 00:02:34
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
ParallelTest.testParallel thrpt 25 67.869 ± 4.371 ops/s
Process finished with exit code 0
本文只是一个JMH的基础入门,官网有更详细的案例可供参考:
hg.openjdk.java.net/code-tools/…