优化 C++/优化生命周期
高效应用程序的构建应遵循以下开发过程
- 设计。首先,算法和数据结构的设计要符合应用程序逻辑,并且具有合理的效率,但无需考虑优化。在设计广泛使用的数据结构时,其最佳实现并不明显(例如,争论数组或链表哪个更好),可以定义一个抽象结构,在优化阶段可以更改其实现。
- 实现。其次,编写实现设计算法的代码,遵循指南以避免低效操作并封装可能需要优化的操作。
- 功能测试。然后测试生成的软件,以提高其没有严重缺陷的可能性。
- 优化(又名调整)。在完成正确工作的应用程序的开发后,优化阶段开始,包括以下子阶段
- 性能测试。检测性能不足的命令。这些命令在处理典型数据时,需要的资源(CPU 时间、存储空间等)超过了可用资源。
- 分析(又名性能分析)。对于每个性能不足的命令,使用分析器确定哪些代码部分是该命令的所谓瓶颈。瓶颈是代码中花费大量时间或分配大量内存空间的部分。
- 算法优化。对于每个瓶颈,应用很大程度上独立于编程语言并且完全独立于平台的优化技术。这类技术可以在算法理论教科书中找到。这种优化涉及尝试减少执行的机器周期数量。特别地,它涉及减少对昂贵例程的调用次数,或将昂贵的操作转换为等效但成本更低的操作。例如,选择快速排序排序算法而不是选择排序算法。如果这使程序足够快,则优化阶段完成。
- 平台无关优化。对于每个瓶颈,应用依赖于编程语言及其标准库,但独立于软件和硬件平台的优化技术。例如,使用整数运算而不是浮点运算,或从标准库中选择更合适的容器类。如果这使程序足够快,则优化阶段完成。
- 软件平台依赖优化。对于每个瓶颈,应用依赖于编程语言和软件平台,但独立于硬件平台的优化技术。例如,利用编译器选项、pragma 编译器指令、语言扩展、非标准库、对操作系统的直接调用。如果这使程序足够快,则优化阶段完成。
- 硬件平台依赖优化。对于每个瓶颈,应用依赖于硬件平台的优化技术。这可能涉及使用特定于处理器系列的机器指令,或使用在某些处理器类型上特别有利的高级功能,这些功能通常可用。
这个开发过程遵循两个标准
- 收益递减原则。首先应用那些只需很少努力就能产生很大效果的优化,因为这可以最大程度地减少达到性能目标所需的时间。
- 可移植性递减原则。最好首先应用适用于多个平台的优化,因为它们在平台改变时仍然适用,并且更容易被其他程序员理解。
在软件必须使用多个编译器和多个操作系统,但只使用一种处理器架构的罕见情况下,应交换阶段 4.5 和 4.6。
这个阶段序列并非指单向序列,即一旦达到一个阶段,就不会再使用前一个阶段。实际上,每个阶段都可能成功或失败。如果一个阶段成功,则应用下一阶段,而如果一个阶段失败,则重复前一个阶段,这就像一种回溯算法。
此外,在每次优化尝试后,应执行部分性能测试,以检查尝试是否有用,如果有用,则检查是否需要更多优化。
最后,在完成优化后,必须重复功能测试和完整的性能测试,以确保软件的新优化版本在功能上仍然正确并且具有合适的性能。
本书仅涉及上述三个阶段
- 阶段 2,特别是在“编写高效代码”一章中,使用 C++ 语言。
- “通用优化技术”一章中,使用 C++ 的示例介绍了阶段 4.3 的一些通用技术。
- 阶段 4.4,特别是在“代码优化”一章中,使用 C++ 语言。
通过对象,是指分配的内存区域。特别是,与基本类型变量(如bool
、double
、unsigned long
或指针)关联的数据片段是一个对象,因为它与类的实例关联的数据结构。每个变量都关联一个对象,其大小由sizeof
C++ 运算符给出,但对象可能没有与之关联的变量,或与之关联的多个变量。例如,指针是一个对象,但它可以指向另一个对象;这个被指向的对象与任何变量都没有关联。另一方面,在以下代码中,变量a
和变量b
都与同一个对象关联
int a;
int& b = a;
数组、结构体和类实例是对象,如果它们不为空,则包含子对象。因此,它们将被称为聚合对象。
当前一个对象的销毁导致后一个对象的销毁时,我们说一个对象拥有另一个对象。例如,一个非空的vector
对象通常包含一个指向包含所有元素的缓冲区的指针;vector
的销毁会导致该缓冲区的销毁,因此我们说这个缓冲区被vector
对象拥有。
某些优化仅对短数据序列有用,而另一些对长数据序列有用。稍后将使用以下分类来对对象大小进行分类
- 微小:不超过 8 字节。它适合一个或两个 32 位寄存器,或一个 64 位寄存器。
- 小:超过 8 字节,但不超过 64 字节。它不适合处理器寄存器,但适合处理器数据缓存行,并且可以使用从起始地址偏移的非常紧凑的机器指令完全引用它。
- 中等:超过 64 字节,但不超过 4096 字节。它不适合处理器数据缓存行,也不能完全用紧凑的机器指令引用,但它适合处理器一级数据缓存,适合虚拟内存页面,并且适合大规模存储块集群。
- 大:超过 4096 字节。它不适合处理器一级数据缓存,不适合单个虚拟内存页面,也不适合单个大规模存储块集群。
例如,只有当double
数组包含一个元素时才被认为是微小的,如果它有 2 到 8 个元素,则被认为是小的,如果它有 9 到 512 个数字,则被认为是中等的,如果它有超过 512 个数字,则被认为是大的。
由于存在非常不同的硬件架构,给出的数字仅是一种指示。虽然,这些数字相当现实,可以作为开发软件以覆盖主要架构以相当高效的方式的严肃标准。