什么是JMH

​ JMH,即Java Microbenchmark Harness,Java平台下的一套微基准测试工具。如果我们需要测试API性能的话,就可以用上这个工具,所以它并不是取代单元测试的。它可以在开发阶段提供性能参考标准,不过这并不代表线上的性能表现,不同的硬件和操作系统也会带来性能差异,所以最终还是需要上到测试或沙箱环境,让测试人员进行压测。

为什么需要JMH

​ 在了解JMH之前,如果需要性能测试,我们通常会使用for循环,或者JMeter。而JMH正是比for循环严谨,比JMeter使用简单的测试工具。

​ 再者,不知道你注意过没有,在使用for循环测试时,第一次或者头几次运行总是最慢的,越到后面越快。从《计算机组成与设计 硬件软件接口》一书中可以了解到,从更底层讲,Java是解释型的语言。虽然Java也需要编译,但是编译后只是字节码,还需要JVM解释成对应宿主机的机器码。解释的优势是可移植性,但是性能较差。在20世纪80和90年代,虽然解释型语言的性能也飞速提升,但是与C语言相比,仍有10倍的性能差距。

为了保持可移植性,同时又提高性能,Java便开发了即时编译器(Just In Time complier),其通过记录运行的程序来找到所谓的“热点”方法,然后将它们直接编译成宿主机的指令序列,即不通过JVM解释那一层。这样以后该方法的运行就会更快。

​ 看到这里也就明白了,为什么程序越到后面就会越快。JMH在真正的测试之前会预热程序,而且还可以通过配置进程数、线程数等参数来使程序更接近实际的运行状况。

如何使用

​ 首先引入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>test</scope>
</dependency>

​ 本案例中,我写了一个简单的小程序,它会从指定目录读取文件夹内容(每行一个数字),然后会对取出来的数字进行排序。排序算法选择了插入排序和归并排序,我们通过基准测试来看看两者的性能差距。

​ 读取文件内容

public class ReadFile {
public static int[] readInteger(String path){
try(BufferedReader in = new BufferedReader(new FileReader(path));) {
List<Integer> temp = new ArrayList<>();
String str;
while ((str = in.readLine()) != null) {
temp.add(Integer.parseInt(str));
}
int[] result = new int[temp.size()];
for(int i=0;i<temp.size();i++){
result[i]=temp.get(i);
}
return result;
} catch (Exception e) {
e.printStackTrace();
return new int[0];
}
}
}

​ 两个排序算法就不贴了,网上可以搜到很多。实际的开发可能会用上SpringBoot,所以还得与Junit整合,并使用自动注入功能。先直接贴上测试代码:

@BenchmarkMode(Mode.All)
@Warmup(iterations = 3)//预热轮数
@Measurement(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS)
@Threads(8)//线程数
@Fork(0) //fork的次数,如果想用Autowired自动注入,这个填0
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
@RunWith(SpringRunner.class) //整合SpringBoot的测试运行环境
@SpringBootTest
public class JHMTest {
//想用自动注入功能,对象必须是静态的,fork填0
private static SortService service; @Autowired
void setService(SortService service){
JHMTest.service =service;
} @Test
public void executeBenchmark() throws RunnerException {
//JMH的选项配置,除了上面的注解方式的配置,也可以直接在这个Options里面配置。
//其中/Users/xxxx/Desktop/Benchmark.json是结果的输出文件
Options options = new OptionsBuilder().include(this.getClass().getSimpleName())
.output("/Users/xxxx/Desktop/Benchmark.json").build();
new Runner(options).run();
} @Benchmark
public void insertSortTest(){
int[] arr = ReadFile.readInteger("/Users/xxxxx/Desktop/test.txt");
service.insertSort(arr);
} @Benchmark
public void mergeSortTest(){
int[] arr = ReadFile.readInteger("/Users/xxxxx/Desktop/test 2.txt");
service.mergeSort(arr);
}
}

​ 上面注释简单写了几个关键点,我们执行executeBenchmark方法,JMH就会执行该类下带有Benchmark注解的方法。最终结果会输出到指定文件中

​ 其他注解的解释可见图

截图截取自:https://dunwu.github.io/javatech/test/jmh.html#jmh-api

结果查看

​ 打开结果文件,前面一大坨是系统信息,可以简单看看,直接拉到最后,结果如下:

Benchmark                                        Mode     Cnt    Score   Error   Units
JHMTest.insertSortTest thrpt 129.302 ops/ms
JHMTest.mergeSortTest thrpt 122.224 ops/ms
JHMTest.insertSortTest avgt 0.065 ms/op
JHMTest.mergeSortTest avgt 0.066 ms/op
JHMTest.insertSortTest sample 122410 0.066 ± 0.002 ms/op
JHMTest.insertSortTest:insertSortTest·p0.00 sample 0.014 ms/op
JHMTest.insertSortTest:insertSortTest·p0.50 sample 0.050 ms/op
JHMTest.insertSortTest:insertSortTest·p0.90 sample 0.106 ms/op
JHMTest.insertSortTest:insertSortTest·p0.95 sample 0.120 ms/op
JHMTest.insertSortTest:insertSortTest·p0.99 sample 0.192 ms/op
JHMTest.insertSortTest:insertSortTest·p0.999 sample 0.492 ms/op
JHMTest.insertSortTest:insertSortTest·p0.9999 sample 11.891 ms/op
JHMTest.insertSortTest:insertSortTest·p1.00 sample 17.334 ms/op
JHMTest.mergeSortTest sample 122055 0.066 ± 0.002 ms/op
JHMTest.mergeSortTest:mergeSortTest·p0.00 sample 0.014 ms/op
JHMTest.mergeSortTest:mergeSortTest·p0.50 sample 0.050 ms/op
JHMTest.mergeSortTest:mergeSortTest·p0.90 sample 0.107 ms/op
JHMTest.mergeSortTest:mergeSortTest·p0.95 sample 0.121 ms/op
JHMTest.mergeSortTest:mergeSortTest·p0.99 sample 0.187 ms/op
JHMTest.mergeSortTest:mergeSortTest·p0.999 sample 0.457 ms/op
JHMTest.mergeSortTest:mergeSortTest·p0.9999 sample 11.957 ms/op
JHMTest.mergeSortTest:mergeSortTest·p1.00 sample 12.419 ms/op
JHMTest.insertSortTest ss 0.020 ms/op
JHMTest.mergeSortTest ss 0.020 ms/op

​ 结果如上,Mode中thrpt代表吞吐量,单位时间内的执行次数。avgt是平均时间,一次执行需要的单位时间。sample是基于采样的执行时间,采样频率由JMH自动控制。ss是单次执行的时间

​ 从结果上看,两种排序算法的性能相差无几,当然与我们的逻辑太简单也有关系。这次的分享就到这里,大家赶紧用到自己的项目中,测试一下吧。

更准确的测试Java程序性能——JMH基准测试的更多相关文章

  1. Java程序性能定位工具-火焰图

    Java程序性能定位工具-火焰图 前言 Java火焰图是一种新的查看CPU利用率方式.今天就带大家一起使用来自Google大神的工具来生成火焰图.火焰图非常的直观,问题一目了然,希望有一天它能成为JA ...

  2. Java程序性能优化技巧

    Java程序性能优化技巧 多线程.集合.网络编程.内存优化.缓冲..spring.设计模式.软件工程.编程思想 1.生成对象时,合理分配空间和大小new ArrayList(100); 2.优化for ...

  3. 《Java程序性能优化:让你的Java程序更快、更稳定》

    Java程序性能优化:让你的Java程序更快.更稳定, 卓越网更便宜,不错的书吧

  4. [JAVA] java程序性能优化

    一.避免在循环条件中使用复杂表达式 在不做编译优化的情况下,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循环条件值不变的话,程序将会运行的更快. 例子: import java.util ...

  5. Java程序性能优化——让你的java程序更快、更稳定

    1.Java性能调优概述 1.1.Web服务器,响应时间.吞吐量是两个重要的性能参数. 1.2.程序性能的几个表现: 执行速度:程序的反映是否迅速,响应时间是否足够短 内存分配:分配是否合理,是否过多 ...

  6. Java程序性能优化读书笔记(一):Java性能调优概述

    程序性能的主要表现点: 执行速度:程序的反映是否迅速,响应时间是否足够短 内存分配:内存分配是否合理,是否过多地消耗内存或者存在内存泄漏 启动时间:程序从运行到可以正常处理业务需要花费多少时间 负载承 ...

  7. Java程序性能优化之性能概述

    性能的基本概念 一).什么叫程序的性能? 程序运行所需的内存和时间. 二).性能的表现形式: 1).执行速度: 程序的反应是否迅速,响应时间是否足够短. 2).启动时间:程序从运行到可以处理正常业务所 ...

  8. java程序性能优化

    一.避免在循环条件中使用复杂表达式 在不做编译优化的情况下,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循环条件值不变的话,程序将会运行的更快. 例子: import java.util ...

  9. 《Java程序性能优化》学习笔记 设计优化

    豆瓣读书:http://book.douban.com/subject/19969386/ 第一章 Java性能调优概述 1.性能的参考指标 执行时间: CPU时间: 内存分配: 磁盘吞吐量: 网络吞 ...

随机推荐

  1. Linux初探之如何查看帮助文档自学命令

    linux命令种类繁多,参数各异,要每个都记住除非是过目不忘的神人,否则你只能记住常用的那几个,等到 要用时还是得靠--help,man,info这些命令去读文档,可是这些文档看起来也不那么直观,所以 ...

  2. ubuntu 查看系统信息

    1.系统信息 uname -a 显示linux的内核版本和系统是多少位的:X86_64代表系统是64位的. Linux field-ubuntu-18 4.15.0-20-generic #21-Ub ...

  3. git rebase和git merge的区别

    前言:    平时工作中发现一般同事在同步远程代码的时候都是用git pull,其实git pull包含有两个操作,一个是fetch远程的代码,一个是将本地当前的代码和远程代码进行merge,即git ...

  4. Maven无法导入插件,pom文件报错

    最近在使用IDEA导入开源项目bootshiro,更新依赖的时候,发现有些插件无法导入,以致于pom文件一直报找不到该插件的错误 一开始就网上各种百度,无论怎么更换阿里云的镜像都导不进,最后想着试试自 ...

  5. OpenCV 传统分割测试

    github官网源文件:https://github.com/opencv/opencv/tree/master/samples/python 最好是先克隆整个仓库下来,再测试里面的:floodfil ...

  6. C++小坑汇总

    std::vector::end, 是构想的下一个push_back位置,并不实际指向vector中任何元素. Returns an iterator referring to the past-th ...

  7. Python3-sqlalchemy-orm 回滚

    #-*-coding:utf-8-*- #__author__ = "logan.xu" import sqlalchemy from sqlalchemy import crea ...

  8. 类的基础语法阅读【Python3.8官网文档】

    英文官方文档: https://docs.python.org/3.8/tutorial/classes.html 中文官方文档: https://docs.python.org/zh-cn/3.8/ ...

  9. HashSet的存储原理

    HashSet的底层用哈希散列表来存储对象(默认长度为16的数组),假如: Set set=new HashSet(); set.add(obj); 内部存储过程为:定义h=obj.hashCode, ...

  10. 使用Apache poi来编写导出excel的工具类

    在JavaWeb开发的需求中,我们会经常看到导出excel的功能需求,然后java并没有提供操作office文档的功能,这个时候我们就需要使用额外的组件来帮助我们完成这项功能了. 很高兴Apache基 ...