JMH – 实践与用法详解

背景

”If you cannot measure it, you cannot improve it“

在日常开发中,我们对一些代码的调用或者工具的使用会存在多种选择,在不确定他们性能的时候,我们首先想要做的就是先去度量它。大多时候,我们会采用简单的结束时间-开始时间进行测量,这个方法通常不够准确和科学,因为没有经过大量的模拟或者测试。随着JMH的出现,可以很大程度帮助了开发者,为决策提供主要的科学依据以及数据支撑。

JMH 应用场景

  1. 评估一个方法的不同实现,当你想对比两种不同的数据结构哪种性能更好。
  2. 评估第三方库的执行性能,当你想比较两种不同的工具包的实现哪个更优(比如Jackson和Gson实现)。
  3. 度量方法的执行耗时及输入的相关性,当你找到项目热点方法或者代码,想对其进一步进行优化时,也可以使用JMH进行定量分析;

JMH 快速开始

增加Maven依赖

`<!--jmh 基准测试 -->`
`<dependency>`
    `<groupId>org.openjdk.jmh</groupId>`
    `<artifactId>jmh-core</artifactId>`
    `<version>1.23</version>`
`</dependency>`
`<dependency>`
    `<groupId>org.openjdk.jmh</groupId>`
    `<artifactId>jmh-generator-annprocess</artifactId>`
    `<version>1.23</version>`
    `<scope>provided</scope>`
`</dependency>`

代码示例

/** 基准模式 {@link Mode} */
@BenchmarkMode(Mode.AverageTime)
// 输出的时间单位
@OutputTimeUnit( TimeUnit.MILLISECONDS )
// 进程数,默认1即可
@Fork(1)
// 预热次数、时长/次,目前配置为:预热五次,每次1秒
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
// 测试次数、时长/次,目前为:执行六次,每次1秒
@Measurement(iterations = 6,time = 1, timeUnit = TimeUnit.SECONDS)
public class TrieBenchmark {

   //基准测试
    @Benchmark
    public void trieInsert_10000() throws Exception {
        Trie trie = new Trie();
        for (int i = 0; i < 10000 ; i++) {
      Thread.sleep(1000);
        }
    }

    public static void main(String[] args) throws RunnerException{
        Options options = new OptionsBuilder()
                .include(TrieBenchmark.class.getName())
                .resultFormat(ResultFormatType.CSV)
                .build()
;
        new Runner(options).run();
    }
}

JMH详解

什么是JMH

JMH全称 Java Microbenchmark Harness,用于构建、运行和分析以Java和其他基于JVM的其他语言编写的 nano/micro/milli/macro 基准测试的Java工具。

“JMH is a Java harness for building, running, and analysing nano/micro/milli/macro benchmarks written in Java and other languages targetting the JVM.” —官方介绍

相关概念

  • BeachMark:基准测试,主要用来测试一些方法的性能,可以根据不同的参数以不同的单位进行计算(可以使用平均时间作为单位,也可以使用吞吐量作为单位,可以在BenchmarkMode值进行调整)。
  • MIcro Benchmark:简单地说就是在method层面上的benchmark,精度可以精确到微秒级。
  • OPS,Operation Per Second:每秒操作量,是衡量性能的重要指标,数值的性能越好。类似的有:TPS、QPS。
  • Throughput:吞吐量。
  • Warmup:预热,因为JVM的JIT机制的存储,如果某个函数被调用多次之后,JVM会尝试将其编译称为机器码从而提高执行速度。为了让结果更加接近真实情况就需要进行预热。JMH - 实践与用法详解

注解详细介绍

注解 注解说明 使用方法
@BenchmarkMode 表示JMH进行Bechmark时使用的模式,测量维度不同与测量的方式不同 目前有四种模式:1.Throughput:吞吐量模式,例如”1秒内可以执行多少次调用“,单位是操作数/时间。
2.AverageTime:调用的平均时间,例如”每次调用平均耗时x毫秒“,单位是时间/操作数。
3.SampleTime,随机采样,最后输出采样结果分布,例如”99%的调用在xx毫秒以内“。
4.SingleShotTime:以上模式都是默认一次iteration是1s,唯有SingleShotTime只运行一次。往往把warmup次数设置为0,用于冷启动性能。
@OutputTimeUnit 输出的时间单位 即统计时的时间单位
@Fork 进程数,默认1即可 用于多进程同时基准测试
@Warmup 在基准测试前先进行的预热行为 为了让基准测试结果更加接近真实情况,@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
即预热5次数、每次1秒
@Measurement 基准测试阶段 指定迭代次数与运行时间。@Measurement(iterations = 6,time = 1, timeUnit = TimeUnit.SECONDS)
即执行六次,每次1秒
@Threads Fork面向进程,Threads则是面向线程。 如果配置Threads.MAX,则使用和处理器核数相同的线程数
@Benchmark 基准测试方法注解 表示该方法是需要进行基准测试的对象
@Param 只能修饰字段,用来测试不同的参数对程序性能的影响,可以配合@State使用 常用于多参数基准测试,可以生成不同参数的直接结果分析
@Setup 用于初始化 和单元测试JUnit类似,用于基准测试前的初始化动作
@TearDown 基准测试后动作 用于资源释放或者资源回收
@State 共享范围 用于表示状态共享范围,Benchmark(默认)、Thread,Group三种值

图形化结果分析

使用JMH测试的结果,可以二次加工,进行图形化展示。结合图表数据,更加直观。通过运行时,指定输出的格式文件,即可获得相应格式的性能测试结果。

比如下面这行代码,就是指定输出JSON格式的数据。

Options opt = new OptionsBuilder()  
    .resultFormat(ResultFormatType.JSON)  
    .build();

JMH支持以下5种格式的结果:

  • TEXT 导出文本文件。
  • CSV 导出csv格式文件。
  • SCSV 导出scsv等格式的文件。
  • JSON 导出成json文件。
  • LATEX 导出到latex,一种基于ΤΕΧ的排版系统。

一般来说,我们导出成CSV文件,直接在Excel中操作,生成相应的图形就可以了。

jmh_excel.pngJMH - 实践与用法详解

另外介绍几个可以做图的工具:

JMH Visualizer这里有一个开源的项目(https://jmh.morethan.io/) ,通过导出json文件,上传之后,可得到简单的统计结果。个人认为它的展示方式并不是很好。

jmh-visual-chart

相比较而言,下面这个工具(http://deepoove.com/jmh-visual-chart) ,就相对直观一些。

JMH - 实践与用法详解

结束

JMH 进行基准测试的使用过程并不复杂,同为 Java 虚拟机团队开发,准确性毋容置疑。

但是在进行基准测试时还是要注意自己的代码问题,如果编写的要进行测试的代码本身存在问题,那么测试的结果必定是不准的。

了解了 JMH 基准测试之后,可以尝试测试一些常用的工具或者框架的性能如何,看看哪个工具的性能最好,比如 FastJSON 真的比 Jackson / Gson在进行 JSON 转换时更快吗?

参考

https://openjdk.java.net/projects/code-tools/jmh/ http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/ https://lexburner.github.io/java-jmh/ https://segmentfault.com/a/1190000039902797


原文始发于微信公众号(程序猿阿南):JMH – 实践与用法详解

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/22324.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!