别再写 main 方法测试了,太 Low!这才是专业 Java 测试方法!

1.深入浅出:理解三极管截止、放大和饱和状态!——参考《模拟电子技术基础》清华大学华成英主讲 幼儿园水平理解三极管截止、放大和饱和状态!书上看不懂,听课听不懂的过来! 绕不开的三极管结构 以NPN为例,晶体三极管的结构,这是很多人不想看的,但是确实是非常重要的!不看结构是理解不了工作原理的! (这样记忆:N是negative,负,代表多子为电子;P是positive,正,代表多子为空穴) 注意观察三极管的结构,有助于理解工作时的状态。 两张图结合起来看,略作解释: 1.图中空心为空穴带正... 阅读详情

来源:https://juejin.cn/post/6844903936869007368

前言

"If you cannot measure it, you cannot improve it".

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

但是,如果熟悉JVM类加载机制的话,应该知道JVM默认的执行模式是JIT编译与解释混合执行。JVM通过热点代码统计分析,识别高频方法的调用、循环体、公共模块等,基于JIT动态编译技术,会将热点代码转换成机器码,直接交给CPU执行。

77550bc6edd0a53251daeb55879205bf.png

也就是说,JVM会不断的进行编译优化,这就使得很难确定重复多少次才能得到一个稳定的测试结果?所以,很多有经验的同学会在测试代码前写一段预热的逻辑。

JMH,全称 Java Microbenchmark Harness (微基准测试框架),是专门用于Java代码微基准测试的一套测试工具API,是由 OpenJDK/Oracle 官方发布的工具。何谓 Micro Benchmark 呢?简单地说就是在 method 层面上的 benchmark,精度可以精确到微秒级。

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

JMH的使用场景:

  1. 定量分析某个热点函数的优化效果

  2. 想定量地知道某个函数需要执行多长时间,以及执行时间和输入变量的相关性

  3. 对比一个函数的多种实现方式

本篇主要是介绍JMH的DEMO演示,和常用的注解参数。希望能对你起到帮助。

DEMO 演示

这里先演示一个DEMO,让不了解JMH的同学能够快速掌握这个工具的大概用法。

1. 测试项目构建

JMH是内置Java9及之后的版本。这里是以Java8进行说明。另外,最新最全的 Java 面试题整理好了,微信搜索Java面试库小程序在线刷题。

为了方便,这里直接介绍使用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项目,打开项目后我们会看到这样的项目结构。

15aee39ffc53c2e4ffac9910131c33ae.png

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

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

2. 编写性能测试

这里我以测试LinkedList 通过index 方式迭代和foreach 方式迭代的性能差距为例子,编写测试类,涉及到的注解在之后会讲解

/**
 * @author Richard_yyf
 * @version 1.0 2019/8/27
 */

@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.SECONDS)
@Threads(Threads.MAX)
public class LinkedListIterationBenchMark {
 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("");
        }
    }
}

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

4. 报告结果

输出结果如下,

最后的结果:

Benchmark                                      Mode  Cnt     Score   Error  Units
LinkedListIterationBenchMark.forEachIterate   thrpt    2  1192.380          ops/s
LinkedListIterationBenchMark.forIndexIterate  thrpt    2   206.866          ops/s

整个过程:

# Detecting actual CPU count: 12 detected
# JMH version: 1.21
# VM version: JDK 1.8.0_131, Java HotSpot(TM) 64-Bit Server VM, 25.131-b11
# VM invoker: C:\Program Files\Java\jdk1.8.0_131\jre\bin\java.exe
# VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.2\lib\idea_rt.jar=65175:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.2\bin -Dfile.encoding=UTF-8
# Warmup: 2 iterations, 10 s each
# Measurement: 2 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 12 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.sample.jmh.LinkedListIterationBenchMark.forEachIterate

# Run progress: 0.00% complete, ETA 00:01:20
# Fork: 1 of 1
# Warmup Iteration   1: 1189.267 ops/s
# Warmup Iteration   2: 1197.321 ops/s
Iteration   1: 1193.062 ops/s
Iteration   2: 1191.698 ops/s

Result "org.sample.jmh.LinkedListIterationBenchMark.forEachIterate":
  1192.380 ops/s

# JMH version: 1.21
# VM version: JDK 1.8.0_131, Java HotSpot(TM) 64-Bit Server VM, 25.131-b11
# VM invoker: C:\Program Files\Java\jdk1.8.0_131\jre\bin\java.exe
# VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.2\lib\idea_rt.jar=65175:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.2\bin -Dfile.encoding=UTF-8
# Warmup: 2 iterations, 10 s each
# Measurement: 2 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 12 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.sample.jmh.LinkedListIterationBenchMark.forIndexIterate

# Run progress: 50.00% complete, ETA 00:00:40
# Fork: 1 of 1
# Warmup Iteration   1: 205.676 ops/s
# Warmup Iteration   2: 206.512 ops/s
Iteration   1: 206.542 ops/s
Iteration   2: 207.189 ops/s

Result "org.sample.jmh.LinkedListIterationBenchMark.forIndexIterate":
  206.866 ops/s

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

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.

Benchmark                                      Mode  Cnt     Score   Error  Units
LinkedListIterationBenchMark.forEachIterate   thrpt    2  1192.380          ops/s
LinkedListIterationBenchMark.forIndexIterate  thrpt    2   206.866          ops/s

注解介绍

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

@BenchmarkMode

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

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

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

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

@Warmup

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

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

@Measurement

正式度量计算的轮数。

  • iterations 进行测试的轮次

  • time 每轮进行的时长

  • timeUnit时长单位

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

@Threads

每个进程中的测试线程。

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

@Fork

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

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

@OutputTimeUnit

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

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

@Benchmark

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

@Param

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

@Setup

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

@TearDown

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

@State

当使用@Setup参数的时候,必须在类上加这个参数,不然会提示无法运行。

就比如我上面的例子中,就必须设置state。

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

  1. Thread: 该状态为每个线程独享。

  2. Group: 该状态为同一个组里面所有线程共享。

  3. Benchmark: 该状态在所有线程间共享。

启动方法

在启动方法中,可以直接指定上述说到的一些参数,并且能将测试结果输出到指定文件中,

/**
 * 仅限于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可以对很多工具和框架进行测试,比如日志框架性能对比、BeanCopy性能对比 等,更多的example可以参考官方给出的JMH samples(https://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/)

保姆级教程:用Seurat 5.0.1搞定单细胞测序数据从质控到细胞注释的全流程 本文提供了一份详细的Seurat 5.0.1单细胞测序数据分析教程,涵盖从数据质控到细胞注释的全流程。通过R代码示例和可视化分析,帮助用户掌握单细胞RNA测序数据处理的核心技术,包括数据标准化、降维聚类和细胞类型注释,适用于生物信息学研究和临床数据分析。 阅读详情

相关推荐

【2023版】基于JT808/JT1078协议的车载视频监控平台开发实战-开源架构解析

本文详细解析了基于JT808/JT1078协议的车载视频监控平台开发实战,涵盖协议核心作用、开源架构设计、开发避坑指南及二次开发建议。重点介绍了JT1078协议在音视频传输中的优化方案,以及海量位置数据存储、高可用架构等关键技术点,为开发者提供全面的实战参考。

weixin_26965807的博客 237

JMH - Java 微基准测试工具

点击上方“阿拉奇学Java”,选择“置顶或者星标”优质文章第一时间送达!推荐阅读 |前言"If you cannot measure it, you cannot i...

qq_36189144的博客 309

ISCC-Amy‘s Code CTF-Reverse

ISCC-Amy's Code CTF-Reverse

m0_72904009的博客 682

Java 并发测试神器:基准测试神器-JMH

点击上方“芋道源码”,选择“设为星标”做积极的人,而不是积极废人!源码精品专栏原创 | Java 2019 超神之路,很肝~中文详细注释的开源项目RPC 框架 Dubbo 源码解析网络...

芋艿V 790

TensorRT(C++)部署 Pytorch模型

众所周知,python训练pytorch模型得到.pt模型。但在实际项目应用中,特别是嵌入式端部署时,受限于语言、硬件算力等因素,往往需要优化部署,而tensorRT是最常用的一种方式。本文以yolov5的部署为例,说明模型部署在x86架构上的电脑端的流程。(部署在Arm架构的嵌入式端的流程类似)。

女神sha手的博客 5409

vivado:差分信号和单端信号 的相互转换 IBUFDS OBUFDS

FPGA能处理的都是单端信号,但有些输入信号是差分的,这时候就需要把差分信号转为单端信号。

qq_52137732的博客 1万+

使用JMH做Benchmark基准测试

目录 BenchMark介绍 开始前的步骤 例子 代码 报告 注解介绍 @BenchmarkMode @OutputTimeUnit @Iteration @WarmUp ...

weixin_30478757的博客 604

Stata实战入门:从数据导入到基础分析的全流程指南

本文提供了Stata实战入门的全流程指南,从数据导入、清洗到基础分析和可视化,帮助初学者快速掌握Stata的核心功能。通过详细的步骤和实用技巧,读者可以高效完成数据处理、变量操作、描述性统计和回归分析等任务,提升数据分析效率。

weixin_28327051的博客 384

顶级Java才懂的,基准测试JMH!

原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。最近在手写一个ID生成器,需要比较UUID和目前比较流行的 NanoID之间的速度差异,当然也要测一下根据规则自...

小姐姐味道 1287

泛微OA表单设计实战:如何快速实现自定义多选浏览框(附完整配置步骤)

本文详细介绍了在泛微OA中快速实现自定义多选浏览框的完整配置步骤。针对招聘提报、活动审批等需要从海量数据中规范选择多个条目的业务场景,文章从理解需求、构建数据源、设计查询页面到创建浏览框控件,提供了清晰的实战指南,帮助开发者高效解决复杂数据选择难题,提升表单操作体验。

yog99的博客 1051

MDM主数据清洗和编码集成说明

绝大多数的企业业务系统是伴随业务发展而建设的,但随着业务发展单一系统难以支撑企业业务闭环。MDM主数据管理平台可以满足企业数据治理的需求,本文针对主数据清洗和编码集成进行梳理。...

数通畅联 3321

Java性能测试Benchmark使用总结

在 Java 中,可以使用多种方法来测量一段代码的执行性能。使用 System.currentTimeMillis()是最常见的方法但是这么测结果是不太准确的,因为Java存在代码优化以及JIT编译等等,通常更准确的方式是使用Benchmark的方式来评估性能。

vonlinee的博客 1934

Centos 7 Linux 安装 JDK 8:下载、安装、配置环境变量、验证

本文详细介绍了在 CentOS 7 系统上手动安装 JDK 8 的完整流程,包括环境准备、JDK 安装包下载与上传、解压安装、配置全局环境变量,以及最终的验证步骤。适用于使用 VMware 虚拟机、云服务器或物理机的 CentOS 7 用户,操作清晰、步骤完整,帮助开发者快速搭建 Java 开发环境。

寒水馨 · 轻量级技术文档 · hanshuixin.org 2584

求求你以后别再写 main 方法测试了,太 Low了!这才是专业 Java 测试方法!

点击上方蓝色字体,选择“标星公众号”优质文章,第一时间送达关注公众号后台回复pay或mall获取实战项目资料视频 作者:Richard_Yi 来源juejin.cn/post/684490...

公众号-老炮说Java 236

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

别再写 main 方法测试了,太 Low!这才是专业 Java 测试方法

推荐阅读:妙用Java 8中的 Function接口 消灭if...else(非常新颖的写法)前言"If you cannot measure it, you cannot improve it".在日常开发中,我们对一些代码的调用或者工具的使用会存在多种选择方式,在不确定他们性能的时候,我们首先想要做的就是去测量它。大多数时候,我们会简单的采用多次计数的方式来测量,来看这个方法的总耗时。但是,如...

Java和Android架构 127

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

上一篇: 多账号统一登陆,账号模块的系统设计
下一篇: 曝光一位出身寒门的技术大佬
Java思维导图
博客等级 码龄10年 306粉丝 71原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值