Java--JMH--性能测试--测试软件运行效率/时间--StopWatch
很多时候想要测试代码运行时间,或者比较2个运行的效率。 最简单的方法就是Sytem.currentTimeMillis记录2开始和结束时间来算
但是Java 代码越执行越快,放在后面的方法会有优势,这个原因受留个眼,以后研究。大概有受类加载,缓存预热, jit 编译优化等原因。
简单点的StopWatch
//创建对象
StopWatch s = new StopWatch();
//计时
s.start("这一次的名字");
....程序
//结束
s.stop();
//生成一个字符串,其中包含描述所有已执行任务的表。
System.out.println(s.prettyPrint());
多个对象
需要有参构造
//使用给定的 ID 构造一个新 StopWatch 。
//当我们有来自多个秒表的输出并需要区分它们时,该 ID 很方便。
public StopWatch(String id)
其他的应该不太用的着
JMH
一款一款官方的微基准测试工具 - JMH.
JMH(Java Microbenchmark Harness)是用于代码微基准测试的工具套件,主要是基于方法层面的基准测试,精度可以达到纳秒级。使用 JMH 可以让你方便快速的进行一次严格的代码基准测试,并且有多种测试模式,多种测试维度可供选择;而且使用简单、增加注解便可启动测试。
加入依赖
他们说高版本jdk自带,我用的jdk17不知道为啥没有,也没有找到有教程。
<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></dependency>
插件
这里可以使用JMH Java Microbenchmark Harness插件快速生成。而且可以像 JUnit 一样,运行单独的 Benchmark 方法\运行类中所有的 Benchmark 方法.
生成代码

右边可以直接运行

第一个案列
将要测试的方法添加@Benchmark注解即可。
然后用main方法启动就可以了。
public class JMHSample_01_HelloWorld {@Benchmarkpublic void wellHelloThere() {// this method was intentionally left blank.}public static void main(String[] args) throws RunnerException {Options opt = new OptionsBuilder().include(JMHSample_01_HelloWorld.class.getSimpleName()).forks(1).build();new Runner(opt).run();}
}
最后的结果,前面还有一大堆,在结果进行分析

结果分析(逐行分析)
结果输出有这样几个部分
版本和参数
// JMH版本号
# JMH version: 1.35
//jdk的版本和路径参数
# VM version: JDK 17, Java HotSpot(TM) 64-Bit Server VM, 17+35-LTS-2724
# VM invoker: D:\study\app\jdk\bin\java.exe
# VM options: -javaagent:D:\idea\IntelliJ IDEA 2021.3.3\lib\idea_rt.jar=9272:D:\idea\IntelliJ IDEA 2021.3.3\bin -Dfile.encoding=UTF-8//黑洞模式
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)//预热的次数和每一次的时间
# Warmup: 5 iterations, 10 s each //5次每次10s
// 测试的次数和时间
# Measurement: 5 iterations, 10 s each
// 超时,每次迭代10分钟
# Timeout: 10 min per iteration
//线程
# Threads: 1 thread, will synchronize iterations
//输出结果打印方式
# Benchmark mode: Throughput, ops/time//吞吐量
//执行的类
# Benchmark: org.openjdk.jmh.samples.JMHSample_01_HelloWorld.wellHelloThere
每次的统计
# Run progress: 0.00% complete, ETA 00:01:40
# Fork: 1 of 1
# Warmup Iteration 1: 1895950720.598 ops/s
# Warmup Iteration 2: 1906625977.172 ops/s
# Warmup Iteration 3: 2580159163.977 ops/s
# Warmup Iteration 4: 2564641382.865 ops/s
# Warmup Iteration 5: 2561493559.473 ops/s
Iteration 1: 2529900034.146 ops/s
Iteration 2: 2497119600.056 ops/s
Iteration 3: 2512703440.984 ops/s
Iteration 4: 2568341912.143 ops/s
Iteration 5: 2574424415.895 ops/s
这一部分是总体的统计
有最小值平均值和最大值 、标准差、置信区间
Result "org.openjdk.jmh.samples.JMHSample_01_HelloWorld.wellHelloThere":2536497880.645 ±(99.9%) 130763529.779 ops/s [Average](min, avg, max) = (2497119600.056, 2536497880.645, 2574424415.895), stdev = 33958873.426CI (99.9%): [2405734350.865, 2667261410.424] (assumes normal distribution)
一些介绍,没啥用
# Run complete. Total time: 00:01:41REMEMBER: 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 | 执行的 |
| mod | 执行模式 |
| cnt | 轮次 |
| score | 执行的结果平均值 |
| error | 误差 |
| units | 单位 |
Benchmark Mode Cnt Score Error Units
JMHSample_01_HelloWorld.wellHelloThere thrpt 5 2536497880.645 ± 130763529.779 ops/s进程已结束,退出代码0
使用介绍
只打算简单介绍对象的配置,注解肯定用的更舒服。
对象配置
创建对象后在后面逐个添加属性,然后在使用Runner类启动。

Benchmark
为该方法生成基准代码,在基准列表中将该方法注册为基准,从注释中读出默认值,并大致为基准测试运行准备环境。
适用:ElementType.METHOD,后面用注解表示了
BenchmarkMode
基准测试模式声明运行此基准测试的默认模式。有关可用的基准测试模式
@Target({ElementType.METHOD, ElementType.TYPE})Mode[] value:模式,必须的AverageTime - 调用的平均时间SampleTime - 随机取样,最后输出取样结果的分布,输出会比较多,但是不会全部统计SingleShotTime - 只会执行一次,通常用来测试冷启动时候的性能。All - 所有的benchmark modes。
Fork
执行次数,除了value默认就好了
@Target({ElementType.METHOD,ElementType.TYPE})int BLANK_FORKS = -1;String BLANK_ARGS = "blank_blank_blank_2014";/** @return 表示该benchMark执行多少次 */
int value() default BLANK_FORKS;/** @return 线束应分叉并忽略结果的次数 */
int warmups() default BLANK_FORKS;/** @return 要运行的 JVM 可执行文件 */
String jvm() default BLANK_ARGS;/** @return 要在命令行中替换的 JVM 参数 */
String[] jvmArgs() default { BLANK_ARGS };/** @return 要在命令行中预置的 JVM 参数*/
String[] jvmArgsPrepend() default { BLANK_ARGS };/** @return :要在命令行中追加的 JVM 参数*/
String[] jvmArgsAppend() default { BLANK_ARGS };
fork(0)同一个进程执行一次
fork(1)新建一个进程执行一次
fork(n)新建n个进程
默认的-1等同于传5
Warmup
允许为基准设置默认预热参数
@Target({ElementType.METHOD,ElementType.TYPE})/** @return 预热迭代次数 */
int iterations() default BLANK_ITERATIONS;/** @return每次预热迭代的时间 */
int time() default BLANK_TIME;/** @return 预热迭代持续时间的时间单位 */
TimeUnit timeUnit() default TimeUnit.SECONDS;/** @return 批大小:每个操作的基准方法调用数*/
int batchSize() default BLANK_BATCHSIZE;
Measurement
设置默认测量参数。
这个和上面一样,就换成翻译了
@Target({ElementType.METHOD,ElementType.TYPE})/** @return Number of measurement iterations */
int iterations() default BLANK_ITERATIONS;/** @return Time of each measurement iteration */
int time() default BLANK_TIME;/** @return Time unit for measurement iteration duration */
TimeUnit timeUnit() default TimeUnit.SECONDS;/** @return Batch size: number of benchmark method calls per operation */
int batchSize() default BLANK_BATCHSIZE;
OutputTimeUnit
为统计结果的时间单位
@Target({ElementType.METHOD,ElementType.TYPE})value: 时间单位
State
状态对象通常作为参数注入到方法中Benchmar。
@State 可以被继承使用,如果父类定义了该注解,子类则无需定义。由于 JMH 允许多线程同时执行测试,不同的选项含义如下:
@Target(ElementType.TYPE)Scope.Benchmark:所有测试线程共享一个实例,测试有状态实例在多线程共享下的性能
Scope.Group:同一个线程在同一个 group 里共享实例
Scope.Thread:默认的 State,每个测试线程分配一个实例
感觉这个不太好理解,这里看官方示例
这个代码中measureShared多个线程会同时操作这个x。
如2线程轮流那么x是1 2 3 4 5 6
measureUnshared同时操作那么就是
1 1 2 2 3 3
@State(Scope.Benchmark)public static class BenchmarkState {volatile double x = Math.PI;}@State(Scope.Thread)public static class ThreadState {volatile double x = Math.PI;}@Benchmarkpublic void measureUnshared(ThreadState state) {// 所有基准测试线程都将调用此方法。//// 但是,由于 ThreadState 是 Scope.Thread,因此每个线程将拥有自己的状态副本,此基准将衡量未共享的情况。state.x++;}@Benchmarkpublic void measureShared(BenchmarkState state) {// 由于 BenchmarkState 是 Scope.Benchmark,所有线程都将共享状态实例,我们最终将测量共享案例。state.x++;}
如果标记到类,我们看第四个代码.
那么我们相当于可以调用这个类的属性,而不用注入了。
@State(Scope.Thread)
public class JMHSample_04_DefaultState {double x = Math.PI;@Benchmarkpublic void measure() {x++;}
Setup and TearDown
会在执行 benchmark 之前/后被执行,主要用于初始化/资源处理。
@Target(ElementType.METHOD)此方法的级别。
Level value() default Level.Trial;
参数Level.Trial:Benchmark级别,benchmark执行完执行,最大的。Level.Iteration:执行迭代级别,每次执行完都会Level.Invocation:每次方法调用级别,每次方法调用就都执行
| 类型 | 结果 |
|---|---|
| Trial | Iteration 2:x = 1.059139639E9 |
| Iteration | # Warmup Iteration x = 3.54500352E8 Iteration 1: x = 7.0472257E8 Iteration 2:x = 1.059033135E9 |
| Invocation | 每次都调用一下![]() |
@TearDown(Level.Iteration)public void check() {System.out.println("---------------------------------");System.out.println("x = " + x);}@Benchmarkpublic void measureRight() {x++;}
死码消除问题
介绍
在第8给示例中主要介绍了死码优化的问题。
如果一个方法调用后没有什么用,就在编译器被消除了,但这给我做基准测试带了一些麻烦
我们看第八个:
讲道理来演示的意思是measureWrong比rigth应该慢超级多,但是我怎么测试都测试不出来,不知道为什么。而且第九个也测试不出来。
private double x = Math.PI;@Benchmarkpublic void baseline() {// do nothing, this is a baseline}@Benchmarkpublic void measureWrong() {// This is wrong: result is not used and the entire computation is optimized away.Math.log(x);}@Benchmarkpublic double measureRight() {// This is correct: the result is being used.return Math.log(x);}

解决
第九个示例告诉我们解决。
不过我测不出来。
- 使用他
- 注入Blackhole bh 对象,使用consume方法调用
@Benchmarkpublic void measureRight_2(Blackhole bh) {bh.consume(Math.log(x1));bh.consume(Math.log(x2));}
同一个进程会互相影响
众所周知,JVM擅长按配置文件优化。这对基准测试不利,因为不同的测试可以将它们的配置文件混合在一起,然后为每个测试呈现“统一错误”的代码。分叉(在单独的进程中运行)每个测试都有助于避免此问题。默认情况下,JMH 将分叉测试。
@Benchmark@Fork(0)public int measure_1_c1() {return measure(c1);}/** Then Counter2...*/@Benchmark@Fork(0)public int measure_2_c2() {return measure(c2);}/** Then Counter1 again...*/@Benchmark@Fork(0)public int measure_3_c1_again() {return measure(c1);}
请注意,C1 更快,C2 更慢,但 C1 又慢了!这是因为 C1 和 C2 的配置文件已合并在一起。请注意分叉运行的测量是多么完美。

还有些案例,不过不想学了,这些够现在的用了,以后需要在学
相关文章:
Java--JMH--性能测试--测试软件运行效率/时间--StopWatch
写在前面: 很多时候想要测试代码运行时间,或者比较2个运行的效率。 最简单的方法就是Sytem.currentTimeMillis记录2开始和结束时间来算 但是Java 代码越执行越快,放在后面的方法会有优势,这个原因受留个眼,以后研究。大概有受类加…...
JavaScript Array(数组)对象
数组对象的作用是:使用单独的变量名来存储一系列的值。参数参数 size 是期望的数组元素个数。返回的数组,length 字段将被设为 size 的值。参数 element ...; elementn 是参数列表。当使用这些参数来调用构造函数 Array() 时,新创建的数组的元…...
干货 | 电容在电路35个基本常识
第1个电压源正负端接了一个电容,与电路并联,用于整流电路时,具有很好的滤波作用,当电压交变时,由于电容的充电作用,两端的电压不能突变,就保证了电压的平稳。当用于电池电源时,具有交…...
日读300篇文献的技巧
感觉自己看文章很慢,有时候也抓不住重点。 如果是英文文献的话,可能还要有点难度,毕竟英语渣渣还是需要有中文-》英文的转换过程。 最近在搞毕业论文的时候,发现了一个非常好玩的东西,大大提升了我看文章搞科研&#x…...
C++核心编程
一、内存分区模型概述:C程序在执行时,将内存划分为4个区域程序运行前:代码区:存放函数体的二进制代码,由操作系统管理①共享。共享的目的是对于频繁被执行的程序,在内存中只需有一份代码即可②只读。使其只…...
SpringMVC程序开发
目录 SpringMVC 1、MVC定义 2、MVC和SpringMVC之间的关系 学SpringMVC 1、Spring MVC的创建和连接 浏览器获取前端接口和后端程序连接功能实现 2、获取参数 2.1、传递单个参数/多个参数 2.2、传递对象 2.3、传递表单参数 2.4、后端参数重命名 2.5、RequestBody接收J…...
多版本并发控制MVCC
什么是MVCC? MVCC是一种并发控制方法,一般在数据库管理系统中,实现数据库的并发访问。 可以使用乐观锁和悲观锁来实现。 MVCC的作用? 可以在不加锁的情况下解决读写问题,同时还可以解决脏读,幻读&#…...
JavaScript Date(日期)对象
日期对象用于处理日期和时间。在线实例返回当日的日期和时间如何使用 Date() 方法获得当日的日期。getFullYear()使用 getFullYear() 获取年份。getTime()getTime() 返回从 1970 年 1 月 1 日至今的毫秒数。setFullYear()如何使用 setFullYear() 设置具体的日期。toUTCString()…...
【Python】AES加解密代码,文章还有加密串等你来解密,等你来挑战
🍦🍦写这篇AES文章也是有件趣事,有位小伙伴发了段密文,看看谁解密速度快,学过Python的小伙伴一下子就解开来了,内容也挺有趣的。 🍟🍟原来加解密也可以这么有趣,虽然看起…...
代码随想录【Day34】| 1005. K 次取反后最大化的数组和、134. 加油站、135. 分发糖果
1005. K 次取反后最大化的数组和 题目链接 题目描述: 给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次。(我们可以多次选择同一个索引 i。&…...
Java性能调优杀手锏JMH
JMH简介 JMH(Java Microbenchmark Harness)由 OpenJDK/Oracle 里面那群开发了 Java编译器的大牛们所开发,是一个功能强大、灵活的工具,它可以用于检测和评估Java应用程序的性能,主要目的是测量Java应用程序的性能,尤其是在多线程…...
实现excle表上传生成echarts图
代码如下html <!--这是一个网上关于读取Excel最经典的代码--> <!DOCTYPE html> <html><head><meta charset"utf-8"><title>ECharts</title><!-- 引入 echarts.js --><!-- <script src"newjs/js/incubato…...
python代码如何打包
网上的文章对小白都不太友好呀,讲得都比较高大上,本文章就用最简单的方式来教会大家如何打包。既然各位已经学习到了python打包了, 深适度应该跟我查不多。 注意事项: 1. 这个插件只能打包 mac 、win系统运行的文件,也…...
MyBatis学习笔记(十二) —— MyBatis的逆向工程
12、MyBatis的逆向工程 正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的。 逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源: Java实体类Mappe…...
4.Elasticsearch深入了解
4.Elasticsearch深入了解[toc]1.Elasticsearch架构原理Elasticsearch的节点类型在Elasticsearch主要分成两类节点,一类是Master,一类是DataNode。Master节点在Elasticsearch启动时,会选举出来一个Master节点。当某个节点启动后,然…...
【HashSet】| 深度剥析Java SE 源码合集Ⅲ
目录一. 🦁 HashSet介绍1.1 特点1.2 底层实现二. 🦁 结构以及对应方法分析2.1 结构组成2.1.1 源码实现2.1.2 成员变量及构造方法2.2 常用的方法2.2.1 添加add(E e)方法2.2.2 删除remove(Object o)方法三. 最后想说一. 🦁 HashSet介绍 1.1 特…...
你了解线程的状态转换吗
本文概述: 讲述线程的六种状态. 你可能已经了解了六种状态, 但是你知道 sleep 被唤醒之后, wait ()被 notify 之后进入了什么状态吗? 本文只是开胃小菜, 你看看下一篇文章对你有没有帮助. 一共有六种状态: New 新建状态Runnable 运行状态Blocked 阻塞状态Waiting 等待状态Tim…...
MyBatis-Plus联表查询的短板,该如何解决呢
mybatis-plus作为mybatis的增强工具,它的出现极大的简化了开发中的数据库操作,但是长久以来,它的联表查询能力一直被大家所诟病。一旦遇到left join或right join的左右连接,你还是得老老实实的打开xml文件,手写上一大段…...
吲哚菁绿-巯基,ICG-SH,科研级别试剂,吲哚菁绿可用于测定心输出量、肝脏功能、肝血流量,和对于眼科血管造影术。
ICG-THIOL,吲哚菁绿-巯基 中文名称:吲哚菁绿-巯基 英文名称:ICG-THIOL 英文别名:ICG-SH 性状:绿色粉末 溶剂:溶于二氯甲烷等其他常规有机溶剂 稳定性:冷藏保存,避免反复冻融。 存储条件&…...
深度剖析JavaOptional类
Java Optional 类 Optional类在 Java 8中被加了进来,提供了一种处理业务逻辑想要的值可能没有出现(null)也可能出现的情况,可能直到目前,我们还是用null 来表示业务值不存在的情况,但是这可能导致空指针异常,Java 8新添加 Optional类可以从一定程度上来解决这个问题。 O…...
Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...

