当前位置: 首页 > news >正文

Java基准测试工具JMH高级使用

去年,我们写过一篇关于JMH的入门使用的文章:Java基准测试工具JMH使用,今天我们再来聊一下关于JMH的高阶使用。主要我们会围绕着以下几点来讲:

  • 对称并发测试
  • 非对称并发测试
  • 阻塞并发测试
  • Map并发测试

关键词

@State 在很多时候我们需要维护一些状态内容,比如在多线程的时候我们会维护一个共享的状态,这个状态值可能会在每根线程中都一样,也有可能是每根线程都有自己的状态,JMH为我们提供了状态的支持。该注解只能用来标注在类上,因为类作为一个属性的载体。@State的状态值主要有以下几种:

Scope.Benchmark 该状态的意思是会在所有的Benchmark的工作线程中共享变量内容。
Scope.Group 同一个Group的线程可以享有同样的变量
Scope.Thread 每个线程都享有一份变量的副本,线程之间对于变量的修改不会相互影响

@Group 执行组的识别号
@GroupThreads 执行某个方法所需要的线程数量

对称并发测试

我们编写的所有基准测试都会被JMH框架根据方法名的字典顺序排序之后串行执行,然而有些时候我们会想要对某个类的读写方法并行执行,比如,我们想要在修改某个原子变量的时候又有其他线程对其进行读取操作。

@BenchmarkMode(Mode.AverageTime)
@Fork(1)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Group)
public class SymmetricBenchmark {private AtomicLong counter;@Setuppublic void init() {this.counter = new AtomicLong();}@GroupThreads(5)@Group("atomic")@Benchmarkpublic void inc() {this.counter.incrementAndGet();}@GroupThreads(5)@Group("atomic")@Benchmarkpublic long get() {return this.counter.get();}public static void main(String[] args) throws RunnerException {Options opt = new OptionsBuilder().include(SymmetricBenchmark.class.getSimpleName()).build();new Runner(opt).run();}
}

结果为:

Benchmark                      Mode  Cnt  Score   Error  Units
SymmetricBenchmark.atomic      avgt    5  0.126 ± 0.009  us/op
SymmetricBenchmark.atomic:get  avgt    5  0.062 ± 0.011  us/op
SymmetricBenchmark.atomic:inc  avgt    5  0.190 ± 0.011  us/op

我们在对AtomicLong进行自增操作的同时又会对其进行读取操作,这就是我们经常见到的高并发环境中某些API的操作方式,同样也是线程安全存在隐患的地方。5个线程对AtomicLong执行自增操作,5个线程对AtomicLong执行读取时的性能输出说明如下:

  • group atomic(5个读线程,5个写线程)的平均响应时间为0.126 us,误差为0.009。
  • group atomic(5个读线程)同时读取AtomicLong变量的速度为0.062 us,误差为0.011。
  • group atomic(5个写线程)同时修改AtomicLong变量的速度为0.190 us,误差为0.011 。

非对称并发测试

有时,您需要达到非对称测试的目的。

@Fork(1)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@State(Scope.Group)
public class AsymmetricBenchMark {private AtomicLong counter;@Setuppublic void up() {counter = new AtomicLong();}@Benchmark@Group("atomic")@GroupThreads(3)public long inc() {return counter.incrementAndGet();}@Benchmark@Group("atomic")@GroupThreads(1)public long get() {return counter.get();}public static void main(String[] args) throws RunnerException {Options opt = new OptionsBuilder().include(AsymmetricBenchMark.class.getSimpleName()).build();new Runner(opt).run();}
}

结果为:

Benchmark                       Mode  Cnt  Score   Error  Units
AsymmetricBenchMark.atomic      avgt    5  0.053 ± 0.003  us/op
AsymmetricBenchMark.atomic:get  avgt    5  0.025 ± 0.006  us/op
AsymmetricBenchMark.atomic:inc  avgt    5  0.062 ± 0.005  us/op

我们在对AtomicLong进行自增操作的同时又会对其进行读取操作,这就是我们经常见到的高并发环境中某些API的操作方式,同样也是线程安全存在隐患的地方。3个线程对AtomicLong执行自增操作,1个线程对AtomicLong执行读取时的性能输出说明如下:

  • group atomic(1个读线程,3个写线程)的平均响应时间为0.053 us,误差为0.003 。
  • group atomic(1个读线程)同时读取AtomicLong变量的速度为0.025 us,误差为0.006 。
  • group atomic(3个写线程)同时修改AtomicLong变量的速度为0.062 us,误差为0.005 。

阻塞并发测试

有些时候我们想要执行某些容器的读写操作时可能会引起阻塞,比如blockqueue,在某些情况下程序会出现长时间的阻塞,这就严重影响了我们测试的结果,我们可以通过设置Options的timeout来强制让每一个批次的度量超时,超时的基准测试数据将不会被纳入统计之中。
以下测试,我们设置每批次如果超过10秒,就被认为超时不计入统计。

@Fork(1)
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Group)
public class InterruptBenchmark {private BlockingQueue<Integer> queue;private final static int VALUE = Integer.MAX_VALUE;@Setuppublic void init() {this.queue = new ArrayBlockingQueue<>(10);}@GroupThreads(5)@Group("queue")@Benchmarkpublic void put()throws InterruptedException {this.queue.put(VALUE);}@GroupThreads(5)@Group("queue")@Benchmarkpublic int take()throws InterruptedException {return this.queue.take();}public static void main(String[] args) throws RunnerException {Options opt = new OptionsBuilder().include(InterruptBenchmark.class.getSimpleName())// 将每个批次的超时时间设置为10秒.timeout(TimeValue.milliseconds(10000)).build();new Runner(opt).run();}
}

结果为:

Benchmark                      Mode  Cnt      Score       Error  Units
InterruptBenchmark.queue       avgt    5  19204.384 ± 23024.739  ns/op
InterruptBenchmark.queue:put   avgt    5  14049.887 ± 49670.027  ns/op
InterruptBenchmark.queue:take  avgt    5  24358.880 ± 31679.280  ns/op

有些执行时被阻塞的结果就被忽略了,报告中会如下所示:

Iteration   5: (benchmark timed out, interrupted 1 times) 27130.727 ±(99.9%) 53300.757 ns/op

如果超时时间设置得过小,那么,会得到如下警告:

# Timeout: 1000 ms per iteration, ***WARNING: The timeout might be too low!***

Map并发测试

对比几大线程安全Map的多线程下的读写性能,以后类似的操作可以按照这个模板来。

@Fork(1)
@BenchmarkMode(Mode.Throughput)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Group)
public class MapBenchMark {@Param({"ConcurrentHashMap", "ConcurrentSkipListMap", "Hashtable", "Collections.synchronizedMap"})private String type;private Map<Integer, Integer> map;@Setuppublic void setUp() {switch (type) {case "ConcurrentHashMap":this.map = new ConcurrentHashMap<>();break;case "ConcurrentSkipListMap":this.map = new ConcurrentSkipListMap<>();break;case "Hashtable":this.map = new Hashtable<>();break;case "Collections.synchronizedMap":this.map = Collections.synchronizedMap(new HashMap<>());break;default:throw new IllegalArgumentException("Illegal map type.");}}@Group("map")@GroupThreads(5)@Benchmarkpublic void putMap() {int random = randomIntValue();this.map.put(random, random);}@Group("map")@GroupThreads(5)@Benchmarkpublic Integer getMap() {return this.map.get(randomIntValue());}/*** 计算一个随机值用作Map中的Key和Value** @return*/private int randomIntValue() {return (int) Math.ceil(Math.random() * 600000);}public static void main(String[] args) throws RunnerException {Options opt = new OptionsBuilder().include(MapBenchMark.class.getSimpleName()).build();new Runner(opt).run();}
}

结果如下:

Benchmark                                     (type)   Mode  Cnt        Score        Error  Units
MapBenchMark.map                   ConcurrentHashMap  thrpt    5  4903943.211 ± 208719.270  ops/s
MapBenchMark.map:getMap            ConcurrentHashMap  thrpt    5  2442687.631 ± 251150.685  ops/s
MapBenchMark.map:putMap            ConcurrentHashMap  thrpt    5  2461255.580 ± 260557.472  ops/s
MapBenchMark.map               ConcurrentSkipListMap  thrpt    5  3471371.602 ± 334184.434  ops/s
MapBenchMark.map:getMap        ConcurrentSkipListMap  thrpt    5  1710540.889 ± 196183.472  ops/s
MapBenchMark.map:putMap        ConcurrentSkipListMap  thrpt    5  1760830.713 ± 263480.175  ops/s
MapBenchMark.map                           Hashtable  thrpt    5  1966883.854 ± 197740.289  ops/s
MapBenchMark.map:getMap                    Hashtable  thrpt    5   676801.687 ±  71672.436  ops/s
MapBenchMark.map:putMap                    Hashtable  thrpt    5  1290082.167 ± 174730.435  ops/s
MapBenchMark.map         Collections.synchronizedMap  thrpt    5  1976316.282 ±  99878.457  ops/s
MapBenchMark.map:getMap  Collections.synchronizedMap  thrpt    5   655744.125 ±  73634.788  ops/s
MapBenchMark.map:putMap  Collections.synchronizedMap  thrpt    5  1320572.158 ±  75428.848  ops/s

我们可以看到,在 putMap 和 getMap 方法中,通过随机值的方式将取值作为 key 和 value 存入 map 中,同样也是通过随机值的方式将取值作为 key 从 map 中进行数据读取(当然读取的值可能并不存在)。还有我们在基准方法中进行了随机值的运算,虽然随机值计算所耗费的CPU时间也会被纳入基准结果的统计中,但是每一个 map 都进行了相关的计算,因此,我们可以认为大家还是站在了同样的起跑线上,故而可以对其忽略不计。

基准测试的数据可以表明,在5个线程同时进行 map 写操作,5个线程同时进行读操作时,参数 type=ConcurrentHashMap 的性能是最佳的 。

下一篇,将和大家介绍下JMH的profiler

相关文章:

Java基准测试工具JMH高级使用

去年&#xff0c;我们写过一篇关于JMH的入门使用的文章&#xff1a;Java基准测试工具JMH使用&#xff0c;今天我们再来聊一下关于JMH的高阶使用。主要我们会围绕着以下几点来讲&#xff1a; 对称并发测试非对称并发测试阻塞并发测试Map并发测试 关键词 State 在很多时候我们…...

问心 | 再看token、session和cookie

什么是cookie HTTP Cookie&#xff08;也叫 Web Cookie或浏览器 Cookie&#xff09;是服务器发送到用户浏览器并保存在本地的一小块数据&#xff0c;它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。 什么是session Session 代表着服务器和客户端一次会话…...

Ubuntu 安装 CUDA and Cudnn

文章目录0 查看 nvidia驱动版本1 下载Cuda2 下载cudnn参考&#xff1a;0 查看 nvidia驱动版本 nvidia-smi1 下载Cuda 安装之前先安装 gcc g gdb 官方&#xff1a;https://developer.nvidia.com/cuda-toolkit-archive&#xff0c;与驱动版本进行对应&#xff0c;我这里是12.0…...

【漏洞复现】Grafana任意文件读取(CVE-2021-43798)

docker环境搭建 #进入环境 cd vulhub/grafana/CVE-2021-43798#启动环境&#xff0c;这个过程可能会有点慢&#xff0c;保持网络通畅 docker-compose up -d#查看环境 docker-compose ps直接访问虚拟机 IP地址:3000 目录遍历原理 目录遍历原理&#xff1a;攻击者可以通过将包含…...

磨金石教育摄影技能干货分享|春之旅拍

春天来一次短暂的旅行&#xff0c;你会选择哪里呢&#xff1f;春天的照片又该如何拍呢&#xff1f;看看下面的照片&#xff0c;或许能给你答案。照片的构图很巧妙&#xff0c;画面被分成两部分&#xff0c;一半湖泊&#xff0c;一半绿色树林。分开这些的是一条斜向的公路&#…...

中断以及 PIC可编程中断控制器

1 中断分为同步中断&#xff08;中断&#xff09;和异步中断&#xff08;异常&#xff09; 1.1 中断和异常的不同 中断由IO设备和定时器产生&#xff0c;用户的一次按键会引起中断。异步。 异常一般由程序错误产生或者由内核必须处理的异常条件产生。同步。缺页异常&#xff…...

SecureCRT 安装并绑定ENSP设备终端

软件下载链接链接&#xff1a;https://pan.baidu.com/s/1WFxmQgaO9bIiUTwBLSR4OA?pwd2023 提取码&#xff1a;2023 CRT安装&#xff1a;软件可以从上面链接进行下载&#xff0c;下载完成后解压如下&#xff1a;首先双击运行scrt-x64.8.5.4 软件&#xff0c;进行安装点击NEXT选…...

ESP32设备驱动-TCS3200颜色传感器驱动

TCS3200颜色传感器驱动 1、TCS3200介绍 TCS3200 和 TCS3210 可编程彩色光频率转换器在单个单片 CMOS 集成电路上结合了可配置的硅光电二极管和电流频率转换器。 输出是方波(50% 占空比),其频率与光强度(辐照度)成正比。 满量程输出频率可以通过两个控制输入引脚按三个预…...

< JavaScript小技巧:Array构造函数妙用 >

文章目录&#x1f449; Array构造函数 - 基本概念&#x1f449; Array函数技巧用法1. Array.of()2. Array.from()3. Array.reduce()4. (Array | String).includes()5. Array.at()6. Array.flat()7. Array.findIndex()&#x1f4c3; 参考文献往期内容 &#x1f4a8;今天这篇文章…...

【17】组合逻辑 - VL17/VL19/VL20 用3-8译码器 或 4选1多路选择器 实现逻辑函数

VL17 用3-8译码器实现全减器 【本题我的也是绝境】 因为把握到了题目的本质要求【用3-8译码器】来实现全减器。 其实我对全减器也是不大清楚,但是仿照对全加器的理解,全减器就是低位不够减来自低位的借位 和 本单元位不够减向后面一位索要的借位。如此而已,也没有很难理解…...

2023年全国最新二级建造师精选真题及答案19

百分百题库提供二级建造师考试试题、二建考试预测题、二级建造师考试真题、二建证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 37.下列纠纷中&#xff0c;属于劳动争议范围的有()。 A.因劳动保护发生的纠纷 B.家庭与家政…...

Java中的 this 和 super

1 this 关键字 1.1 this 访问本类属性 this 代表对当前对象的一个引用 所谓当前对象&#xff0c;指的是调用当前类中方法或属性的那个对象this只能在方法内部使用&#xff0c;表示对“调用方法的那个对象”的引用this.属性名&#xff0c;表示本对象自己的属性 当对象的属性和…...

ESP32设备驱动-红外寻迹传感器驱动

红外寻迹传感器驱动 1、红外寻迹传感器介绍 红外寻迹传感器具有一对红外线发射管与接收管,发射管发射出一定频率的红外线,当检测方向遇到障碍物(反射面)时,红外线反射回来被接收管接收,经过比较器电路处理之后,输出接口会输出一个数字信号(低电平或高电平,取决于电路…...

初识BFC

初识BFC 先说如何开启BFC&#xff1a; 1.设置display属性&#xff1a;inline-block&#xff0c;flex&#xff0c;grid 2.设置定位属性&#xff1a;absolute&#xff0c;fixed 3.设置overflow属性&#xff1a;hidden&#xff0c;auto&#xff0c;scroll 4.设置浮动&#xf…...

随想录二刷Day17——二叉树

文章目录二叉树9. 二叉树的最大深度10. 二叉树的最小深度11. 完全二叉树的节点个数12. 平衡二叉树二叉树 9. 二叉树的最大深度 104. 二叉树的最大深度 思路1&#xff1a; 递归找左右子树的最大深度&#xff0c;选择最深的 1&#xff08;即加上当前层&#xff09;。 class So…...

Weblogic管理控制台未授权远程命令执行漏洞复现(cve-2020-14882/cve-2020-14883)

目录漏洞描述影响版本漏洞复现权限绕过漏洞远程命令执行声明&#xff1a;本文仅供学习参考&#xff0c;其中涉及的一切资源均来源于网络&#xff0c;请勿用于任何非法行为&#xff0c;否则您将自行承担相应后果&#xff0c;本人不承担任何法律及连带责任。 漏洞描述 Weblogic…...

STM32F103CubeMX定时器

前言定时器作为最重要的内容之一&#xff0c;是每一位嵌入式软件工程师必备的能力。STM32F103的定时器是非常强大的。1&#xff0c;他可以用于精准定时&#xff0c;当成延时函数来使用。不过个人不建议这么使用&#xff0c;因为定时器很强大&#xff0c;这么搞太浪费了。如果想…...

多态且原理

多态 文章目录多态多态的定义和条件协变&#xff08;父类和子类的返回值类型不同&#xff09;函数隐藏和虚函数重写的比较析构函数的重写关键字final和override抽象类多态的原理单继承和多继承的虚函数表单继承下的虚函数表多继承下的虚函数表多态的定义和条件 定义&#xff1…...

动态库(二) 创建动态库

文章目录创建动态库设计动态库ABI兼容动态符号的可见性示例控制符号可见性通过-fvisibility通过strip工具删除指定符号创建动态库 在Linux中创建动态库通过如下过程完成&#xff1a; gcc -fPIC -c first.c second.c gcc -shared first.o second.o -o libdynamiclib.so 按照Li…...

opencv加水印

本文介绍opencv给图片加水印的方法。 目录1、添加水印1.1、铺满1.2、在指定区域添加1.3、一比一铺满1、添加水印 添加水印的原理是调低两张图片的透明度&#xff0c;然后叠加起来。公式如下&#xff1a; dst src1 * opacity src2 * (1 - opacity) gamma; opacity是透明度&a…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

搭建DNS域名解析服务器(正向解析资源文件)

正向解析资源文件 1&#xff09;准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2&#xff09;服务端安装软件&#xff1a;bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...

PHP 8.5 即将发布:管道操作符、强力调试

前不久&#xff0c;PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5&#xff01;作为 PHP 语言的又一次重要迭代&#xff0c;PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是&#xff0c;借助强大的本地开发环境 ServBay&am…...

DBLP数据库是什么?

DBLP&#xff08;Digital Bibliography & Library Project&#xff09;Computer Science Bibliography是全球著名的计算机科学出版物的开放书目数据库。DBLP所收录的期刊和会议论文质量较高&#xff0c;数据库文献更新速度很快&#xff0c;很好地反映了国际计算机科学学术研…...

用 Rust 重写 Linux 内核模块实战:迈向安全内核的新篇章

用 Rust 重写 Linux 内核模块实战&#xff1a;迈向安全内核的新篇章 ​​摘要&#xff1a;​​ 操作系统内核的安全性、稳定性至关重要。传统 Linux 内核模块开发长期依赖于 C 语言&#xff0c;受限于 C 语言本身的内存安全和并发安全问题&#xff0c;开发复杂模块极易引入难以…...

leetcode_69.x的平方根

题目如下 &#xff1a; 看到题 &#xff0c;我们最原始的想法就是暴力解决: for(long long i 0;i<INT_MAX;i){if(i*ix){return i;}else if((i*i>x)&&((i-1)*(i-1)<x)){return i-1;}}我们直接开始遍历&#xff0c;我们是整数的平方根&#xff0c;所以我们分两…...

未授权访问事件频发,我们应当如何应对?

在当下&#xff0c;数据已成为企业和组织的核心资产&#xff0c;是推动业务发展、决策制定以及创新的关键驱动力。然而&#xff0c;未授权访问这一隐匿的安全威胁&#xff0c;正如同高悬的达摩克利斯之剑&#xff0c;时刻威胁着数据的安全&#xff0c;一旦触发&#xff0c;便可…...

spring boot使用HttpServletResponse实现sse后端流式输出消息

1.以前只是看过SSE的相关文章&#xff0c;没有具体实践&#xff0c;这次接入AI大模型使用到了流式输出&#xff0c;涉及到给前端流式返回&#xff0c;所以记录一下。 2.resp要设置为text/event-stream resp.setContentType("text/event-stream"); resp.setCharacter…...