第7章 CPU前端优化
接下来讨论如何使用CPU监控特性寻找CPU上运行的代码中可被调优的位置。
标准的算法和数据结构在性能敏感型工作负载并不总能表现的很好。例如,在“扁平化”数据结构的冲击下,链表基本上快被放弃了。传统链表中的每个节点都是动态分配的,除了引入耗时的内存分配操作,还可能让链表中所有元素分散在内存中,导致随机内存访问。
二分搜索在排序数组中查找元素方面是最优的,但是该算法经常会有很多分支预测错误的问题,这就是为何线性搜索在小型(少于20个元素)整型数组上表现得最好。
本章尝试专注于CPU微架构相关的优化,而不是覆盖所有你能想到的优化机会、不过也有必要列出上层的优化点:
1. 使用开销更低的语言重写程序的性能关键部分;
2. 分析程序中使用的算法和数据结构;
3. 调优编译器参数,检查至少使用了-O3(与机器无关的优化功能)、 -march(启用针对特定CPU系列的优化功能)和-flto(启用过程间优化功能);
4. 如果问题是高度并行化的计算,考虑把程序线程化或者放到GPU上运行;
5. 当等待IO操作时,使用同步IO以避免阻塞;
6. 利用更多的RAM来减少必须使用的CPU和IO量(记忆、查找表、数据缓存、压缩等);
数据驱动优化
数据驱动的优化是最重要的调优技术之一,它基于对程序正在处理的数据的洞察,聚焦于数据的分布及其在程序中的转换方式。典型的有SOA和AOS数据布局。如果程序遍历数据结构并且只访问指端b,那么SOA会更好。然而如果程序遍历数据结构并且访问该对象的所有字段都需要进行许多操作,那么AOS会更好。因为该数据的所有成员可能都会保留在相同的缓存行里。
另一个非常重要的数据驱动的优化是“小尺寸优化”,其理念是提前为容器分配一定量的内存,以避免动态内存分配。这对元素数据上限可以预测的中小尺寸容器非常有用。
实现的优化不一定对所有平台都有效果。例如循环阻塞非常依赖系统内存的层次特征,尤其是L2和L3缓存大小。在程序将要运行的平台上测试这些变化是非常重要的。
CPU前端低效指后端在等待指令来执行,但是前端不能给后端提供指令,原因归类为2种:缓存利用率和无法从内存中获取指令。建议只有当TMA显式较高的“前端bound”指标(大于20%)时,才关注CPU前端的代码优化。
7.1 机器码布局
当编译器将源代码翻译为机器码时,它会生成一个串行的字节列。其中指令在内存中放置的偏移位置,也会反过来影响二进制文件的性能。
7.2 基本块
基本块是指只有一个入口和一个出口的指令序列。虽然基本块可以有多个前导和后继,但是在基本块中间没有任何指令可以跳出基本块,保证基本块中的每条代码只会被执行1次,能大大地减少控制流图分析和转化的问题。
7.3 基本块布局
// hot path
if (cond)coldFunc();
// hot path again
如果cond通常为真,那么就选默认布局。因为另一个布局通常做2次而不是1次跳转。但是coldFunc是一个错误处理函数,并且不太可能会被经常执行,选择保持热点代码间的直通,并且把选取分支转化为未被选取分支。
选择热点代码间的直通的布局有原因如下:
1. 未被选取的分支比被选取时耗时更少。一般情况下,Intel CPU每个时钟可以执行2个未被选择的分支,但是每2个时钟周期才能执行一个被选取的分支。
2. 更充分利用指令和微操作缓存。因为所有热点代码都是连续的,所以没有缓存行碎片化问题。
3. 被选取的分支对于读取单元来说也更耗时。每个被选取的跳转指令都意味着跳转之后的字节都是都无效的。
可以使用__builtin_expect(cond, 0)注解告诉编译器概率高低。
7.4 基本块对齐
性能会由于指令在内存中的偏移量而发生明显的变化。若循环跨越多条缓存行,可能会导致CPU前端出现性能问题,所以我们可以使用nop指令将循环指令向前移动,以便让整个循环驻留在一条缓存行中。
LLVM使用-mllvm-align-all-blocks对齐基本块,注意它们可能导致性能劣化,插入nop指令,会增加程序的开销,尤其是当它们处于关键路径上。nop指令不需要被执行,但是它们仍然需要从内存中读取、解码和退休,额外地消耗前端数据结构和用于记账的缓冲区空间。
为了细粒度地控制对齐,还可以使用ALIGN汇编指令,针对实验场景,开发人员先生成汇编列表,然后插入ALIGN指令。
7.5 函数拆分
函数拆分的设想是把热点代码和冷代码区分开,该优化对在热路径中具有复杂CFG和大量冷代码的函数是有益的。
void foo(bool cond1, bool cond2) {// hot pathif (cond1) {//large amount of cold code cond1}// hot pathif (cond2) {//large amount of cold code cond2}
}// 优化后
void foo(bool cond1, bool cond2) {// hot pathif (cond1) {cold1()}// hot pathif (cond2) {cold2()}
}void cold1() __attribute_((noinline)) { // cold code (1)};
void cold2() __attribute_((noinline)) { // cold code (2)};
图中我们只保存了热路径的call指令,所以下一个热点代码指令可能会驻留在相同的缓存行,提升CPU前端数据结构(指令缓存和DSB)的利用率。留意其中的另一个重要思想:禁止内联冷函数。最后,创建的新函数要放在.text段之外。如果从不调用该函数,俺么它不会在运行时加载到内存中,所以可能会改善内存占用情况。
7.6 函数分组
热点函数可以被分组在一起以进一步提升CPU前端缓存的利用率,减少需要读取缓存行的数量。
链接器负责程序在最终的二进制输出中所有函数的排列布局。LLVM的LLD链接器使用--symbol-ordering-file优化函数的布局。
HFSort工具基于剖析数据自动生成分区排序文件。
7.7 基于剖析文件的编译优化
大多数编译器都有一组转换功能,可以根据反馈给它们的剖析数据来调整算法,被称为基于剖析文件的编译优化Profile Directed Optimization,PGO。
剖析数据生成方式有二:代码插桩核基于采样的剖析。
1. 先利用LLVM编译器使用-fprofile-instr-generate告诉编译器生成插桩代码。然后LLVM编译器使用-fprofile-inst-use利用剖析数据重新编译程序,并生成PGO调优的二进制文件。
2. 基于采样生成编译器所需的剖析数据。然后AutoFDO把linux perf生成的采样数据转换为GCC和LLVM的编译器可以理解的形式。不过编译器会假设所有负载的表现都一样。
7.8 对ITLB的优化
内存地址中虚地址到物理地址的翻译是前端性能调优的另一个重要领域。通过把应用程序的性能关键代码部分地映射到大页上,可以减少ITLB压力。这需要重新链接二进制文件,在合适的页边界对齐代码段。除了使用大页,用于优化指令缓存性能的标准技术也可以提升ITLB性能,即重排函数让热点函数更集中,通过LTO/IPO减小热点区域的大小,使用PGO并避免过度内联。
7.9 总结
| 转换 | 如何转换 | 优点 | 应用场景 | 执行者 |
| 基本块布局 | 维护热点代码的直通 | 未被选取的分支耗时更少;缓存利用率更高 | 任何代码,尤其是由很多分支的代码 | 编译器 |
| 基本块对齐 | 使用NOP指令对热点代码进行移位 | 缓存利用率更高 | 热点循环 | 编译器 |
| 函数拆分 | 把冷代码拆分出来并放到单独的函数中 | 缓存利用率更高 | 当在热代码间存在大段冷代码的函数时,具有复杂CFG的函数 | 编译器 |
| 函数分组 | 把热点函数分组到一起 | 缓存利用率更高 | 有很多热点小函数 | 链接器 |
相关文章:
第7章 CPU前端优化
接下来讨论如何使用CPU监控特性寻找CPU上运行的代码中可被调优的位置。 标准的算法和数据结构在性能敏感型工作负载并不总能表现的很好。例如,在“扁平化”数据结构的冲击下,链表基本上快被放弃了。传统链表中的每个节点都是动态分配的,除了…...
idea新建Java-maven项目时,出现Dependency‘xxx(jar包名)‘ not found的解决方案
项目场景: 项目场景:使用idea创建maven项目时,导入简单依赖时(本文以mysql-connector-java为例)。 问题描述 问题: 首先,在创建新的maven项目中,出现下列两种情况: &am…...
STM32--USART串口
文章目录 通信接口串口通信硬件电路电平标准参数时序 USART主要特性框图 数据帧发送器 波特率发生器SWART串口发送与接收工程串口收发数据包 通信接口 通信接口是指连接中央处理器(CPU)和标准通信子系统之间的接口,用于实现数据和控制信息在不…...
2023年Java毕业设计题目推荐,怎样选题?500道毕业设计题目推荐
大家好,我是程序员徐师兄,最近有很多同学咨询,说毕业设计了,不知道选怎么题目好,有哪些是想需要注意的。 今天,我整理了一些Java毕业设计的题目,可以参考一下,希望对大家有所帮助 文章目录 一、…...
基于数据湖的多流拼接方案-HUDI概念篇
目录 一、为什么需要HUDI? 1. 传统技术选型存在哪些问题? 2. Hudi有什么优点? 基于 Hudi Payload 机制的多流拼接方案: 二、HUDI的应用场景 1. 什么场景适合使用hudi? 2. 什么场景不适合使用hudi? …...
OpenCV基础知识(5)— 几何变换
前言:Hello大家好,我是小哥谈。OpenCV中的几何变换是指改变图像的几何结构,例如大小、角度和形状等,让图像呈现出缩放、翻转、旋转和透视效果。这些几何变换操作都涉及复杂、精密的计算。OpenCV将这些计算过程都封装成了非常灵活的…...
Linux下源码安装MySQL 8.0
MySQL 8.0源码安装 环境准备步骤 环境准备 Linux环境,本文基于CentOS 8 MySQL安装包,本文基于MySQL 8.1,以下为带boost MySQL 8.1源码下载地址: https://dev.mysql.com/get/Downloads/MySQL-8.1/mysql-boost-8.1.0.tar.gz 步骤…...
大聪明教你学Java | 深入浅出聊 Java 内存模型
前言 🍊作者简介: 不肯过江东丶,一个来自二线城市的程序员,致力于用“猥琐”办法解决繁琐问题,让复杂的问题变得通俗易懂。 🍊支持作者: 点赞👍、关注💖、留言💌~ 在多线程环境下,多个线程同时访问共享数据可能导致一系列问题,如数据不一致、竞态条件和死锁等…...
SAP ABAPG开发屏幕自动生成日期的搜索帮助
代码如下: REPORT z_jason_test_f4 . TABLES: s031. PARAMETER p_spmon TYPE spmon DEFAULT sy-datum0(6) OBLIGATORY. SELECT-OPTIONS s_spmon FOR s031-spmon DEFAULT sy-datum0(6) OBLIGATORY. AT SELECTION-SCREEN ON VALUE-REQUEST…...
leetcode 674. 最长连续递增序列
2023.8.24 与最长递增子序列 类似,不同的是, 本题要求连续序列,所以不需要第二层遍历比较之前所有的元素了,只需要比较上一个元素i-1。 dp[i]的含义为:以nums[i]元素为结尾的序列的最长递增子序列。 注意这里是以i为结…...
Mysql简短又易懂
MySql 连接池:的两个参数 最大连接数:可以同时发起的最大连接数 单次最大数据报文:接受数据报文的最大长度 数据库如何存储数据 存储引擎: InnoDB:通过执行器对内存和磁盘的数据进行写入和读出 优化SQL语句innoDB会把需要写入或者更新的数…...
vue 简单实验 v-model 变量和htm值双向绑定
1.代码 <script src"https://unpkg.com/vuenext" rel"external nofollow" ></script> <div id"two-way-binding"><p>{{ message }}</p><input v-model"message" /> </div> <script>…...
测试框架pytest教程(8)失败重试-pytest-rerunfailures
pytest-rerunfailures是一个pytest插件,用于重新运行失败的测试用例。当测试用例在第一次运行时失败,该插件会自动重新运行指定次数的失败用例,以提高稳定性和减少偶发性错误的影响。 要使用pytest-rerunfailures插件,需要按照以…...
6个主流的工业3D管道设计软件
3D 管道设计软件是大多数行业工程工作的主要部分,例如: 电力、石油和天然气、石化、炼油厂、纸浆和造纸、化学品和加工业。 全球各工程公司使用了近 50 种工厂或管道设计软件。 每个软件都有优点和缺点,包括价格点。 EPC 和业主部门当前的趋势…...
基于微信小程序的垃圾分类系统设计与实现(2.0 版本,附前后端代码)
博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 1 简介 视频演示地址: 基于微信小程序的智能垃圾分类回收系统,可作为毕业设计 小…...
基础论文学习(4)——CLIP
《Learning Transferable Visual Models From Natural Language Supervision》 CLIP的英文全称是Contrastive Language-Image Pre-training,即一种基于对比文本-图像对的预训练模型。CLIP是一种基于对比学习的多模态模型,与CV中的一些对比学习方法如moc…...
SpringBoot利用ConstraintValidator实现自定义注解校验
一、前言 ConstraintValidator是Java Bean Validation(JSR-303)规范中的一个接口,用于实现自定义校验注解的校验逻辑。ConstraintValidator定义了两个泛型参数,分别是注解类型和被校验的值类型。在实现ConstraintValidator接口时&…...
十、pikachu之php反序列化
文章目录 1、php反序列化概述2、实战3、关于Magic function4、__wakeup()和destruct() 1、php反序列化概述 在理解这个漏洞前,首先搞清楚php中serialize(),unserialize()这两个函数。 (1)序列化serialize():就是把一个…...
PHP“牵手”拼多多商品详情数据获取方法,拼多多API接口批量获取拼多多商品详情数据说明
拼多多商品详情接口 API 是开放平台提供的一种 API 接口,它可以帮助开发者获取拼多多商品的详细信息,包括商品的标题、描述、图片等信息。在拼多多电商平台的开发中,拼多多详情接口 API 是非常常用的 API,因此本文将详细介绍拼多多…...
前端面试:【Redux】状态管理的精髓
嘿,亲爱的Redux探险家!在前端开发的旅程中,有一个强大的状态管理工具,那就是Redux。Redux是一个状态容器,它以一种可预测的方式管理应用的状态,通过Store、Action、Reducer、中间件和异步处理等核心概念&am…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...
安卓基础(Java 和 Gradle 版本)
1. 设置项目的 JDK 版本 方法1:通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分,设置 Gradle JDK 方法2:通过 Settings File → Settings... (或 CtrlAltS)…...
云安全与网络安全:核心区别与协同作用解析
在数字化转型的浪潮中,云安全与网络安全作为信息安全的两大支柱,常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异,并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全:聚焦于保…...
