点击上方蓝色字体,选择“标星公众号”
优质文章,第一时间送达
关注公众号后台回复pay或mall获取实战项目资料视频
作者:Richard_Yi
来源juejin.cn/post/6844903936869007368
前言
If you cannot measure it, you cannot improve it.
在日常开发中,我们对一些代码的调用或者工具的使用会存在多种选择方式,在不确定他们性能的时候,我们首先想要做的就是去测量它。大多数时候,我们会简单的采用多次计数的方式来测量,来看这个方法的总耗时。
但是,如果熟悉JVM类加载机制的话,应该知道JVM默认的执行模式是JIT编译与解释混合执行。JVM通过热点代码统计分析,识别高频方法的调用、循环体、公共模块等,基于JIT动态编译技术,会将热点代码转换成机器码,直接交给CPU执行。
也就是说,JVM会不断的进行编译优化,这就使得很难确定重复多少次才能得到一个稳定的测试结果?所以,很多有经验的同学会在测试代码前写一段预热的逻辑。
JMH,全称 Java Microbenchmark Harness (微基准测试框架),是专门用于Java代码微基准测试的一套测试工具API,是由 OpenJDK/Oracle 官方发布的工具。何谓 Micro Benchmark 呢?简单地说就是在 method 层面上的 benchmark,精度可以精确到微秒级。
Java的基准测试需要注意的几个点:
测试前需要预热。
防止无用代码进入测试方法中。
并发测试。
测试结果呈现。
JMH的使用场景:
1.定量分析某个热点函数的优化效果
2.想定量地知道某个函数需要执行多长时间,以及执行时间和输入变量的相关性
3.对比一个函数的多种实现方式
DEMO 演示
这里先演示一个DEMO,让不了解JMH的同学能够快速掌握这个工具的大概用法。
1. 测试项目构建
JMH是内置Java9及之后的版本。这里是以Java8进行说明。
为了方便,这里直接介绍使用maven构建JMH测试项目的方式。
第一种是使用命令行构建,在指定目录下执行以下命令:
$ mvn archetype:generate \
-DinteractiveMode=false \
-DarchetypeGroupId=org.openjdk.jmh \
-DarchetypeArtifactId=jmh-java-benchmark-archetype \
-DgroupId=org.sample \
-DartifactId=test \
-Dversion=1.0
对应目录下会出现一个test项目,打开项目后我们会看到这样的项目结构。第二种方式就是直接在现有的maven项目中添加jmh-core和jmh-generator-annprocess的依赖来集成JMH。
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${jmh.version}</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
<scope>provided</scope>
</dependency>
2. 编写性能测试
这里我以测试LinkedList 通过index 方式迭代和foreach 方式迭代的性能差距为例子,编写测试类,涉及到的注解在之后会讲解,
/**
* @author Richard_yyf
* @version 1.0 2019/8/27
*/
@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.SECONDS)
@Threads(Threads.MAX)
public class LinkedListIterationBenchMark {
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
@BenchmarkMode(Mode.Throughput)
public void forIndexIterate() {
for (int i = 0; i < list.size(); i++) {
list.get(i);
System.out.print("");
}
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
public void forEachIterate() {
for (String s : list) {
System.out.print("");
}
}
}
3. 执行测试
运行 JMH 基准测试有两种方式,一个是生产jar文件运行,另一个是直接写main函数或者放在单元测试中执行。
生成jar文件的形式主要是针对一些比较大的测试,可能对机器性能或者真实环境模拟有一些需求,需要将测试方法写好了放在linux环境执行。具体命令如下
$ mvn clean install
$ java -jar target/benchmarks.jar
我们日常中遇到的一般是一些小测试,比如我上面写的例子,直接在IDE中跑就好了。启动方式如下:
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(LinkedListIterationBenchMark.class.getSimpleName())
.forks(1)
.warmupIterations(2)
.measurementIterations(2)
.output("E:/Benchmark.log")
.build();
new Runner(opt).run();
}
4. 报告结果
输出结果如下
最后的结果:
Benchmark Mode Cnt Score Error Units
LinkedListIterationBenchMark.forEachIterate thrpt 2 1192.380 ops/s
LinkedListIterationBenchMark.forIndexIterate thrpt 2 206.866 ops/s
整个过程:
# Detecting actual CPU count: 12 detected
# JMH version: 1.21
# VM version: JDK 1.8.0_131, Java HotSpot(TM) 64-Bit Server VM, 25.131-b11
# VM invoker: C:\Program Files\Java\jdk1.8.0_131\jre\bin\java.exe
# VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.2\lib\idea_rt.jar=65175:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.2\bin -Dfile.encoding=UTF-8
# Warmup: 2 iterations, 10 s each
# Measurement: 2 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 12 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.sample.jmh.LinkedListIterationBenchMark.forEachIterate
# Run progress: 0.00% complete, ETA 00:01:20
# Fork: 1 of 1
# Warmup Iteration 1: 1189.267 ops/s
# Warmup Iteration 2: 1197.321 ops/s
Iteration 1: 1193.062 ops/s
Iteration 2: 1191.698 ops/s
Result "org.sample.jmh.LinkedListIterationBenchMark.forEachIterate":
1192.380 ops/s
# JMH version: 1.21
# VM version: JDK 1.8.0_131, Java HotSpot(TM) 64-Bit Server VM, 25.131-b11
# VM invoker: C:\Program Files\Java\jdk1.8.0_131\jre\bin\java.exe
# VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.2\lib\idea_rt.jar=65175:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.2\bin -Dfile.encoding=UTF-8
# Warmup: 2 iterations, 10 s each
# Measurement: 2 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 12 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.sample.jmh.LinkedListIterationBenchMark.forIndexIterate
# Run progress: 50.00% complete, ETA 00:00:40
# Fork: 1 of 1
# Warmup Iteration 1: 205.676 ops/s
# Warmup Iteration 2: 206.512 ops/s
Iteration 1: 206.542 ops/s
Iteration 2: 207.189 ops/s
Result "org.sample.jmh.LinkedListIterationBenchMark.forIndexIterate":
206.866 ops/s
# Run complete. Total time: 00:01:21
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
LinkedListIterationBenchMark.forEachIterate thrpt 2 1192.380 ops/s
LinkedListIterationBenchMark.forIndexIterate thrpt 2 206.866 ops/s
注解介绍
下面我们来详细介绍一下相关的注解
@BenchmarkMode
微基准测试类型。JMH 提供了以下几种类型进行支持:可以注释在方法级别,也可以注释在类级别,
@BenchmarkMode(Mode.All)
public class LinkedListIterationBenchMark {
...
}
@Benchmark
@BenchmarkMode({Mode.Throughput, Mode.SingleShotTime})
public void m() {
...
}
@Warmup
这个单词的意思就是预热,iterations = 3就是指预热轮数。
@Benchmark
@BenchmarkMode({Mode.Throughput, Mode.SingleShotTime})
@Warmup(iterations = 3)
public void m() {
...
}
@Measurement
正式度量计算的轮数。
iterations 进行测试的轮次
time 每轮进行的时长
timeUnit时长单位
精彩推荐
1.Java8中一个极其强悍的新特性,很多人没用过(非常实用)
2.100多个免费常用API接口分享,调用完全不限次数,以后总用得着!
3.Java 8 中的这个接口真好用!炸了!
4.还在用策略模式解决 if-else?Map+函数式接口方法才是YYDS!
5.Maven官宣:干掉Maven和Gradle!推出更强更快更牛逼的新一代构建工具,炸裂!
6.Docker 大势已去,Podman 崛起
7.为什么阿里规范需要在 @Transactional 事务注解中指定 rollbackFor?
8.新来的CTO强制规定所有接口都用 POST请求。。。
9.Nginx从安装到高可用,一篇搞定!
点个在看少个 bug