深入 C 语言和程序运行原理
于航
PayPal 技术专家
21121 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 49 讲
深入 C 语言和程序运行原理
15
15
1.0x
00:00/00:00
登录|注册

19|极致优化(下):如何实现高性能的 C 程序?

用循环代替递归提升性能
利用编译器优化
防止条件分支指令的 CPU 周期浪费
利用 CPU 指令级并行
作用和实现原理
函数体小且递归调用次数多的函数
减少栈帧创建与销毁过程
减少 call 指令调用次数
将递归调用转换为循环结构
让编译器自动进行代码优化
-O0, -O1, -O2, -O3, -Os, -Ofast
分支预测和投机执行的影响
条件分支指令 vs 条件传送指令
使用三元运算符代替 if 语句
避免分支预测错误的性能损耗
编译器可能已隐式优化
可能增加代码量和降低可读性
使用多个独立变量存储累积值
增加每次迭代计算的元素个数
优化指令级并行和流水线调度
减少循环迭代次数
技巧概览
达夫设备 (Duff's Device)
适用情况
效果
原理
作用
GCC 优化选项
比较
实现
优势
注意事项
实现
原理
总结
思考题
尾递归调用优化 (Tail-Call Optimization)
编译优化等级
条件传送指令 (Conditional Move)
循环展开 (Loop Unrolling)
高性能 C 程序优化技巧

该思维导图由 AI 生成,仅供参考

你好,我是于航。
在上一讲中,我介绍了几个用于编写高性能 C 代码的实用技巧。今天,我们继续聊这个话题,来讨论其他几种常见的 C 代码和程序优化技巧,它们分别是利用循环展开、使用条件传送指令、尾递归调用优化,以及为编译器指定更高的编译优化等级。

技巧五:循环展开(Loop Unrolling)

为了让你更好地理解“循环展开”这个优化技巧背后的原理,我们先从宏观角度看看 CPU 是如何运作的。
早期的 CPU 在执行指令时,是以串行的方式进行的,也就是说,一个指令的执行开始,需要等待前一个指令的执行完全结束。这种方式在实现上很简单,但存在的问题也十分明显:由于指令的执行是一个涉及多个功能单元的复杂过程,而在某一时刻,CPU 也只能够对指令进行针对当前所在阶段的特定处理。
那么,将 CPU 处理指令的流程划分为不同阶段,并让它对多条指令同时进行多种不同处理,这样是否可以进一步提升 CPU 的吞吐量呢?事实正是如此。
现代 CPU 为了进一步提升指令的执行效率,通常会将单一的机器指令再进行拆分,以达到指令级并行的目的。比如,对于一个基本的五级 RISC 流水线来说,CPU 会将指令的执行细分为指令提取(IF)、指令译码(ID)、指令执行(EX)、内存访问(MEM),以及寄存器写回(WB)共五个步骤。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文介绍了实现高性能 C 程序的一些实用技巧,包括循环展开、条件传送指令、尾递归调用优化和编译优化等级。循环展开通过增加循环结构每次迭代时计算的元素个数,减少循环次数,优化 CPU 的指令集并行与流水线调度,提升程序执行效率。条件传送指令能有效避免使用条件分支指令带来的 CPU 周期浪费。使用更高的编译优化等级能让编译器自动进行更多程序执行细节上的优化。尾递归调用优化通过将函数的递归调用过程优化为循环结构,减少了程序执行时对 `call` 指令的调用次数,提升了程序的执行性能。文章还提到了“达夫设备(Duff's Device)”的作用和实现原理。这些技巧能帮助读者优化 C 程序,提升程序性能。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《深入 C 语言和程序运行原理》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(5)

  • 最新
  • 精选
  • 纳兰容若
    老师您好 我看达夫设备的功能是针对字节的拷贝,达夫设备的效率和标准库中的memcpy哪个效率更高一些呢 多谢老师指教

    作者回复: 在大多数情况下,请优先使用标准库中提供的方法,这里也就是 memcpy。达夫设备只是提出了使用循环展开进行优化的一种方式,而这种方式也仅适用于当时它出现的那个特殊场景中,也就是“将 16 位的无符号整数从一个数组中复制到 MMIO 寄存器”。而标准库中的方法在考虑常见性能优化策略的同时,还可能还会使用一些独立于架构的不同优化方式。除此之外,从兼容性、易用性、可读性上也更具优势。

    2022-04-20
    2
  • 八怪
    老师 __builtin_expect 能有效减少分支预测带来的性能损失吗?

    作者回复: 如果合理使用的话(场景合适),理论上是可以的,Linux 内核里也有在用这些扩展函数。但实际使用时还是建议配合 profiler 检验一下优化效果。

    2022-03-22
  • fee1in
    而当五个阶段全部执行完毕后,CPU 会更新指令指针(PC),将其指向下一个需要执行的指令 应该是在IF结束后,更新PC把 不然跳转指令就会出问题

    作者回复: 这里针对五级 RISC 流水线来说,实际上 PC 的值一般是在 IF 阶段就可以计算(预测)好的,然后在 WB 之后才会实际更新到 PC 寄存器中。

    2022-01-28
  • 术子米德
    🤔☕️🤔☕️🤔 优化,仅知道方法,非常容易出现伪优化 优化,确定度量方法,才能控制住优化真正效果 度量一段实现代码执行所需的耗时,即总指令数,以及每个时钟周期执行的指令数,即IPC=Instructions-Per-Cycle,这两个指标抓住,大部分情况下打开编译器优化,就达到技巧所谓的优化效果 如果要有更多的优化,都是要选择新的算法或者结合业务和运行环境的各种适配性调优,语言层面的技巧开不出更多的花🌹
    2022-01-26
    3
    11
  • liu_liu
    看了达夫设备的代码,原来 switch case 语句还可以这样用,涨见识了。
    2022-01-26
    1
收起评论
显示
设置
留言
5
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部