1 概述
性能优化是软件开发过程中必不可少,但又很困难的工作。这里是我长期对C/C++开发的性能优化的经验总结。
2 原则
性能优化必须遵循必要的原则进行。
2.1 明确目标
优化前必须有个明确的目标。目标可以有近期的,中期的和远期的。
并且目标必须是可达到,可量化的具体的值。
2.2 性能测试先行
在任何优化前必须进行性能测试,得到的测试结果必须保存下来。这些数据有如下用处:
-
与之前的测试结果进行比较。如果没有任何数据,仅仅靠自我推断绝对是不可靠的。
-
对外公布。这是让同事/领导/客户相信性能的最直接的信息。
-
学习。多次优化得到的测试数据是学习和选择高效的优化方式的最好的参照物。
-
发现热点。如果没有这些测试数据,我们无法明确的指导最大最长的耗时发生在哪里。这是优化的前提。
2.3 记录
性能优化的任何方法和尝试,以及得到的测试数据都应该记录下来。
2.4 数据量
性能测试在很大程度上实际就是压力负载测试。对于这类的性能不需要尽可能的加大数据压力,测试对应的性能。
另一个必须要进行多次反复的相同测试,并执行相关的数理统计计算。有些产品和流程只有运行几百万次才能真正说明性能。这个是非常重要的。
2.5 重构
性能优化不是改变功能。
所以这些都应该基于重构的原则进行,这就意味这任何性能优化不能对上层客户代码造成影响。如果这是无法避免的,必须明确说明。
2.6 2/8原则
发现了热点后,我们必须将从最大的耗时着手。2/8原则有两层含义:
-
最为耗时最影响性能的热点仅占所有代码或者流程的非常小的比例;
-
仅对很小的一部分的代码执行优化,性能即可得到极大提升,甚至达到预订目标。
所以我们不能盲目的优化,更不能以自己的推断或者所为的“理论上是这样的”想法执行优化,必须实事求是。
2.7 区分Debug/Release
很多时候我们执行优化时是在debug模式下执行的,但是最终我们形成release模式下的性能数据。如果其中加入了为了记录性能的debug代码,那么在release模式下必须关闭。
2.8 自动化
很多场景下,多是开发人员的性能测试指导性能优化。如果我们将整个流程自动化,那么可以极大的提升优化效率,更快发布。另外自动化最大的好处是可以将该过程嵌入测试人员测试过程,或者自动化测试/集成/交付(CI/CD)。
但是这是非常困难的,所以需要视情况而定。
2.9 真实可信
我们的性能优化以及得到的结果必须是真实可信的,不能有半点作假和推测。
3 性能测试
性能测试是我们优化的第一步。良好的测试方法是好的开始,而不良甚至错误的测试方法会得出错误的结果。
4 优化方法
优化的方法有很多,但是这不意味着所有的方法在每个场景下都是可用的。
可以分为宏观和微观两个层次:宏观主要是基础设施以及工程设计的优化,这个层面是不会对实现做很大变动的;而微观则是对具体的编码调整,内部调整可能会非常大。
4.1 宏观层面
4.1.1 升级基础设施
-
硬件升级:这是最直接,有时甚至是最高效的优化方法。
-
操作系统升级:新的操作系统版本拥有更好的性能表现,特别是在内核操作以及内存操作方面。
-
编程语言:使用其他更好的编程语言,或者更加符合性能表现的编程语言也是常用的性能优化方法。
-
编译器:很多语言拥有不同的编译器,不同的编译器得到可执行程序性能有时差别很到。而同一个编译器的不同版本也有不同的性能表现。
4.1.2 架构设计
4.1.3 结构和流程设计
4.2 微观层面
4.2.1 变量
-
临时变量
-
成员变量
-
静态变量
-
函数参数和返回值
4.2.2 内存管理
-
动态内存分配
-
内存池
-
内存拷贝
-
缓存
4.2.3 多线程和并发
-
阻塞和同步
-
无锁队列
-
内存映射
-
共享内存
4.2.4 面向对象
-
使用模板和范型替换继承
-
错误码替换异常
4.2.5 数据结构和算法
-
字符串
-
浮点
-
递归
-
搜索
-
排序
4.2.6 IO和系统调用
-
网络通信
-
文件读写
-
用户态和内核态的切换
4.2.7 编译
-
编译期计算
-
编译选项
-
内联-inline
4.2.8 语言/库版本
-
新的语言版本性能更好;
-
新的语言版本提供了更好性能工具和特性的选择;
-
新的内存布局;
-
新的内存管理方式。
5 耐心和信心
在很多时候优化可能得出完全相反的结果,即性能反而更糟糕。这是非常正常的,优化路线可能很曲折漫长。有些热点需要长时间尝试不同的方法才能有效优化。
所以耐心和信心是优化过程中必备的良好心理素质。