百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

JAVA基准测试工具套件:JMH

bigegpt 2024-08-26 11:07 3 浏览

JMH是OpenJDK的JIT团队开发的微基准测试框架,可针对基准方法在吞吐量、响应时间等维度进行纳秒/微秒/毫秒/秒级的性能基准测试。随着虚拟机的逐步优化,过去的一些常见说辞已经不再那么绝对,比如:final 修饰的变量性能更好,对象使用后赋null可以加快GC回收等。因此性能的好坏需要量化比较,JMH正是解决这方面的好工具。



JMH典型用法:

1.生成一个maven工程

 mvn archetype:generate 
 -DinteractiveMode=false 
 -DarchetypeGroupId=org.openjdk.jmh 
 -DarchetypeArtifactId=jmh-java-benchmark-archetype 
 -DgroupId=com.jmh 
 -DartifactId=jmh 
 -Dversion=1.0.0-SNAPSHOT

执行上述命令后,会创建一个默认的JMH Maven工程。

2.写压测代码

默认生成的 MyBenchmark.java 就是执行JMH的压测类,方法 testMethod 中就是要压测的代码

代码示例:

@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(1)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class MyBenchmark {

    @GenerateMicroBenchmark
    public List<Integer> testMethod() {
        //....
    }

}

3.构建打包

 mvn clean package

4.执行基准测试

打包成功后在target目录下生成了一个JAR文件:microbenchmarks.jar,需要注意的是,官网的运行命令是java -jar target/benchmarks.jar,至于到底是benchmarks.jar还是microbenchmarks.jar,取决于你的POM文件:

<configuration>
    <finalName>microbenchmarks</finalName>
    <transformers>
        <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
            <mainClass>org.openjdk.jmh.Main</mainClass>
        </transformer>
    </transformers>
</configuration>

执行jar包运行命令

 java -jar target/microbenchmarks.jar

输出结果如下:

# Run progress: 0.00% complete, ETA 00:00:10
# VM invoker: C:\Program Files\Java\jre1.8.0_181\bin\java.exe
# VM options: <none>
# Fork: 1 of 1
# Warmup: 5 iterations, 1 s each
# Measurement: 5 iterations, 1 s each
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: com.afei.jmh.MyBenchmark.testMethod
# Warmup Iteration   1: 1133.738 ns/op
# Warmup Iteration   2: 1169.750 ns/op
# Warmup Iteration   3: 1066.204 ns/op
# Warmup Iteration   4: 1086.300 ns/op
# Warmup Iteration   5: 1145.228 ns/op
Iteration   1: 1045.157 ns/op
Iteration   2: 1064.303 ns/op
Iteration   3: 1064.227 ns/op
Iteration   4: 1053.979 ns/op
Iteration   5: 1055.718 ns/op

Result : 1056.677  ±(99.9%) 30.809 ns/op
  Statistics: (min, avg, max) = (1045.157, 1056.677, 1064.303), stdev = 8.001
  Confidence interval (99.9%): [1025.868, 1087.486]

Benchmark                        Mode   Samples         Mean   Mean error    Units
c.a.j.MyBenchmark.testMethod     avgt         5     1056.677       30.809    ns/op

5.结果解读

下面对输出结果一些重要信息进行解读:

@Warmup

由于注解:@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)。所以,基准测试后对代码预热总计5秒(迭代5次,每次1秒)。预热对于压测来说非常非常重要,如果没有预热过程,压测结果会很不准确。这个注解对应的日志如下:

# Warmup Iteration   1: 1133.738 ns/op
# Warmup Iteration   2: 1169.750 ns/op
# Warmup Iteration   3: 1066.204 ns/op
# Warmup Iteration   4: 1086.300 ns/op
# Warmup Iteration   5: 1145.228 ns/op

@Measurement

另外一个重要的注解:@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS),表示循环运行5次,总计5秒时间。

@Fork

这个注解表示fork多少个线程运行基准测试,如果@Fork(1),那么就是一个线程,这时候就是同步模式。

@BenchmarkMode&@OutputTimeUnit

基准测试模式申明为:@BenchmarkMode(Mode.AverageTime)搭配@OutputTimeUnit(TimeUnit.NANOSECONDS)(可选基准测试模式通过枚举Mode得到),笔者的示例是AverageTime,即表示每次操作需要的平均时间,而OutputTimeUnit申明为纳秒,所以基准测试单位是ns/op,即每次操作的纳秒单位平均时间。基准测试结果如下:


Result : 1056.677 ±(99.9%) 30.809 ns/op
  Statistics: (min, avg, max) = (1045.157, 1056.677, 1064.303), stdev = 8.001
  Confidence interval (99.9%): [1025.868, 1087.486]

最后一段结果如下,重点关注MeanUnits两个字段,组合起来就是1227.928ns/op,即每次操作耗时1056.677纳秒:

Benchmark                        Mode   Samples         Mean   Mean error    Units
c.a.j.MyBenchmark.testMethod     avgt         5     1056.677       30.809    ns/op

如果我们将@BenchmarkMode(Mode.AverageTime)与@OutputTimeUnit(TimeUnit.NANOSECONDS)的组合,改成@BenchmarkMode(Mode.Throughput)和@OutputTimeUnit(TimeUnit.MILLISECONDS),那么基准测试结果就是每毫秒的吞吐量(即每毫秒多少次操作),结果如下,表示943.437ops/ms:


Benchmark                        Mode   Samples         Mean   Mean error    Units
c.a.j.MyBenchmark.testMethod    thrpt         5      943.437       44.060   ops/ms

注:Mean error表示误差,或者波动,与Result的±值对应:Result : 1056.677 ±(99.9%) 30.809 ns/op;

JMH和jMeter的不同

JMH和jMeter的使用场景还是有很大的不同的,jMeter更多的是对rest api进行压测,而JMH关注的粒度更细,它更多的是发现某块性能槽点代码,然后对优化方案进行基准测试对比。

相关推荐

Docker篇(二):Docker实战,命令解析

大家好,我是杰哥上周我们通过几个问题,让大家对于Docker有了一个全局的认识。然而,说跟练往往是两个概念。从学习的角度来说,理论知识的学习,往往只是第一步,只有经过实战,才能真正掌握一门技术所以,本...

docker学习笔记——安装和基本操作

今天学习了docker的基本知识,记录一下docker的安装步骤和基本命令(以CentOS7.x为例)一、安装docker的步骤:1.yuminstall-yyum-utils2.yum-con...

不可错过的Docker完整笔记(dockerhib)

简介一、Docker简介Docker是一个开源的应用容器引擎,基于Go语言并遵从Apache2.0协议开源。Docker可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,...

扔掉运营商的 IPTV 机顶盒,全屋全设备畅看 IPTV!

其实现在看电视节目的需求确实大大降低了,折腾也只是为了单纯的让它实现,享受这个过程带来的快乐而已,哈哈!预期构想家里所有设备直接接入网络随时接收并播放IPTV直播(电信点播的节目不是太多,但好在非常稳...

第五节 Docker 入门实践:从 Hello World 到容器操作

一、Docker容器基础运行(一)单次命令执行通过dockerrun命令可以直接在容器中执行指定命令,这是体验Docker最快捷的方式:#在ubuntu:15.10容器中执行ech...

替代Docker build的Buildah简单介绍

Buildah是用于通过较低级别的coreutils接口构建OCI兼容镜像的工具。与Podman相似,Buildah不依赖于Docker或CRI-O之类的守护程序,并且不需要root特权。Builda...

Docker 命令大全(docker命令大全记录表)

容器生命周期管理run-创建并启动一个新的容器。start/stop/restart-这些命令主要用于启动、停止和重启容器。kill-立即终止一个或多个正在运行的容器rm-于删除一个或...

docker常用指令及安装rabbitMQ(docker安装rabbitmq配置环境)

一、docker常用指令启动docker:systemctlstartdocker停止docker:systemctlstopdocker重启docker:systemctlrestart...

使用Docker快速部署Storm环境(docker部署confluence)

Storm的部署虽然不是特别麻烦,但是在生产环境中,为了提高部署效率,方便管理维护,使用Docker来统一管理部署是一个不错的选择。下面是我开源的一个新的项目,一个配置好了storm与mono环境的D...

Docker Desktop安装使用指南:零基础教程

在之前的文章中,我多次提到使用Docker来安装各类软件,尤其是开源软件应用。鉴于不少读者对此有需求,我决定专门制作一期关于Docker安装与使用的详细教程。我主要以Macbook(Mac平台)为例进...

Linux如何成功地离线安装docker(linux离线安装httpd)

系统环境:Redhat7.2和Centos7.4实测成功近期因项目需要用docker,所以记录一些相关知识,由于生产环境是不能直接连接互联网,尝试在linux中离线安装docker。步骤1.下载...

Docker 类面试题(常见问题)(docker面试题目)

Docker常见问题汇总镜像相关1、如何批量清理临时镜像文件?可以使用sudodockerrmi$(sudodockerimages-q-fdanging=true)命令2、如何查看...

面试官:你知道Dubbo怎么优雅上下线的吗?你:优雅上下线是啥?

最近无论是校招还是社招,都进行的如火如荼,我也承担了很多的面试工作,在一次面试过程中,和候选人聊了一些关于Dubbo的知识。Dubbo是一个比较著名的RPC框架,很多人对于他的一些网络通信、通信协议、...

【Docker 新手入门指南】第五章:Hello Word

适合人群:完全零基础新手|学习目标:30分钟掌握Docker核心操作一、准备工作:先确认是否安装成功打开终端(Windows用户用PowerShell或GitBash),输入:docker--...

松勤软件测试:详解Docker,如何用portainer管理Docker容器

镜像管理搜索镜像dockersearch镜像名称拉取镜像dockerpullname[:tag]列出镜像dockerimages删除镜像dockerrmiimage名称或id删除...