zoukankan      html  css  js  c++  java
  • 基于JMH框架做Java微基准测试

    使用JMH做Java微基准测试

    转载自(https://www.jianshu.com/p/09837e2b4408),感谢原作者的分享!

    一、前言

    在Java编程中,我们对于一些代码调用细节有多种实现方式,但是不能确定它们性能,实践中往往通过理论推导或重复多次计时的方式来判定。但是随着JVM不断的进化,随着代码执行次数的增加,JVM会不断的进行编译优化,使得重复多少次才能够得到一个稳定的测试结果变得让人疑惑,这时候有经验的同学就会在测试执行前先循环上万次并注释为预热。

    没错!这样做确实可以获得一个偏向正确的测试结果,但试想如果每到需要斟酌性能的时候,都要根据场景写一段预热的逻辑吗?当预热完成后,需要多少次迭代来进行正式内容的测量呢?每次测试结果的输出报告是不是都需要用System.out来输出呢?

    其实这些工作都可以交给 JMH (Java Microbenchmark Harness) ,它被作为Java9的一部分来发布,但是我们完全可以脱离Java9,而方便的使用它来简化我们测试,它能够照看好JVM的预热、代码优化,让你的测试过程变得更加简单。

    注意:从JMH的名字中的“微基准”可知,它只适合用于局部点的精细粒度性能测试,通常用于对比多个细节实现,辅助做出更高性能抉择,不适用于应用级等粗粒度的性能测试。

    二、准备

    1. 首先在项目中新增依赖,jmh-core以及jmh-generator-annprocess的依赖可以在maven仓库中找寻最新版本。

    img

    1. 创建一个Helloworld类,里面只有一个空方法m(),标注了@Benchmark的注解,声明这个方法为一个微基准测试目标方法,JMH 会在编译期基于代理机制生成基准测试的代码,并运行它。

    img

    ​3. 接着添加一个main入口,由它来启动测试。

    img

    简单介绍一下这个HelloworldRunner类,它是一个入口的同时还完成了 **JMH** 测试的配置工作。默认场景下,JMH会找寻标注了@Benchmark类型的方法,可能会跑一些你所不需要的测试,
    这样就需要通过include和exclude两个方法来完成包含以及排除的语义。
    warmupIterations(10)的意思是预热做10轮。
    measurementIterations(10)代表正式计量测试做10轮,而每次都是先执行完预热再执行正式计量,内容都是调用标注了@Benchmark的代码。
    forks(3)指的是做3轮测试,因为一次测试无法有效的代表结果,所以通过3轮测试较为全面的测试,而每一轮都是先预热,再正式计量。
    

    我们运行HelloworldRunner,经过一段时间,最终测试结果如下:
    img

    可以看到分数是30亿次,但是这30亿指的是什么呢?仔细观察 Mode 一项中类型是thrpt,其实就是Throughput吞吐量,代表着每秒完成的次数。

    三、测试模式

    ​ 前面提到测试的类型是吞吐量,也就是一秒钟调用完成的次数,但是如果想知道做一次需要多少时间该怎么办?

    其实 1 / 吞吐量 就是这个值

    JMH 提供了以下几种类型进行支持:

    img

    使用这些模式也非常简单,只需要增加@BenchmarkMode注解即可,例如:

    img

    四、配置策略

    JMH 支持通过@Fork注解完成配置,例如:

    img

    以上注解指init()方法测试时,预热2轮,正式计量1轮,但是如果测试方法比较多,还是建议通过Options进行配置,具体可以参考HelloworldRunner。

    五、例子:循环的微基准测试

    for循环大家平时经常使用,但是看到过一个优化策略,就是倒序遍历,比如:for (int i = length; i > 0; i--)优于for (int i = 0; i < length; i++),有些不解。咨询了温少,温少给出的答案是i > 0优于i < length,因此倒序有优势,那么我们将这个场景做一下基准测试。

    ​ 首先是正向循环,次数是1百万次迭代。

    img

    接着是逆向循环,次数也是1百万次。

    img

    最后是一个测试的入口,我们采用3组,每组预热10轮,正式计量10轮,测试类型是吞吐量。

    img

    测试结果如下,有数据表现可以看到逆序在宏观上是优于正序的。

    img

    六、优化的Hessian2微基准测试

    ​ HSF默认使用Hessian2进行序列化传输,而Hessian2在传输时,每次会捎带上类型元信息,这些在实际场景下对资源会产生一定的开销。HSF2.2会使用优化的Hessian2进行序列化,与Hessian2的不同在于,它会基于长连接级别缓存元信息,每次只会发送数据内容,由于只发送数据内容,所以资源开销会更少,我们对Hessian2和优化后的Hssian2做了基准测试,结果如下:

    img

    优化后的hessian在序列化吞吐量上领先hessian2,达到每秒17W,反序列化出乎意料,超过hessian2两倍,达到32W每秒。

    学习使我充实,分享给我快乐!
  • 相关阅读:
    LeetCode 227. Basic Calculator II
    LeetCode 224. Basic Calculator
    LeetCode 103. Binary Tree Zigzag Level Order Traversal
    LeetCode 102. Binary Tree Level Order Traversal
    LeetCode 106. Construct Binary Tree from Inorder and Postorder Traversal
    LeetCode 105. Construct Binary Tree from Preorder and Inorder Traversal
    LeetCode 169. Majority Element
    LeetCode 145. Binary Tree Postorder Traversal
    LeetCode 94. Binary Tree Inorder Traversal
    LeetCode 144. Binary Tree Preorder Traversal
  • 原文地址:https://www.cnblogs.com/JaxYoun/p/13773671.html
Copyright © 2011-2022 走看看