测试工具之JMH详解

这篇具有很好参考价值的文章主要介绍了测试工具之JMH详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1 JMH

1.1 引言

在日常开发中,我们对一些代码的调用或者工具的使用会存在多种选择方式,在不确定他们性能的时候,我们首先想要做的就是去测量它。大多数时候,我们会简单的采用多次计数的方式来测量,来看这个方法的总耗时。

但是,如果熟悉 JVM 类加载机制的话,应该知道 JVM 默认的执行模式是 JIT 编译与解释混合执行。

JVM 通过热点代码统计分析,识别高频方法的调用、循环体、公共模块等,基于 JIT 动态编译技术,会将热点代码转换成机器码,直接交给 CPU 执行。
jmh,日志,# 工具使用,测试工具,jvm,java
也就是说,JVM 会不断的进行编译优化,这就使得很难确定重复多少次才能得到一个稳定的测试结果?所以,很多有经验的同学会在测试代码前写一段预热的逻辑。

1.2 简介

JMH,全称 Java Microbenchmark Harness(微基准测试框架),是专门用于 Java 代码微基准测试的一套测试工具 API,是由 OpenJDK/Oracle 官方发布的工具。

何谓 Micro Benchmark 呢?简单地说就是在 method 层面上的 benchmark,精度可以精确到微秒级。

Java 的基准测试需要注意的几个点:

  • 测试前需要预热
  • 防止无用代码进入测试方法中
  • 并发测试
  • 测试结果呈现

JMH 的使用场景:

  • 定量分析某个热点函数的优化效果
  • 想定量地知道某个函数需要执行多长时间,以及执行时间和输入变量的相关性
  • 对比一个函数的多种实现方式

1.3 DEMO演示

1.3.1 测试项目构建

JMH 是内置 Java9 及之后的版本。这里是以 Java17 进行说明。为了方便,这里直接介绍使用 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 项目,打开项目后我们会看到这样的项目结构。
jmh,日志,# 工具使用,测试工具,jvm,java

第二种方式就是直接在现有的 maven 项目中添加 jmh-corejmh-generator-annprocess 的依赖来集成 JMH。

<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>1.35</version>
</dependency>
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>1.35</version>
    <scope>provided</scope>
</dependency>

1.3.2 编写性能测试

这里我以测试 LinkedList 通过 index 方式迭代和 foreach 方式迭代的性能差距为例子,编写测试类

@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.SECONDS)
@Threads(Threads.MAX)
public class TestList{
    private static final int SIZE = 10000;

    private List<String> 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("");
        }
    }
}

1.3.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();
    }

1.3.4 报告结果

最后的输出结果如下:

Benchmark                  Mode  Cnt    Score   Error  Units
TestList.forEachIterate   thrpt    2  590.884          ops/s
TestList.forIndexIterate  thrpt    2  179.808          ops/s

整个文档:

# Detecting actual CPU count: 8 detected
# JMH version: 1.35
# VM version: JDK 17.0.6, Java HotSpot(TM) 64-Bit Server VM, 17.0.6+9-LTS-190
# VM invoker: D:\SoftWare\Environments\jdk-17.0.6\bin\java.exe
# VM options: -javaagent:D:\SoftWare\Codes\Idea\IntelliJ IDEA 2022.1.4\lib\idea_rt.jar=54795:D:\SoftWare\Codes\Idea\IntelliJ IDEA 2022.1.4\bin -Dfile.encoding=UTF-8
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 10 s each
# Measurement: 2 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 8 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: cn.test.TestList.forEachIterate

# Run progress: 0.00% complete, ETA 00:01:20
# Fork: 1 of 1
# Warmup Iteration   1: 587.183 ops/s
# Warmup Iteration   2: 606.869 ops/s
Iteration   1: 582.747 ops/s
Iteration   2: 599.022 ops/s


Result "cn.test.TestList.forEachIterate":
  590.884 ops/s


# JMH version: 1.35
# VM version: JDK 17.0.6, Java HotSpot(TM) 64-Bit Server VM, 17.0.6+9-LTS-190
# VM invoker: D:\SoftWare\Environments\jdk-17.0.6\bin\java.exe
# VM options: -javaagent:D:\SoftWare\Codes\Idea\IntelliJ IDEA 2022.1.4\lib\idea_rt.jar=54795:D:\SoftWare\Codes\Idea\IntelliJ IDEA 2022.1.4\bin -Dfile.encoding=UTF-8
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 10 s each
# Measurement: 2 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 8 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: cn.test.TestList.forIndexIterate

# Run progress: 50.00% complete, ETA 00:00:41
# Fork: 1 of 1
# Warmup Iteration   1: 178.333 ops/s
# Warmup Iteration   2: 178.797 ops/s
Iteration   1: 180.050 ops/s
Iteration   2: 179.565 ops/s


Result "cn.test.TestList.forIndexIterate":
  179.808 ops/s


# Run complete. Total time: 00:01:22

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.

NOTE: Current JVM experimentally supports Compiler Blackholes, and they are in use. Please exercise
extra caution when trusting the results, look into the generated code to check the benchmark still
works, and factor in a small probability of new VM bugs. Additionally, while comparisons between
different JVMs are already problematic, the performance difference caused by different Blackhole
modes can be very significant. Please make sure you use the consistent Blackhole mode for comparisons.

Benchmark                  Mode  Cnt    Score   Error  Units
TestList.forEachIterate   thrpt    2  590.884          ops/s
TestList.forIndexIterate  thrpt    2  179.808          ops/s

1.4 注解介绍

下面我们来详细介绍一下相关的注解

1.4.1 @BenchmarkMode

微基准测试类型。JMH 提供了以下几种类型进行支持:

类型 描述
Throughput 每段时间执行的次数,一般是秒
AverageTime 平均时间,每次操作的平均耗时
SampleTime 在测试中,随机进行采样执行的时间
SingleShotTime 在每次执行中计算耗时
All 所有模式

可以注释在方法级别,也可以注释在类级别

@BenchmarkMode(Mode.All)
public class LinkedListIterationBenchMark {
    ...
}
@Benchmark
@BenchmarkMode({Mode.Throughput, Mode.SingleShotTime})
public void m() {
    ...
}

1.4.2 @Warmup

这个单词的意思就是预热iterations = 3 就是指预热轮数

@Benchmark
@BenchmarkMode({Mode.Throughput, Mode.SingleShotTime})
@Warmup(iterations = 3)
public void m() {
    ...
}

1.4.3 @Measurement

正式度量计算的轮数:

  • iterations:进行测试的轮次
  • time:每轮进行的时长
  • timeUnit:时长单位
@Benchmark
@BenchmarkMode({Mode.Throughput, Mode.SingleShotTime})
@Measurement(iterations = 3)
public void m() {
    ...
}

1.4.4 @Threads

每个进程中的测试线程

@Threads(Threads.MAX)
public class LinkedListIterationBenchMark {
    ...
}

1.4.5 @Fork

进行 fork 的次数。如果 fork 数是 3 的话,则 JMH 会 fork 出 3 个进程来进行测试

@Benchmark
@BenchmarkMode({Mode.Throughput, Mode.SingleShotTime})
@Fork(value = 3)
public void m() {
    ...
}

1.4.6 @OutputTimeUnit

基准测试结果的时间类型。一般选择秒、毫秒、微秒

@OutputTimeUnit(TimeUnit.SECONDS)
public class LinkedListIterationBenchMark {
    ...
}

1.4.7 @Benchmark

方法级注解,表示该方法是需要进行 benchmark 的对象,用法和 JUnit@Test 类似

1.4.8 @Param

属性级注解,@Param 可以用来指定某项参数的多种情况。特别适合用来测试一个函数在不同的参数输入的情况下的性能

1.4.9 @Setup

方法级注解,这个注解的作用就是我们需要在测试之前进行一些准备工作,比如对一些数据的初始化之类的

1.4.10 @TearDown

方法级注解,这个注解的作用就是我们需要在测试之后进行一些结束工作,比如关闭线程池,数据库连接等的,主要用于资源的回收等。

1.4.11 @State

当使用 @Setup 参数的时候,必须在类上加这个参数,不然会提示无法运行。就比如我上面的例子中,就必须设置 state

State 用于声明某个类是一个状态,然后接受一个 Scope 参数用来表示该状态的共享范围。
因为很多 benchmark 会需要一些表示状态的类,JMH 允许你把这些类以依赖注入的方式注入到 benchmark 函数里。

其中的参数Scope 主要分为三种:

  • Thread:该状态为每个线程独享。
  • Group:该状态为同一个组里面所有线程共享。
  • Benchmark:该状态在所有线程间共享。

1.5 启动方法解释

在启动方法中,可以直接指定上述说到的一些参数,并且能将测试结果输出到指定文件中。文章来源地址https://www.toymoban.com/news/detail-721463.html

/**
* 仅限于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详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【精通性能优化:解锁JMH微基准测试】一基本用法

    1. 什么是JMH JMH是Java Micro Benchmark Harness的简写,是专门用于代码微基准测试的工具集。 JMH由实现Java虚拟你的团队开发,现代JVM已经变的越来越智能,在Java文件的编译阶段、类的加载阶段,以及运行阶段都可能进行了不同程度的优化,因此开发者编写的代码未必会像自己所预

    2024年02月12日
    浏览(31)
  • 性能调优之JMH必知必会3:编写正确的微基准测试用例

      性能调优之JMH必知必会1:什么是JMH 性能调优之JMH必知必会2:JMH的基本用法 性能调优之JMH必知必会4:JMH的高级用法 性能调优之JMH必知必会5:JMH的Profiler       在前面两篇文章中分别介绍了什么是JMH、JMH的基本法。现在来介绍JMH正确的微基准测试用例如何编写。【 单位

    2023年04月08日
    浏览(38)
  • JMH204剑网2+精品怀旧端游【剑侠情缘2】降龙端优化汉化+任务GM工具+视频安装教程

                                                                JMH204精品端游-剑网2-剑侠情缘2-降龙端优化汉化 详情介绍 是否需要虚拟机:是 文件大小:压缩包约5G 支持系统:win7、win10、win11 硬件需求:运行内内存8G + 4核及以上CPU 录制的精细视频安装教程,有电脑使用

    2024年02月16日
    浏览(24)
  • 接口测试工具——Postman使用详解

    目录 Postman简介 Postman主界面 菜单栏 工具栏 请求管理区 环境管理区 请求设计区 发送请求 发送GET请求 Postman发送GET请求 发送表单格式POST请求 发送JSON格式POST请求 发送XML格式POST请求 发送文件上传类型的请求 响应 环境和变量 环境变量设置 环境变量使用 全局变量 测试脚本及

    2024年02月08日
    浏览(43)
  • Python测试工具-Pytest使用详解

    Pytest是一个全功能Python测试工具,支持第三方扩展插件,能够使用其开展单元测试和复杂功能测试。可以和selenium、requests、appium等模块结合使用实现WEB UI、API、APP自动化测试。 详见参考指南文档:https://docs.pytest.org/en/7.1.x/# PDF文档 : https://media.readthedocs.org/pdf/pytest/latest/pyt

    2024年02月02日
    浏览(29)
  • Onvif协议及协议测试工具使用详解

    目录 ​1、Onvif协议的发展 2、Onvif协议概述及优势 2.1    协议概述 2.2、规范优势

    2024年01月15日
    浏览(25)
  • 接口自动化测试工具,Postman使用详解

    1、Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件,Postman分为Postman native app和Postman Chrome app两个版本。目前Chrome app已经停止维护,官方也不推荐使用该版本。 2、官网下载地址:http://www.getpostman.com 3:官方文档:http://www.getpostman.com/docs 2、第二步:新建接口请

    2024年02月06日
    浏览(60)
  • 性能测试工具 ab(Apache Bench)使用详解

    Apache Bench (ab) 是一个由 Apache 提供的非常流行的、简单的性能测试工具,用于对 HTTP 服务器进行压力测试。下面是 ab 工具的一些基本使用方法。 安装 在大多数 Unix 系统中,ab 通常作为 Apache HTTP 服务器的一部分预装在系统中。你可以通过在终端中运行 ab -V 来检查 ab 的版本,

    2024年04月11日
    浏览(27)
  • 橘子学JDK之JMH-02(BenchmarkModes)

    这次我们来搞一下官网文档的第二个案例,我删除了一些没用的注释,然后对代码做了一下注释的翻译,可以看一下意思。 我们先不急着运行程序,我们在看完上面的例子之后可以看到这次出现了一个新的注解,就是 @BenchmarkMode 这个注解我们看到,作用的位置就是方法和类

    2024年04月16日
    浏览(23)
  • C++开发测试工具gmock的安装与使用超详解

    gmock是google公司推出的一款开源的白盒测试工具。gmock是个很强大的东西,测试一个模块的时候,可能涉及到和其他模块交互,可以将模块之间的接口mock起来,模拟交互过程。其作用就类似白盒测试中的打桩的概念。google 开源的跨平台C++单元测试框架,是为在不同平台上为编

    2024年02月06日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包