当前位置: 首页 > 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…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

【单片机期末】单片机系统设计

主要内容&#xff1a;系统状态机&#xff0c;系统时基&#xff0c;系统需求分析&#xff0c;系统构建&#xff0c;系统状态流图 一、题目要求 二、绘制系统状态流图 题目&#xff1a;根据上述描述绘制系统状态流图&#xff0c;注明状态转移条件及方向。 三、利用定时器产生时…...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机&#xff08;Finite Automaton, FA&#xff09;到正规文法&#xff08;Regular Grammar&#xff09;转换器&#xff0c;它配备了一个直观且完整的图形用户界面&#xff0c;使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…...

安卓基础(Java 和 Gradle 版本)

1. 设置项目的 JDK 版本 方法1&#xff1a;通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分&#xff0c;设置 Gradle JDK 方法2&#xff1a;通过 Settings File → Settings... (或 CtrlAltS)…...

DAY 26 函数专题1

函数定义与参数知识点回顾&#xff1a;1. 函数的定义2. 变量作用域&#xff1a;局部变量和全局变量3. 函数的参数类型&#xff1a;位置参数、默认参数、不定参数4. 传递参数的手段&#xff1a;关键词参数5 题目1&#xff1a;计算圆的面积 任务&#xff1a; 编写一…...

flow_controllers

关键点&#xff1a; 流控制器类型&#xff1a; 同步&#xff08;Sync&#xff09;&#xff1a;发布操作会阻塞&#xff0c;直到数据被确认发送。异步&#xff08;Async&#xff09;&#xff1a;发布操作非阻塞&#xff0c;数据发送由后台线程处理。纯同步&#xff08;PureSync…...

【1】跨越技术栈鸿沟:字节跳动开源TRAE AI编程IDE的实战体验

2024年初&#xff0c;人工智能编程工具领域发生了一次静默的变革。当字节跳动宣布退出其TRAE项目&#xff08;一款融合大型语言模型能力的云端AI编程IDE&#xff09;时&#xff0c;技术社区曾短暂叹息。然而这一退场并非终点——通过开源社区的接力&#xff0c;TRAE在WayToAGI等…...

深入浅出JavaScript中的ArrayBuffer:二进制数据的“瑞士军刀”

深入浅出JavaScript中的ArrayBuffer&#xff1a;二进制数据的“瑞士军刀” 在JavaScript中&#xff0c;我们经常需要处理文本、数组、对象等数据类型。但当我们需要处理文件上传、图像处理、网络通信等场景时&#xff0c;单纯依赖字符串或数组就显得力不从心了。这时&#xff…...