【System Beats!】第五章 优化程序性能

news/2025/10/28 15:59:57/文章来源:https://www.cnblogs.com/namelessstory/p/19172053

性能优化概况

  • 在实际生活中,需要提升软件性能,最终目标是编写高效的代码,最大限度地利用硬件资源。
  • 性能优化通常考虑以下三方面:
    • 选择恰当的算法和数据结构
    • 理解编译器的能力和局限性
    • 大规模任务下进行并行计算
  • 需要在简单性和运行速度上进行权衡
  • 注意避免"渐进低效率"的情况,即在小数据集上表现良好,但在实际部署后,由于数据规模增大,原本的算法或数据结构反而成为性能瓶颈。
  • 许多低级别的优化往往会降低可读性和模块性,使得容易出错,并且更难以修改。
  • 最好的编译器也收到妨碍优化的因素。

编译器(Compiler)

  • 在代码优化中扮演直观重要角色
  • 可以进行以下操作
    • 执行寄存器的分配和调度
    • 代码的重新排序和规划
    • 赘余代码的消除
  • 不会引入可能引入风险的优化,即优化后得到的程序与未优化的版本有一样行为
  • 大多数优化在函数内部
  • 最简单的控制是指定优化级别
    • -Og是让GCC进行基本优化
    • -O1,-O2,-O3是让GCC进行更大量的优化
    • 可以提高程序性能
    • 也可能增加程序规模,使标准调试工具更难对程序进行调试
    • 正如在Attack Lab中看到的一样

普适性优化(Machine-independent)

消除循环低效率

  • 这种优化也被称为代码移动
  • 比较以下两段代码
int combine_1(vector<int>& nums,int x){int n=nums.size();for(int i=0;i<=n-1;i++){x+=nums[i];}return x;
}
int combine_2(vector<int>& nums,int x){for(int i=0;i<=nums.size();i++){x+=nums[i];}return x;
}
  • 显然,combine_1的性能优于combine_2
  • 但是,如果nums.size()有副作用,这两者可能有不同的行为

减少过程调用

  • 同上,可以将内循环中调用的函数移出循环体。

消除不必要的内存调用

  • 若每次都要访问内存并进行读写,程序的速度会大幅下降。
  • 参考以下代码:
void combine_3(vec_ptr v,data_t *dest){long i;long length=vec_length(v);data_t *data=get_vec_start(v);*dest=IDENT;for(i=0;i<length;i++){*dest=*dest OP data[i];}return ;
}
void combine_4(vec_ptr v,data_t *dest){long i;long length=vec_length(v);data_t *data=get_vec_start(v);data_t acc=IDENT;for(i=0;i<length;i++){acc=acc OP data[i];}*data=acc;return ;
}
  • 可以观察到,此时combine_4最后才把计算出的值写回,消除了不必要的内存引用。

降低运算复杂性

  • 通过将复杂的运算转化为简单的运算,例如将乘除法转化为移位操作,可以降低运算的复杂性。
  • 当乘法需要多个CPU时钟周期时,这种转换可以有效节省时间。

函数副作用

  • 即函数修改全局程序状态的一部分
  • 参考以下代码
long f();
long func1(){return f()+f()+f()+f();
}
long func2(){return 4*f();
}
long counter=0;
long f(){return counter++;
}
  • 当上文中的f采用修改全局状态的定义时,则发生副作用
  • 此时,func1func2的返回值不同。
  • 可以使用内联函数替换,即用inline将一些简单函数声明为内联函数。
  • 将函数调用替换为函数体。
  • 当编译器为funline-O1或更高等级时,编译器会尝试使用单文件中的函数内联,但是无法跨文件内联。
  • 在使用GDB代码剖析评估程序性能时,避免使用内联函数。

内存别名使用

  • 内存别名使用:两个指针可能指向同一个内存位置
  • 在只执行安全的优化中,编译器必须假设不同指针可能指向同一个内存位置
  • 这造成了一个主要的妨碍优化的因素

特殊优化

  • 需要考虑特定的处理器/ISA架构。

跳转/条件跳转

  • 当处理器计算跳转目标地址时,编译器可以优化程序,让处理器在跳转前的阶段执行一些不相关的指令。

充分运行CPU各个算术单元

  • 现代CPU的执行阶段通常包含多个算术单元。
  • 在进行多周期运算(如乘法)时,可以合理安排顺序,使运算流水线化。
  • 并且可以让CPU在每个周期内同时执行多个运算。

超标量CPU(Supercalar Processor)

  • 可以在一个周期内发射(issue)和执行(execute)多条指令,实现指令级并行(issue位于decode和execute之间)。
  • 同时,这些指令是乱序的。
  • 现代处理器通常是4发射(一次发射4条指令)。
  • 现代CPU通常分为指令控制单元(ICU)和执行单元(EU)
  • 取指:
    • ICU从指令高速缓存中读取指令
    • 指令高速缓存是一个特殊高速存储器,包含最近访问指令。
    • ICU会在当前正在执行的指令很早之前取指。
    • 遇到分支时,ICU采用分支预测和投机执行策略。
    • 投机执行:开始取出预测分支跳到的地方的指令,对指令译码甚至执行。
    • 最终结果不会放在程序寄存器或者数据内存中,直到处理器能确定是否应该实际执行这些指令。
    • 若错误,会将状态设置为分支点的状态,开始取出和执行另一个方向的指令。
  • 执行:
    • EU包含多个功能单元,例如load/store单元,整数运算器浮点运算器分支器等。
    • 执行单元之间可以直接交换信息,可以并行执行多条指令的不同部分。
    • 会接收分支操作,确定分支操作是否正确。
    • 若错误,则会发信号给分支单元,说预测是错误的,并指出正确的分支目的。
    • 执行单元之间使用寄存器重命名机制来避免数据冲突。
    • 寄存器重命名:类似于转发机制,会更新寄存器的操作被写入寄存器表。
    • 随后,等待它进行源的操作都是用其作为源值。
    • 若在寄存器表找不到标记,则去寄存器堆里找。
  • 写回:
    • ICU中的退役单元(retirement unit)确保操作按照程序顺序执行。
    • 若一条指令的操作完成了,并且所有引起这条指令的分支点都正确,那么其可以退役(retirement)。
    • 若某个分支点预测错误,它会被清空(flushed),丢弃所有计算出的结果。

表示程序性能

CPE(Cycle-Per-Element)

  • 指的是每个元素运算所需的时钟周期数
  • 总周期 \(T\) 可以表示为 \(T=n \times CPE + Overhead\)
  • 其中 \(n\) 是元素数量, \(Overhead\) 是其他开销
  • 单位用千兆赫,即 GHz 表示,意为每秒能执行 n*10^9条指令
  • 当数据规模很大时,可以忽略常数项

功能单元的性能:

  • 在本节中,以Intel Core i7 Haswell作为参考机。
  • 它有八个功能单元,其中:
    • 四个功能单元可以执行整数操作(加法、移位、乘法)
    • 两个单元可以执行加载操作
    • 两个单元可以执行浮点乘法
  • 主要包括以下三部分:
    • 延迟:表示完成运算所需要的总时间。
    • 发射时间:表示两个连续的同类型运算之间需要的最小时钟周期数。
    • 容量:能执行该运算的功能单元的数量。
  • 例如,在参考机上,各运算的数据如下:

  • 发射时间为1的运算被称为完全流水线化的(fully pipelined)
  • 容量大于1的运算是由于有多个功能单元。
  • 部分指令的最小延迟(也就是CPE)超过1个周期,但是可以被流水线化。
  • 例如,load/store(4周期),整数乘法(3周期),浮点加法(3),浮点乘法(5)都可以被流水线化。
  • 因为,每个周期只占用一部分运算单元。
  • 整数与浮点出发则比较复杂,且不能被流水线化(全程占用全部运算单元,发射时间=延迟)。
  • 处理器吞吐量:吞吐量=每时钟周期(容量/发射时间)个操作。
  • 延迟界限:任何必须按照严格顺序完成合并运算的函数所需要的最小CPE值。
  • 吞吐量界限:给出CPE的最小界限,需要额外考虑加载/存储操作等等。
  • 下面是上述运算的两个基本界限

处理器操作的抽象类型

  • 需要使用程序的数据流表示
  • 限制形成关键路径,是执行一组机器指令时所需时钟周期数的一个下界。
  • 大概可以理解为,在下一次迭代前无法开始的指令,是关键路径的候选者。
  • 循环访问的寄存器可以分为以下四类:
    • 只读,只用作源值,可以作为数据或计算内存地址,但不会被修改。
    • 只写:作为数据传送的目的。
    • 局部:在循环内部被修改和使用,迭代和迭代之间不相关,例如条件码寄存器。
    • 循环:既作为源值,又作为目的,一次迭代中产生的值会在另一次迭代中被用到。

循环优化

  • 一种优化手段,希望突破延迟界限,使吞吐量界限成为唯一限制。
  • 循环的性能由所有循环寄存器完成一次循环所需的最长时间(关键路径长度)所制约。

循环展开

  • 通过增加每次迭代的元素来减少循环次数,只适用于循环控制开销大于循环内计算开销的情况。
  • -O3或者更高优化等级时,编译器会自动展开循环。
  • 例如以下代码:
void combine5(vec_ptr v,data_t *dest){long i;long length=vec_length(v);long limit=length-1;data_t *data=get_vec_start(v);data_t acc=IDENT;for(i=0;i<limit;i+=2){acc=(acc OP data[i]) OP data[i+1];}for(;i<length;i++){acc=acc OP data[i];}*dest=acc;return ;
}
  • 可以观察到,迭代次数减少了,但是乘法的关键路径还是不变。
  • 因此,没有突破延迟限制。

循环展开-多个累计变量

  • 循环分解将满足交换律和结合律的合并运算分割为多个部分
  • 在最后合并结果,适用于循环内的数据相关性是主要制约因素的情况。
  • 注意,浮点加和浮点乘不总是满足结合律。
  • 比如以下代码:
void combine6(vec_ptr v,data_t *dest){long i;long length=vec_length(v);long limit=length-1;data_t *data=get_vec_start(v);data_t acc0=IDENT;data_t acc1=IDENT;for(i=0;i<limit;i+=2){acc0=acc0 OP data[i];acc1=acc1 OP data[i+1]; }for(;i<length;i++){acc0=acc0 OP data[i];}*dest=acc0 OP acc1;return ;
}
  • 可以看到,此时acc0acc1没有迭代依赖了,关键路径变短。
  • 通常只有保持能够执行该操作的所有功能单元的流水线都是满的,程序才能达到吞吐量界限。
  • 例如对延迟为 \(L,\) 容量为 \(C\) 的操作而言,循环展开因子需要达到 \(k \geq C \cdot L\)
  • 并行度过大时,会导致寄存器溢出(可用寄存器不够),从而编译器将部分变量存储于内存中,降低性能。

循环展开-重新结合变换

  • 在基础的循环展开中重新排列运算顺序,优先结合只读元素,减少迭代内的数据相关性。
  • 比如,以下代码:
void combine7(vec_ptr v,data_t *dest){long i;long length=vec_length(v);long limit=length-1;data_t *data=get_vec_start(v);data_t acc=IDENT;for(i=0;i<limit;i+=2){acc=acc OP (data[i] OP data[i+1]);}for(;i<length;i++){acc=acc OP data[i];}*data=acc;
}
  • 画出数据流,可以观察到由乘法迭代引起的数据相关缩短了,关键路径也缩短了一半。

补充

SIMD(Single Instruction Multiple Data)

  • Intel基于SIMD引入了SSE(Streaming SIMD Extension)指令集
  • 它使用AVX向量寄存器(32字节)和GPU等独立部件进行并行计算,适用于向量加法等操作。

有关分支

  • 使用条件传送操作可以有效较少分支预测错误的概率。
  • 不要过分关心可预测的分支。
  • 书写适合用条件传送实现的代码。

理解内存性能

  • 由于加载操作的地址只依赖于循环索引,因此不会成为关键路径的一部分。
  • 例如,在参考机上,由于只有两个加载单元。
  • 因此,对于每个被计算的元素必须加载 \(k\) 个值的应用,不可能获得低于 \(\frac{k}{2}\) 的CPE。
  • 存储单元包含一个存储缓冲区,包含已经被发射到存储单元而又没有完成的存储操作的地址和数据。
  • 操作包括更新数据告诉缓存。
  • 当一个加载操作发生时,它必须检查存储缓冲区中的条目,看有没有地址相匹配。
  • 如果有地址相匹配,则取出相应的数据条目作为加载操作的结果。
  • 只有加载操作会受存储操作结果的影响,即写/读相关(write/read dependency)。
  • 意为,存储地址必须在数据被存储之前被计算出来。
  • 同时,加载操作会将它的地址与所有未完成的存储操作的地址进行比较。
  • 最后,还需要考虑加载地址和存储地址相同时的情况。

后记

  • 又写一篇,明天还有一篇,累死了,期中季加油

alt text

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/948889.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025年密集母线槽品牌

摘要 密集母线槽行业在2025年迎来智能化、数字化转型浪潮,随着数据中心、高端建筑和工业基础设施的快速发展,市场对高效、安全、可靠的电力分配解决方案需求激增。本文基于行业调研和用户反馈,整理出排名前十的密集…

2025年口碑好的密集母线槽产品

摘要 密集母线槽行业在2025年迎来智能化、数字化转型浪潮,随着数据中心、智慧城市和绿色建筑需求的增长,市场对高可靠性、高效能母线槽产品的需求持续上升。本文基于行业数据和用户反馈,整理出口碑优秀的密集母线槽…

2025年密集母线槽品牌排行榜

文章摘要 随着城市化进程加速和智能电网发展,密集母线槽行业在2025年迎来技术创新高峰,产品趋向智能化、高效化。本文基于市场调研和用户口碑,整理出2025年密集母线槽品牌排名前十榜单,为行业采购和决策提供参考。…

10 28

P8097积累trick:在正序难的时候就倒序看 倒序则会变为让一个农场开始生产 删去两个活跃农场之间的路 添加一条边可以发现倒序的过程不会让奶牛从有关的变为无关的 故倒序地做记录每个的第一次变为有关的时间即可P8271积…

混合动力汽车MATLAB建模实现方案

一、系统架构设计 混合动力汽车(HEV)的MATLAB建模需包含以下核心模块:动力总成系统发动机模型(基于MAP数据或物理机理) 电机/发电机模型(PMSM/IM模型) 电池管理系统(SOC估算、热管理) 离合器与变速器模型(CV…

2025年口碑好的多功能综合杆公司排名前十

摘要 随着智慧城市建设的加速,多功能综合杆行业在2025年迎来快速发展,集成照明、监控、环境监测等多功能于一体,提升城市管理效率。本文基于行业数据和用户口碑,整理出排名前十的公司榜单,为采购决策提供参考。榜…

2025 年凹槽铝方通,吊顶铝方通,铝方通格栅厂家最新推荐,产能、专利、环保三维数据透视

引言 随着建筑装饰行业对铝方通细分品类需求的升级,凹槽铝方通、吊顶铝方通、铝方通格栅的产品性能与生产标准愈发受关注。为精准筛选优质厂家,本次推荐基于中国建筑装饰协会 2025 年度铝制装饰材料专项测评数据,采…

大模型应用开发--[笔记未完待续]

大模型应用开发 初识大模型认识AI 大模型应用部署大模型(ollama部署模型),掌握阿里云百炼平台使用 调用大模型,使用http方式调用大模型 大模型应用,与传统应用的区别 技术方案SpringAI基本使用 阻塞调用和流式调用…

2025年低压电缆品牌排行榜单

文章摘要 低压电缆行业在2025年持续快速发展,随着新能源、智能电网和工业自动化的推进,市场需求不断增长。行业竞争加剧,品牌实力、技术水平和产品质量成为关键因素。本文基于权威数据和市场调研,发布2025年低压电…

2025年口碑好的模压托盘品牌top5排名

摘要 模压托盘行业近年来随着物流和制造业的快速发展,呈现出高速增长趋势,预计2025年全球市场规模将达到XX亿美元(数据来源:行业报告2024)。模压托盘以其环保、耐用和高性价比的特点,成为替代传统木托盘和塑料托…

完整教程:Spring Boot 缓存技术

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025年模压托盘品牌前十强权威评测:江苏同芯木业引领行业变革

文章摘要 模压托盘行业在2025年迎来技术升级与市场重构,环保型、高强度模压托盘成为物流仓储领域新宠。本文基于产能规模、技术实力、市场口碑等维度,对行业内顶尖模压托盘品牌进行综合评测,为采购决策提供权威参考…

易路iBuilder打造AI Agent时代的智能薪酬管理

引言:AI 浪潮下的企业人力资源管理变革 在数字化浪潮与人工智能技术的双重驱动下,企业人力资源(HR)管理正经历一场深刻的“升维”革命。传统的 HR 职能正在从事务性处理转向战略性赋能,尤其是在与企业成本、员工体…

2025年模压托盘品牌深度分析与推荐排行榜

摘要 模压托盘作为现代物流和仓储行业的关键设备,近年来随着电商和制造业的快速发展,需求持续增长。行业数据显示,全球模压托盘市场年复合增长率预计达5.2%,中国作为主要生产国,占据全球份额的30%以上。本文基于权…

2025年模压托盘源头厂家综合实力前十排行榜

摘要 模压托盘行业作为物流包装领域的重要组成部分,2025年将迎来智能化、环保化发展新趋势。本文基于产品质量、生产能力、技术实力等维度,对国内模压托盘厂家进行综合评估,为采购商提供权威参考。文末附有详细联系…

MATLAB使用内点法解决凸优化问题的原理和实现

内点法原理概述 内点法是一种用于解决凸优化问题的强大算法,通过在可行域内部移动来寻找最优解。主要分为两类:障碍函数法:将约束转化为目标函数的惩罚项 原始-对偶内点法:同时优化原始问题和对偶问题内点法MATLAB…

2025 年钢结构旋转楼梯,小型旋转楼梯,户外旋转楼梯,不锈钢旋转楼梯厂家最新推荐,技术实力与市场口碑深度解析

引言 在建筑装饰与工程领域,钢结构、小型、户外及不锈钢旋转楼梯因空间适配性与耐用性备受青睐,但市场中产品精度差异达 20%、户外防腐性能良莠不齐等问题凸显。为精准筛选优质品牌,本次测评联合行业权威机构,依据…

S + V(主 + 谓)

🧩 一、S + V(主 + 谓)句型的核心结构 基本公式: S(主语) + V(谓语动词) 功能: 表示一个“主语”执行了某个“动作”或处于某种“状态”, 动词是不及物动词(intransitive verb),不需要宾语。 ✅ 基本例句…

2025年制冷设备厂家权威推荐榜单:冷库制冷设备/岗位送风设备/车间降温设备源头厂家精选

在产业升级与技术革新的双重驱动下,中国制冷设备市场正迎来高速增长期,其中工业冷却与冷链制冷领域表现尤为突出。 据权威机构预测,到2031年,全球涡旋及吸收式制冷机市场销售额将达到561.6亿元,2025-2031年期间年…

《ESP32-S3使用指南—IDF版 V1.6》第四十四章 USB虚拟串口(Slave)实验

第四十四章 USB虚拟串口(Slave)实验 1)实验平台:正点原子DNESP32S3开发板 2)章节摘自【正点原子】ESP32-S3使用指南—IDF版 V1.6 3)购买链接:https://detail.tmall.com/item.htm?&id=768499342659 4)全套实…