【基准测试】JMH 简单入门
什么是 JMH
JMH 是 Java Microbenchmark Harness 的缩写。中文意思大致是 “JAVA 微基准测试套件”。首先先明白什么是“基准测试”。百度百科给的定义如下:
①、可重复性:可进行重复性的测试,这样做有利于比较每次的测试结果,得到性能结果的长期变化趋势,为系统调优和上线前的容量规划做参考。
②、可观测性:通过全方位的监控(包括测试开始到结束,执行机、服务器、数据库),及时了解和分析测试过程发生了什么。
③、可展示性:相关人员可以直观明了的了解测试结果(web界面、仪表盘、折线图树状图等形式)。
④、真实性:测试的结果反映了客户体验到的真实的情况(真实准确的业务场景+与生产一致的配置+合理正确的测试方法)。
⑤、可执行性:相关人员可以快速的进行测试验证修改调优(可定位可分析)。
可见要做一次符合特质的基准测试,是很繁琐也很困难的。外界因素很容易影响到最终的测试结果。特别对于 JAVA的基准测试。
有些文章会告诉我们 JAVA是 C++编写的,一般来说 JAVA编写的程序不太可能比 C++编写的代码运行效率更好。但是JAVA在某些场景的确要比 C++运行的更高效。不要觉得天方夜谭。其实 JVM随着这些年的发展已经变得很智能,它会在运行期间不断的去优化。
这对于我们程序来说是好事,但是对于性能测试就头疼的。你运行的次数与时间不同可能获得的结果也不同,很难获得一个比较稳定的结果。对于这种情况,有一个解决办法就是大量的重复调用,并且在真正测试前还要进行一定的预热,使结果尽可能的准确。
除了这些,对于结果我们还需要一个很好的展示,可以让我们通过这些展示结果判断性能的好坏。
而这些JMH都有!😊
如何使用 JMH
下面我们以字符串拼接的几种方法为例子使用JMH做基准测试。
1. 导入依赖
JMH是 JDK9自带的,如果你是 JDK9 之前的版本也可以通过导入 openjdk
<dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-core</artifactId> <version>1.19</version> </dependency> <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-generator-annprocess</artifactId> <version>1.19</version> </dependency>
2. 目录结构
. ├── pom.xml └── src ├── main │ └── java │ └── cn │ └── coder4j │ └── study │ └── demo │ └── jmh │ ├── benchmark │ │ └── StringConnectBenchmark.java │ └── runner │ └── StringBuilderRunner.java └── test └── java └── cn └── coder4j └── study └── demo
3. 具体代码
- StringBuilderRunner.java
/** * coder4j.cn * Copyright (C) 2013-2018 All Rights Reserved. */ package cn.coder4j.study.demo.jmh.runner; import cn.coder4j.study.demo.jmh.benchmark.StringConnectBenchmark; import org.openjdk.jmh.annotations.Mode; 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; /** * @author buhao * @version StringBuilderRunner.java, v 0.1 2018-12-25 09:53 buhao */ public class StringBuilderRunner { public static void main( String[] args ) throws RunnerException { Options opt = new OptionsBuilder() // 导入要测试的类 .include(StringConnectBenchmark.class.getSimpleName()) // 预热5轮 .warmupIterations(5)