超越-O3/-Ofast的G++优化

Posted

技术标签:

【中文标题】超越-O3/-Ofast的G++优化【英文标题】:G++ optimization beyond -O3/-Ofast 【发布时间】:2013-01-07 16:23:51 【问题描述】:

问题

我们有一个用于模拟任务的中型程序,需要对其进行优化。我们已经尽最大努力将源代码优化到我们编程技能的极限,包括使用Gprof 和Valgrind 进行分析。

最终完成后,我们希望在多个系统上运行该程序可能几个月。因此,我们非常有兴趣将优化推向极限。

所有系统都将在相对较新的硬件(Intel i5 或 i7)上运行 Debian/Linux。

问题

使用最新版本的 g++ 有哪些可能的优化选项,超越 -O3/-Ofast?

我们还对代价高昂的小优化感兴趣,从长远来看,这会带来回报。

我们现在使用什么

现在我们使用以下 g++ 优化选项:

-Ofast:最高“标准”优化级别。包含的-ffast-math 没有在我们的计算中造成任何问题,因此我们决定继续使用它,尽管不符合标准。 -march=native:启用所有 CPU 特定指令的使用。 -flto 允许跨不同编译单元的链接时间优化。

【问题讨论】:

您是否尝试过配置文件驱动的优化 - 尽管这将取决于配置文件的“代表性”数据。除此之外,我认为它正在识别热点并查看处理器生成的代码,看看您是否可以更好地组织数据/代码或提出不同的算法。 请注意,在一天后启动您的程序并获得 1% 的性能提升,因为当天优化只会在运行 100 天后收支平衡。换句话说,提前几天开始运行您的程序可能会超过小的优化。 @sth: 这简直是天方夜谭。不过我希望能找到一些提示/技巧,也可以在以后的项目中重复使用,这样我就不用花一天时间从优化中获胜了…… @OliCharlesworth:你可能是对的,所以我把那个明确的例子拿出来了。但是,我希望可能有一些标志/技巧可以产生甚至超过小加速的效果。 我没有检查自己,但-ffast-math 并不总是让代码更快according to this 博客。 【参考方案1】:

大多数答案都建议了替代解决方案,例如不同的编译器或外部库,这很可能会带来大量的重写或集成工作。我将尝试坚持问题的要求,并关注单独使用 GCC 可以做什么,通过激活编译器标志或对代码进行最小的更改,按照 OP 的要求。这不是“您必须这样做”的答案,而是更多对我来说效果很好的 GCC 调整集合,如果它们与您的特定上下文相关,您可以尝试一下。


关于原始问题的警告

在进入细节之前,关于这个问题的一些警告,通常是为了那些会过来的人,阅读这个问题并说“OP 正在优化 O3,我应该使用与他相同的标志!”。

-march=native 允许使用特定于给定 CPU 架构的指令,而这些指令不一定在不同的架构上可用。如果在具有不同 CPU 的系统上运行该程序可能根本无法运行,或者运行速度明显变慢(因为这也启用了mtune=native),因此如果您决定使用它,请注意这一点。更多信息here。 正如您所说,-Ofast 启用了一些不符合标准的优化,因此也应谨慎使用。更多信息here。

可供试用的其他 GCC 标志

here 列出了不同标志的详细信息。

-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 优化 功能。没有很多关于它的精确 GCC 文档,但是让它运行起来非常简单。

首先使用-fprofile-generate 编译您的程序。 让程序运行(执行时间会明显变慢,因为代码还会将配置文件信息生成到 .gcda 文件中)。 用-fprofile-use重新编译程序。如果您的应用程序是多线程的,还请添加 -fprofile-correction 标志。

带有 GCC 的 PGO 可以提供惊人的结果并真正显着提高性能(我看到我最近从事的一个项目的速度提高了 15-20%)。显然,这里的问题是要有一些足够代表应用程序执行的数据,这些数据并不总是可用或容易获得。

GCC 的并行模式

GCC 具有并行模式,它是在 GCC 4.2 编译器出现时首次发布的。

基本上,它为您提供C++ 标准库中许多算法的并行实现。要全局启用它们,您只需将-fopenmp-D_GLIBCXX_PARALLEL 标志添加到编译器。您还可以在需要时选择性地启用每种算法,但这需要对代码进行一些小的更改。

关于这种并行模式的所有信息都可以在here找到。

如果您经常在大型数据结构上使用这些算法,并且有许多可用的硬件线程上下文,那么这些并行实现可以极大地提升性能。到目前为止,我只使用了sort 的并行实现,但为了给出一个粗略的想法,我设法在我的一个应用程序中将排序时间从 14 秒减少到 4 秒(测试环境:包含 1 亿个对象的向量自定义比较器功能和8核机器)。

额外技巧

与前面几点不同,这部分确实需要对代码进行一些小的更改。它们也是 GCC 特定的(其中一些也适用于 Clang),因此应该使用编译时宏来保持代码在其他编译器上的可移植性。本节包含一些更高级的技术,如果您对正在发生的事情没有一定的汇编级理解,则不应使用。另请注意,当今的处理器和编译器非常智能,因此从此处描述的功能中获得任何明显的好处可能会很棘手。

GCC 内建函数,在here 中列出。诸如__builtin_expect 之类的构造可以通过向编译器提供分支预测信息来帮助编译器进行更好的优化。其他构造(例如 __builtin_prefetch)在数据被访问之前将数据带入缓存,有助于减少缓存未命中。 函数属性,列出here。特别是,您应该查看hotcold 属性;前者将向编译器指示该函数是程序的热点,并更积极地优化该函数并将其放置在文本部分的特殊子部分中,以获得更好的局部性;后者将优化函数的大小并将其放置在文本部分的另一个特殊小节中。

我希望这个答案对一些开发者有用,我很乐意考虑任何修改或建议。

【讨论】:

谢谢,这个答案几乎描述了我们最终做了什么,尤其是 PGO 被证明非常有用。此外,我还喜欢 @zaufi 建议的 ACOVEA 项目,尽管它不适合这个项目。 哇,不知道 PGO 选项!我的情况大约提高了 30%。 “这些不包含在 -Ofast 中”我很确定这是错误的。如果您查看 -ffast-math 的 GCC 文档(由 -Ofast 打开),它还会打开 -funsafe-math-optimizations,从而打开 -fassociative-math。 (除其他外)文档中有一个短语“任何 -O 选项都没有打开此选项”,我认为这是一个文档错误,因为 -Ofast 确实打开了它们。另外,PGO 开启了 -funroll-loops,它开启了 -frename-registers。 @uLoop:GCC 文档确实并不总是很清楚。我已经使用编译器的 -Q 标志检查了这些标志,并相应地调整了答案。 @Pyves 我还发现了另一种与您的方法相辅相成的方法:使用 GCC 和 Perf 进行反馈定向优化:blog.wnohang.net/index.php/2015/04/29/… 但是,由于遇到了问题,这篇文章并不符合要求-日期,一些命令已被弃用,gcov_create 在读取 perf 的 perf.data 文件时遇到问题。也许您可以调查并提供一些指导。【参考方案2】:

相对较新的硬件(Intel i5 或 i7)

为什么不投资购买Intel compiler 和高性能库的副本?它在优化方面可以大大优于 GCC,通常从 10% 到 30% 甚至更多,对于繁重的数字运算程序甚至更是如此。英特尔还为高性能数字运算(并行)应用程序提供了许多扩展和库,如果您可以负担得起将其集成到您的代码中的话。如果最终为您节省数月的运行时间,它可能会带来巨大的回报。

我们已经尽最大努力优化源代码以达到我们编程技能的极限

根据我的经验,与宏观优化(简化代码结构)相比,您通常在分析器的帮助下进行的那种微观和纳米优化的时间投资回报往往很差,并且,最重要且经常被忽视的内存访问优化(例如,引用的局部性、按顺序遍历、最小化间接性、使用缓存未命中等)。后者通常涉及设计内存结构以更好地反映内存的使用方式(遍历)。有时它可以像切换容器类型并从中获得巨大的性能提升一样简单。通常,使用分析器,您会迷失在逐条指令优化的细节中,并且内存布局问题不会出现,并且通常会在忘记查看大图时被忽略。这是一种更好的投入时间的方式,而且回报可能是巨大的(例如,许多 O(logN) 算法最终会因为糟糕的内存布局(例如,使用链表)而执行几乎与 O(N) 一样慢与连续存储策略相比,链接树是造成巨大性能问题的典型罪魁祸首))。

【讨论】:

我们(尚未)使用英特尔编译器的原因是它不支持我们正在使用的某些 C++11 功能。如果这种情况很快发生变化,我们也会尝试 ICC。我基本上同意你的第二部分。但除了让更多人查看代码之外,我看不出我们如何进一步改进它。因此我的问题是我们是否可以让编译器做更多的事情。 @Haatschii 是的,很抱歉我不能直接回答你的问题(即如何充分利用 GCC),因为我认为你不能。我只是认为值得将这几点(使用 ICC 和进行内存优化)作为更好的途径来实际实现您的目标。 我非常怀疑“通常从 10% 到 30% 甚至更多”的说法。至少,这些利润远远超出了我在自己的工作中衡量的范围。我很想看到一组已发布的基准测试证明,只要使用等效的编译器标志并发布所使用的标志,如果只是为了看看我是否错过了英特尔编译器的优化机会。【参考方案3】:

嗯,那么您可以尝试的最后一件事:ACOVEA project: Analysis of Compiler Optimizations via an Evolutionary Algorithm -- 从描述中可以明显看出,它尝试了一种遗传算法来为您的项目选择最佳编译器选项(进行编译maaany 时间并检查时间,给算法一个反馈:) - 但结果可能令人印象深刻! :)

【讨论】:

【参考方案4】:

如果你负担得起,试试VTune。它提供了比简单采样更多的信息(据我所知,由 gprof 提供)。你可以试试Code Analyst。后者是一款不错的免费软件,但它可能无法(或根本无法)与 Intel CPU 一起正常工作。

配备此类工具后,您可以检查各种指标,例如缓存利用率(以及基本上是内存布局),如果充分利用这些指标,可以极大地提高效率。

当您确定您的算法和结构是最优的时,您绝对应该在 i5 和 i7 上使用多核。换句话说,尝试不同的并行编程算法/模式,看看是否可以加快速度。

当您拥有真正的并行数据(您可以在其上执行类似/相同操作的类似数组的结构)时,您应该尝试一下 OpenCL 和 SIMD instructions(更易于设置)。

【讨论】:

【参考方案5】:

关于当前选择的答案的一些说明(我还没有足够的声望点来发表评论):

答案是:

-fassociative-math-freciprocal-math-fno-signed-zeros-fno-trapping-math。这些不包含在-Ofast 中,并且可以在计算上提供一些额外的性能提升

也许在发布答案时确实如此,但GCC documentation 表示所有这些都由-funsafe-math-optimizations 启用,-ffast-math 启用,-Ofast 启用。这可以通过命令gcc -c -Q -Ofast --help=optimizer 进行检查,该命令显示-Ofast 启用了哪些优化,并确认所有这些都已启用。

答案还说:

任何“-O”选项都未启用的其他优化标志...-frename-registers

同样,上面的命令显示,至少在我的 GCC 5.4.0 中,-frename-registers 默认使用-Ofast 启用。

【讨论】:

【参考方案6】:

没有更多细节很难回答:

什么类型的数字运算? 您使用什么库? 并行化程度如何?

你能写下你的代码中花费时间最长的部分吗? (通常是一个紧密的循环)

如果您受 CPU 限制,答案将与您受 IO 限制不同。

再次,请提供更多详细信息。

【讨论】:

【参考方案7】:

我建议查看构成繁重工作的操作类型,并寻找优化的库。对于常见问题(主要是数学),有很多快速、汇编优化、SIMD 矢量化库。重新发明***通常很诱人,但如果现有的解决方案可以满足您的需求,通常不值得付出努力。由于您没有说明它是哪种模拟,我只能提供一些示例。

http://www.yeppp.info/

http://eigen.tuxfamily.org/index.php?title=Main_Page

https://github.com/xianyi/OpenBLAS

【讨论】:

【参考方案8】:

使用 gcc intel 转向 / 实现 -fno-gcse(在 gfortran 上运行良好)和 -fno-guess-branch-prbability (gfortran 中的默认值)

【讨论】:

以上是关于超越-O3/-Ofast的G++优化的主要内容,如果未能解决你的问题,请参考以下文章

Oracle性能优化之物化视图管理_超越OCP精通Oracle视频教程培训39

Lion : 超越 AdamW 的优化算法

Oracle性能优化之统计信息管理_超越OCP精通Oracle视频教程培训32

Oracle性能优化之性能诊断工具_超越OCP精通Oracle视频教程培训33

Oracle性能优化之性能跟踪工具_超越OCP精通Oracle视频教程培训34

Oracle性能优化之资源管理_超越OCP精通Oracle视频教程培训36