别再写main方法测试了,太Low!这才是专业Java测试方法
在日常开发中,我们对一些代码的调用或者工具的使用会存在多种选择方式,在不确定他们性能的时候,我们首先想要做的就是去测量它。大多数时候,我们会简单的采用多次计数的方式来测量,来看这个方法的总耗时。
但是,如果熟悉JVM类加载机制的话,应该知道JVM默认的执行模式是JIT编译与解释混合执行。JVM通过热点代码统计分析,识别高频方法的调用、循环体、公共模块等,基于JIT动态编译技术,会将热点代码转换成机器码,直接交给CPU执行。
也就是说,JVM会不断的进行编译优化,这就使得很难确定重复多少次才能得到一个稳定的测试结果?所以,很多有经验的同学会在测试代码前写一段预热的逻辑。
JMH,全称 Java Microbenchmark Harness (微基准测试框架),是专门用于Java代码微基准测试的一套测试工具API,是由 OpenJDK/Oracle 官方发布的工具。何谓 Micro Benchmark 呢?简单地说就是在 method 层面上的 benchmark,精度可以精确到微秒级。
Java的基准测试需要注意的几个点: 测试前需要预热。 防止无用代码进入测试方法中。 并发测试。 测试结果呈现。
JMH的使用场景: 定量分析某个热点函数的优化效果 想定量地知道某个函数需要执行多长时间,以及执行时间和输入变量的相关性 对比一个函数的多种实现方式
本篇主要是介绍JMH的DEMO演示,和常用的注解参数。希望能对你起到帮助。 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。 org.openjdk.jmh jmh-core ${jmh.version} org.openjdk.jmh jmh-generator-annprocess ${jmh.version} provided 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 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 FilesJavajdk1.8.0_131jrebinjava.exe # VM options: -javaagent:D:Program FilesJetBrainsIntelliJ IDEA 2018.2.2libidea_rt.jar=65175:D:Program FilesJetBrainsIntelliJ IDEA 2018.2.2bin -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 FilesJavajdk1.8.0_131jrebinjava.exe # VM options: -javaagent:D:Program FilesJetBrainsIntelliJ IDEA 2018.2.2libidea_rt.jar=65175:D:Program FilesJetBrainsIntelliJ IDEA 2018.2.2bin -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 提供了以下几种类型进行支持:
类型描述 Throughput每段时间执行的次数,一般是秒 AverageTime平均时间,每次操作的平均耗时 SampleTime在测试中,随机进行采样执行的时间 SingleShotTime在每次执行中计算耗时 All所有模式
可以注释在方法级别,也可以注释在类级别, @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 时长单位 @Benchmark @BenchmarkMode({Mode.Throughput, Mode.SingleShotTime}) @Measurement(iterations = 3) public void m() { ... } @Threads
每个进程中的测试线程。 @Threads(Threads.MAX) public class LinkedListIterationBenchMark { ... } @Fork
进行 fork 的次数。如果 fork 数是3的话,则 JMH 会 fork 出3个进程来进行测试。 @Benchmark @BenchmarkMode({Mode.Throughput, Mode.SingleShotTime}) @Fork(value = 3) public void m() { ... } @OutputTimeUnit
基准测试结果的时间类型。一般选择秒、毫秒、微秒。 @OutputTimeUnit(TimeUnit.SECONDS) public class LinkedListIterationBenchMark { ... } @Benchmark
方法级注解,表示该方法是需要进行 benchmark 的对象,用法和 JUnit 的 @Test 类似。 @Param
属性级注解, @Param 可以用来指定某项参数的多种情况。特别适合用来测试一个函数在不同的参数输入的情况下的性能。 @Setup
方法级注解,这个注解的作用就是我们需要在测试之前进行一些 准备工作 ,比如对一些数据的初始化之类的。 @TearDown
方法级注解,这个注解的作用就是我们需要在测试之后进行一些 结束工作 ,比如关闭线程池,数据库连接等的,主要用于资源的回收等。 @State
当使用 @Setup 参数的时候,必须在类上加这个参数,不然会提示无法运行。
就比如我上面的例子中,就必须设置 state 。
State 用于声明某个类是一个"状态",然后接受一个 Scope 参数用来表示该状态的共享范围。因为很多 benchmark 会需要一些表示状态的类,JMH 允许你把这些类以依赖注入的方式注入到 benchmark 函数里。Scope 主要分为三种。 Thread: 该状态为每个线程独享。 Group: 该状态为同一个组里面所有线程共享。 Benchmark: 该状态在所有线程间共享。 启动方法
在启动方法中,可以直接指定上述说到的一些参数,并且能将测试结果输出到指定文件中, /** * 仅限于IDE中运行 * 命令行模式 则是 build 然后 java -jar 启动 * * 1. 这是benchmark 启动的入口 * 2. 这里同时还完成了JMH测试的一些配置工作 * 3. 默认场景下,JMH会去找寻标注了@Benchmark的方法,可以通过include和exclude两个方法来完成包含以及排除的语义 */ public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() // 包含语义 // 可以用方法名,也可以用XXX.class.getSimpleName() .include("Helloworld") // 排除语义 .exclude("Pref") // 预热10轮 .warmupIterations(10) // 代表正式计量测试做10轮, // 而每次都是先执行完预热再执行正式计量, // 内容都是调用标注了@Benchmark的代码。 .measurementIterations(10) // forks(3)指的是做3轮测试, // 因为一次测试无法有效的代表结果, // 所以通过3轮测试较为全面的测试, // 而每一轮都是先预热,再正式计量。 .forks(3) .output("E:/Benchmark.log") .build(); new Runner(opt).run(); } 结语
基于JMH可以对很多工具和框架进行测试,比如日志框架性能对比、BeanCopy性能对比 等,更多的example可以参考官方给出的JMH samples (https://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/)
杨晓月许凯B2C模式下跨境电子商务买卖合同的管辖权问题杨晓月华东政法大学法律硕士许凯华东政法大学副教授要目引言一跨境电商B2C买卖合同管辖权的困境二跨境电子商务买卖合同管辖权域外立法分析三跨境电商B2C买卖合同管辖权实施问题四我国跨境
别急,孩子变乖需要时间法国观点周刊网站8月11日刊登题为为何变乖需要时间的文章,作者是克莱尔勒菲弗。全文摘编如下你家的17岁青少年是否很难放下手机关掉视频结束游戏去做作业?这很正常!他之所以做不到自律,
中秋节发朋友圈祝福语1。明月圆圆降吉祥,中秋之夜人团圆。月饼圆圆传快乐,吃在嘴里比蜜甜。美酒佳肴桌摆满,欢声笑语赏月圆。举杯同庆心祝愿,健康快乐幸福远。中秋节快乐无限!2。人有悲欢离合,月有阴晴圆缺,
紫光展锐5G芯助力中兴远航30S开启全新体验近期上市的中兴远航30S5G手机搭载紫光展锐T760高清护眼大屏纯净安全系统从屏幕影像系统5G通信针对用户使用场景深化优化以越级体验造就千元档位的品质优选搭载展锐5G芯畅享超低功耗
动物死后都会重归自然,为何人类要打破自然规律,选择火化呢?世界上最大的生物蓝鲸,生于海洋也葬于海洋。在死去之后,尸体会沉入海底,形成一个以鲸鱼尸体为基础的循环系统,为那里生存的其他生物提供食物。在这种情况下,鲸落被称为是深海当中的绿洲。一
带着100元人民币,能在日本买到多少东西?在如今,随着经济水平不断发展的同时,物价水平也有所上涨,尤其是像北京上海广州这样的大城市,物价水平是十分高昂的,生活成本也是相当的高的,但是如果要是纵眼全世界的话,给全世界所有国家
关于九月的优美句子文案精选1九月,是一行诗,写着潭柘寺的钟声西湖的残虹,燕子走了,蟋蟀来了。2九月,渐浓秋意洗涤着浮躁的心绪。露白风清,心旷神怡。3九月,追逐一抹金黄,邂逅一捧温暖,珍藏一份遇见。4有的惊艳
湖南女子做的晚餐走红朋友圈,花钱不多营养美味,家人吃得真香对于一个丈夫来说,家中有一个勤俭持家还会做饭的妻子是多么幸福的一件事,每天下班回到家,就能吃到美味的饭菜,一天的疲惫都会抛到脑后。湖南一女子李雪做的晚餐走红朋友圈,花钱不多营养美味
大屏质感全都要!中国移动NZONE50Pro评测NZONE大家有听过吗,是中国移动面向Z世代人群打造的全新的终端品牌,推出的多款5G手机,不但外观潮美,更有科技范儿十足的内涵。下面我们就来了解一下这款手机究竟有何特殊之处。外观设
若觉生活太苦,不妨读读麦家的人生海海,讲透了人的一生文芳草屋图来源网络,与文无关,侵删麦家老师在人生海海中说人生海海,敢死不叫勇气,活着才需要勇气。你要替我记住这句话。董卿说,这部小说戳中了她内心最柔软的部分。上校的人生沉浮让人感慨
寒潮来了不抖腿,小米这个性价比好物爆火炎热的40大热天逐渐过去,随着秋季一同到来的还有逐步下降的气温,已经有人开始提前准备取暖设备。日前小米推出了一款米家立式暖风机Lite,支持3秒即热70广角送风,主打恒温低噪。据介