大多数答案都提供了替代解决方案,例如不同的编译器或外部库,这很可能需要大量的重写或集成工作。我将尝试坚持问题所问的,并专注于仅使用GCC可以做什么,通过激活编译器标志或对代码进行最小更改,如OP所要求的那样。这不是一个“必须这样做”的答案,而是我已经成功尝试过的一些GCC调整的集合,如果在您特定的上下文中相关,您可以尝试一下。
关于原问题的警告
在深入讨论之前,有几个关于该问题的警告,通常是给那些读到这个问题并说“OP正在优化O3,我应该使用与他相同的标志!”的人。
-march=native
启用特定于给定CPU架构的指令,并且这些指令在不同架构上可能不存在。如果在不同的CPU系统上运行程序,则程序可能根本无法工作,或者速度显着降低(因为它还启用了mtune=native
),因此如果您决定使用它,请注意此事项。更多信息here。
-Ofast
,如您所述,启用了一些不符合标准的优化,因此也应谨慎使用。更多信息here。
尝试的其他GCC标志
不同标志的详细信息可以在这里找到。
-Ofast
启用 -ffast-math
,进而启用 -fno-math-errno
,-funsafe-math-optimizations
,-ffinite-math-only
,-fno-rounding-math
,-fno-signaling-nans
和 -fcx-limited-range
。您可以通过有选择地添加一些额外的标志,如 -fno-signed-zeros
,-fno-trapping-math
等,更进一步地进行 浮点数计算优化。这些标志不包括在 -Ofast
中,但可以在计算中提供一些额外的性能提升,但您必须检查它们是否真正对您有益并且不会破坏任何计算。
- GCC 还具有大量其他未由任何 "-O" 选项启用的 其他优化标志。它们被列为“可能产生错误代码的实验性选项”,因此应谨慎使用,并通过测试正确性和基准测试来检查它们的效果。尽管如此,我经常使用
-frename-registers
,这个选项从未为我产生过不良结果,并且往往会给出明显的性能提升(即可以在基准测试中测量)。这是一种非常依赖于您的处理器类型的标志。 -funroll-loops
有时也会产生良好的结果(并且还暗示了 -frename-registers
),但它取决于您的实际代码。
PGO
GCC具有基于性能分析的优化(Profile-Guided Optimisations)功能。虽然GCC没有太多关于此功能的精确文档,但是让它运行起来非常简单。
- 首先使用
-fprofile-generate
编译您的程序。
- 让程序运行(执行时间会显著变慢,因为代码还会生成.gcda文件中的性能分析信息)。
- 使用
-fprofile-use
重新编译程序。如果您的应用程序是多线程的,请添加-fprofile-correction
标志。
使用GCC进行PGO可以产生惊人的结果,并且真正显著提高性能(我最近参与的一个项目中看到了15-20%的速度提升)。显然,问题在于拥有一些足够代表您的应用程序执行的数据,这并不总是可用或易于获取。
GCC的并行模式
GCC具有并行模式,该模式是在GCC 4.2编译器发布时首次推出的。
基本上,它为您提供了许多C++标准库算法的并行实现。要在全局范围内启用它们,您只需要向编译器添加-fopenmp
和-D_GLIBCXX_PARALLEL
标志。您也可以在需要时选择性地启用每个算法,但这将需要进行一些小的代码更改。
有关此并行模式的所有信息,请单击此处。
如果您经常在大型数据结构上使用这些算法,并且有许多硬件线程上下文可用,这些并行实现可以大大提高性能。到目前为止,我只使用了sort
的并行实现,但为了给出一个粗略的想法,我设法将排序时间从14秒降低到4秒,测试环境为:具有自定义比较器功能和8个内核的100万对象向量。
额外技巧
与前面的部分不同,这一部分需要对代码进行一些小的更改。它们也是GCC特定的(其中一些也适用于Clang),因此应使用编译时宏来保持代码在其他编译器上的可移植性。该部分包含一些更高级的技术,如果您没有一定的汇编水平理解,则不应使用。还要注意,处理器和编译器现在非常聪明,因此可能很难从这里描述的函数中获得任何显着的好处。
- GCC内置函数,可以在这里找到。像
__builtin_expect
这样的构造可以通过提供分支预测信息帮助编译器进行更好的优化。其他构造,例如__builtin_prefetch
将数据带入缓存以在访问之前帮助减少缓存未命中。
- 函数属性可以在这里找到。特别是,应该查看
hot
和cold
属性;前者将指示编译器函数是程序的热点,并且更积极地优化该函数,并将其置于文本部分的一个特殊子部分,以便更好地定位;后者将为大小优化函数,并将其放置在文本部分的另一个特殊子部分。
我希望这个答案能对一些开发者有所帮助,如果有任何修改或建议,我很乐意考虑。