JVM常见概念之条件移动
问题
当我们有分支频率数据时,有什么有趣的技巧可以做吗?什么是条件移动?
基础知识
如果您需要在来自一个分支的两个结果之间进行选择,那么您可以在 ISA 级别做两件不同的事情。
首先,你可以创建一个分支:
# %r = (%rCond == 1) ? $v1 : $v2cmp %rCond, $1jne Amov %r, $v1jmp EA: mov %r, $v2E:
其次,您可以执行依赖于比较结果的预测指令 。在 x86 中,这采用条件移动 (CMOV) 的形式,当选定条件成立时执行操作:
# %r = (%rCond == 1) ? $v1 : $v2mov %r, $v1 ; put $v1 to %rcmp %rCond, ...cmovne %r, $v2 ; put $v2 to %r if condition is false
执行条件移动的优点是它有时会生成更紧凑的代码,就像在这个例子中一样,并且它不会受到可能的分支预测错误惩罚。缺点是它需要在选择返回哪一边之前计算两边,这可能会花费过多的 CPU 周期,增加寄存器压力等。在分支情况下,我们可以选择在检查条件后不计算内容。预测良好的分支将优于条件移动。
因此,是否执行条件移动的选择在很大程度上取决于其成本预测。这就是分支分析可以帮助我们的地方:它可以说出哪些分支可能没有被完美预测,因此适合 CMOV 替换。当然, 实际成本模型还包括我们正在处理的参数类型、两个计算分支的相对深度等。
实验
源码-用例1
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(1)
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class BranchFrequency {@Benchmarkpublic void fair() {doCall(true);doCall(false);}@CompilerControl(CompilerControl.Mode.DONT_INLINE)public int doCall(boolean condition) {if (condition) {return 1;} else {return 2;}}
}
执行结果
我们在每次调用时都会在分支之间进行切换,这意味着它的运行时配置文件在它们之间大约是 50%-50%。如果我们通过提供 -XX:ConditionalMoveLimit=0 来限制条件移动替换,那么我们就可以清楚地看到替换的发生。
# doCall, out of box variant4.36% ...4ac: mov $0x1,%r11d ; move $1 -> %r113.24% ...4b2: mov $0x2,%eax ; move $2 -> %res8.46% ...4b7: test %edx,%edx ; test boolean0.02% ...4b9: cmovne %r11d,%eax ; if false, move %r11 -> %res7.88% ...4bd: add $0x10,%rsp ; exit the method8.12% ...4c1: pop %rbp18.60% ...4c2: cmp 0x340(%r15),%rsp...4c9: ja ...4d00.14% ...4cf: retq# doCall, CMOV conversion inhibited6.48% ...cac: test %edx,%edx ; test boolean╭ ...cae: je ...cc8│ ; if true...│ ...cb0: mov $0x1,%eax ; move $1 -> %res7.41% │↗ ...cb5: add $0x10,%rsp ; exit the method0.02% ││ ...cb9: pop %rbp27.43% ││ ...cba: cmp 0x340(%r15),%rsp││ ...cc1: ja ...ccf3.28% ││ ...cc7: retq││ ; if false...7.04% ↘│ ...cc8: mov $0x2,%eax ; move $2 -> %res0.02% ╰ ...ccd: jmp ...cb5 ; jump back
在此示例中,CMOV 版本的表现稍好一些:
Benchmark Mode Cnt Score Error Units# Branches
BranchFrequency.fair avgt 25 5.422 ± 0.026 ns/op
BranchFrequency.fair:L1-dcache-loads avgt 5 12.078 ± 0.226 #/op
BranchFrequency.fair:L1-dcache-stores avgt 5 5.037 ± 0.120 #/op
BranchFrequency.fair:branch-misses avgt 5 0.001 ± 0.003 #/op
BranchFrequency.fair:branches avgt 5 10.037 ± 0.216 #/op
BranchFrequency.fair:cycles avgt 5 14.659 ± 0.285 #/op
BranchFrequency.fair:instructions avgt 5 35.184 ± 0.559 #/op# CMOVs
BranchFrequency.fair avgt 25 4.799 ± 0.094 ns/op
BranchFrequency.fair:L1-dcache-loads avgt 5 12.014 ± 0.329 #/op
BranchFrequency.fair:L1-dcache-stores avgt 5 5.005 ± 0.167 #/op
BranchFrequency.fair:branch-misses avgt 5 ≈ 10⁻⁴ #/op
BranchFrequency.fair:branches avgt 5 7.054 ± 0.118 #/op
BranchFrequency.fair:cycles avgt 5 12.964 ± 1.451 #/op
BranchFrequency.fair:instructions avgt 5 36.285 ± 0.713 #/op
您可能认为这是因为 CMOV 没有分支预测失误惩罚,但这种解释与计数器不一致。请注意,在两种情况下,“分支失误”几乎为零。这是因为硬件分支预测器实际上可以记住一个短暂的分支历史,而这种反复出现的分支对它们来说没有任何问题。性能差异的实际原因是分支情况下的跳跃:我们在关键路径上有一条额外的控制流指令。
源码-用例2
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Fork(1)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
public class AdjustableBranchFreq {@Param("50")int percent;boolean[] arr;@Setup(Level.Iteration)public void setup() {final int SIZE = 100_000;final int Q = 1_000_000;final int THRESH = percent * Q / 100;arr = new boolean[SIZE];ThreadLocalRandom current = ThreadLocalRandom.current();for (int c = 0; c < SIZE; c++) {arr[c] = current.nextInt(Q) < THRESH;}// Avoid uncommon traps on both branches.doCall(true);doCall(false);}@Benchmarkpublic void test() {for (boolean cond : arr) {doCall(cond);}}@CompilerControl(CompilerControl.Mode.DONT_INLINE)public int doCall(boolean condition) {if (condition) {return 1;} else {return 2;}}
}
执行结果
使用不同的 percent 值和 -prof perfnorm JMH 分析器运行它将产生以下结果:
依据上图,你可以清楚地看到几件事:
- 每个测试的分支数约为 5,而 CMOV 转换将其降至 4。这与之前的反汇编转储相关:我们将测试中的一个分支转换为 CMOV。另外 4 个分支来自测试基础设施本身。
- 如果没有 CMOV,分支测试性能会受到影响,在 50% 的分支概率下会变得最差。这个峰值反映了硬件分支预测器几乎完全混乱,因为它每次操作都会遇到大约 0.5 次分支失误。这意味着分支预测器并不是一直猜错(这太荒谬了!),而只是一半的时间猜错。我推测基于历史的预测器会放弃,让静态预测器选择最近的分支,而我们只选择了一半的时间。
- 使用 CMOV 后,我们可以看到操作时间几乎持平 。该图表明 CMOV 成本模型对于此测试来说可能过于保守,并且切换得有点晚。这并不一定意味着它有错误,因为其他情况的表现很可能会有所不同。尽管如此,当进行 CMOV 转换时,对分支情况的改进是巨大的。
- 您可能会注意到,当分支预测准确率为 >97% 时,分支变体会低于 CMOV 中间平均值。当然,这又是测试、硬件、虚拟机特有的事情。
总结
分支分析允许在执行概率敏感指令选择时做出或多或少明智的选择。条件移动替换通常使用分支频率信息来驱动替换。这再次强调了使用与真实数据类似的数据来预热 JIT 编译代码的必要性,以便编译器能够针对特定情况进行有效优化。
相关文章:
JVM常见概念之条件移动
问题 当我们有分支频率数据时,有什么有趣的技巧可以做吗?什么是条件移动? 基础知识 如果您需要在来自一个分支的两个结果之间进行选择,那么您可以在 ISA 级别做两件不同的事情。 首先,你可以创建一个分支ÿ…...
Android AI ChatBot-v1.6.3-28-开心版[免登录使用GPT-4o和DeepSeek]
Android AI ChatBot- 链接:https://pan.xunlei.com/s/VOLi1Ua071S6QZBGixcVL5eeA1?pwdp3tt# 免登录使用GPT-4o和DeepSeek...
集成学习(上):Bagging集成方法
一、什么是集成学习? 在机器学习的世界里,没有哪个模型是完美无缺的。就像古希腊神话中的"盲人摸象",单个模型往往只能捕捉到数据特征的某个侧面。但当我们把多个模型的智慧集合起来,就能像拼图一样还原出完整的真相&a…...
DeepSeek R1 本地部署指南 (3) - 更换本地部署模型 Windows/macOS 通用
0.准备 完成 Windows 或 macOS 安装: DeepSeek R1 本地部署指南 (1) - Windows 本地部署-CSDN博客 DeepSeek R1 本地部署指南 (2) - macOS 本地部署-CSDN博客 以下内容 Windows 和 macOS 命令执行相同: Windows 管理员启动:命令提示符 CMD ma…...
【TI MSPM0】Timer学习
一、计数器 加法计数器:每进入一个脉冲,就加一减法计算器:每进入一个脉冲,就减一 当计数器减到0,触发中断 1.最短计时时间 当时钟周期为1khz时,最短计时时间为1ms,最长计时时间为65535ms 当时…...
Windows部署deepseek R1训练数据后通过AnythingLLM当服务器创建问答页面
如果要了解Windows部署Ollama 、deepseek R1请看我上一篇内容。 这是接上一篇的。 AnythingLLM是一个开源的全栈AI客户端,支持本地部署和API集成。它可以将任何文档或内容转化为上下文,供各种语言模型(LLM)在对话中使用。以下是…...
重删算法中的Bloom滤波器详解与C++实现
一、Bloom滤波器基础概念 Bloom滤波器(Bloom Filter)是一种空间高效的概率型数据结构,用于快速判断某个元素是否存在于集合中。其核心特性: 存在不确定性:可能出现假阳性(False Positive)&…...
信奥赛CSP-J复赛集训(模拟算法专题)(27):P5016 [NOIP 2018 普及组] 龙虎斗
信奥赛CSP-J复赛集训(模拟算法专题)(27):P5016 [NOIP 2018 普及组] 龙虎斗 题目背景 NOIP2018 普及组 T2 题目描述 轩轩和凯凯正在玩一款叫《龙虎斗》的游戏,游戏的棋盘是一条线段,线段上有 n n n 个兵营(自左至右编号 1 ∼ n 1 \sim n 1∼n),相邻编号的兵营之间…...
多模态大模型常见问题
1.视觉编码器和 LLM 连接时,使用 BLIP2中 Q-Former那种复杂的 Adaptor 好还是 LLaVA中简单的 MLP 好,说说各自的优缺点? Q-Former(BLIP2): 优点:Q-Former 通过查询机制有效融合了视觉和语言特征…...
SpringBoot项目实战(初级)
目录 一、数据库搭建 二、代码开发 1.pom.xml 2.thymeleaf模块处理的配置类 3.application配置文件 4.配置(在启动类中) 5.编写数据层 ②编写dao层 ③编写service层 接口 实现类 注意 补充(注入的3个注解) 1.AutoWir…...
Linux NFS、自动挂载与系统启动管理指南
1. NFS客户端挂载导出的目录的方式 NFS(网络文件系统) 允许将远程服务器的目录挂载到本地,像访问本地文件一样操作远程文件。挂载方式主要有两种: 手动挂载:使用 mount 命令(临时生效,重启后丢…...
uniapp实现全局拖拽按钮
要先引入 “vue3-draggable-resizable”: “^1.6.5” 1.创建DragComponent组件 <template><!-- 抽屉组件 --><div class"drag-container" id"dragBox" :style"{ zIndex: zIndex }"><Vue3DraggableResizable :initW"…...
SOFABoot-10-聊一聊 sofatboot 的十个问题
前言 大家好,我是老马。 sofastack 其实出来很久了,第一次应该是在 2022 年左右开始关注,但是一直没有深入研究。 最近想学习一下 SOFA 对于生态的设计和思考。 sofaboot 系列 SOFABoot-00-sofaboot 概览 SOFABoot-01-蚂蚁金服开源的 s…...
计算机网络——总结
01. 网络的发展及体系结构 网络演进历程 从1969年ARPANET的4个节点发展到如今覆盖全球的互联网,网络技术经历了电路交换到分组交换、有线连接到无线覆盖的革命性变革。5G时代的到来使得网络传输速度突破10Gbps,物联网设备数量突破百亿级别。 网络体系…...
Umi-OCR- OCR 文字识别工具,支持截图、批量图片排版解析
Umi-OCR 是免费开源的离线 OCR 文字识别软件。无需联网,解压即用,支持截图、批量图片、PDF 扫描件的文字识别,能识别数学公式、二维码,可生成双层可搜索 PDF。内置多语言识别库,界面支持多语言切换,提供命令…...
高速网络包处理,基础网络协议上内核态直接处理数据包,XDP技术的原理
文章目录 预备知识TCP/IP 网络模型(4层、7层)iptables/netfilterlinux网络为什么慢 DPDKXDPBFPeBPFXDPXDP 程序典型执行流通过网络协议栈的入包XDP 组成 使用 GO 编写 XDP 程序明确流程选择eBPF库编写eBPF代码编写Go代码动态更新黑名单 预备知识 TCP/IP…...
C++:背包问题习题
1. 货币系统 1371. 货币系统 - AcWing题库 给定 V 种货币(单位:元),每种货币使用的次数不限。 不同种类的货币,面值可能是相同的。 现在,要你用这 V 种货币凑出 N 元钱,请问共有多少种不同的…...
数据可信安全流通实战,隐语开源社区Meetup武汉站开放报名
隐语开源社区 Meetup 系列再出发!2025 年将以武汉为始发站,聚焦"技术赋能场景驱动",希望将先进技术深度融入数据要素流转的各个环节,推动其在实际应用场景中落地生根,助力释放数据要素的最大潜能!…...
java使用Apache POI 操作word文档
项目背景: 当我们对一些word文档(该文档包含很多的标题比如 1.1 ,1.2 , 1.2.1.1, 1.2.2.3)当我们删除其中一项或者几项时,需要手动的对后续的进行补充。该功能主要是对标题进行自动的补充。 具…...
【 C/C++ 包管理工具】vcpkg安装+使用
【 C/C 包管理工具】vcpkg安装使用 Vcpkg 是由 Microsoft 和 C 社区维护的免费开源 C/C 包管理器,可在 Windows、macOS 和 Linux 上运行。 可以很方便的安装管理 C/C 库。 1. 安装 不要安装到Program Files这种有空格的路径下,否则后面安装库可能出现…...
免费开源的NAS解决方案:TrueNAS
TrueNAS是业内知名的FreeNAS系统的升级版,是一款开源的网络存储系统,具有高性能、稳定性和易用性等优点。 TrueNAS目前有三个版本,分别是TrueNAS CORE、TrueNAS ENTERPRISE、TrueNAS SCALE。其中,TrueNAS CORE基于FreeBSD开发&…...
LeetCode热题100精讲——Top1:两数之和【哈希】
你好,我是安然无虞。 文章目录 题目背景两数之和C解法Python解法 题目背景 如果大家对于 哈希 类型的概念并不熟悉, 可以先看我之前为此专门写的算法详解: 蓝桥杯算法竞赛系列第九章巧解哈希题,用这3种数据类型足矣 两数之和 题目链接:两数…...
github上传操作简单说明
前期准备 0.下载git(如果已经有了就不用了) 1.在GitHub上新建一个存储库 2.先在本地创建一个目录作为本地库目录,在目录里打开git bash进行上传 上传过程 echo "# Garbled_repair" >> README.md 作用:创建一个…...
GitLens with `Commit Graph`
文章目录 GitLens with Commit Graph GitLens with Commit Graph 想要更直观地查看 Git 提交历史?我打包了一个支持 Commit Graph 的 GitLens 版本,让你轻松在 VSCode 中查看分支、合并、变更记录等内容,一目了然! 📌…...
Rocky9.5基于sealos快速部署k8s集群
首先需要下载 Sealos 命令行工具,sealos 是一个简单的 Golang 二进制文件,可以安装在大多数 Linux 操作系统中。 以下是一些基本的安装要求: 每个集群节点应该有不同的主机名。主机名不要带下划线。 所有节点的时间需要同步。 需要在 K8s …...
阿里云服务器环境部署 四 MySQL主从配置
安装MySQL 导入mysql镜像 docker load -i /opt/dockerinstall/mysql/mysql-8.1.0.tar docker run --privilegedtrue --name mysql8 --restartunless-stopped -e MYSQL_ROOT_PASSWORD123456 -p 3306:3306 -v /usr/local/mysql/logs:/var/log/mysql -v /usr/local/mysql/d…...
GPT-5 将免费向所有用户开放?
GPT-5 将免费向所有用户开放? 硅谷知名分析师 Ben Thompson 最近与 OpenAI CEO Sam Altman 进行了一场深度对谈,其中Sam Altman透漏GPT-5将免费向大家发放。 OpenAI 这波操作可不是一时冲动,而是被逼出来的。DeepSeek 这个新秀横空出世&am…...
web客户端存储,IndexDB相关讲解
IndexDB详细讲解 IndexedDB 是浏览器提供的一种底层 API,用于在客户端存储大量结构化数据。相比 Web Storage(localStorage/sessionStorage),它支持更复杂的数据结构、事务处理、索引查询等高级功能。以下是一个系统化的讲解: 一、核心概念 1、数据库(Database) 每…...
excel文件有两列,循环读取文件两列赋值到字典列表。字典的有两个key,分别为question和answer。将最终结果输出到json文件
import pandas as pd import json# 1. 读取 Excel 文件(假设列名为 question 和 answer) try:df pd.read_excel("input.xlsx", usecols["question", "answer"]) # 明确指定列 except Exception as e:print(f"读取文…...
项目日记 -云备份 -服务器配置信息模块
博客主页:【夜泉_ly】 本文专栏:【项目日记-云备份】 欢迎点赞👍收藏⭐关注❤️ 代码已上传 gitee 目录 前言配置信息文件文件配置类getInstance 获得实例readConfigFile 读取配置信息文件 测试 #mermaid-svg-ewlCpjdOf0q0VTLI {font-family:…...
