Clang vs GCC - 产生更快的二进制文件? [关闭]
Posted
技术标签:
【中文标题】Clang vs GCC - 产生更快的二进制文件? [关闭]【英文标题】:Clang vs GCC - which produces faster binaries? [closed] 【发布时间】:2011-03-12 08:56:59 【问题描述】:我目前正在使用 GCC,但我最近发现了 Clang,我正在考虑切换。但是有一个决定性因素 - 它生成的二进制文件的质量(速度、内存占用、可靠性) - 如果gcc -O3
可以生成运行速度快 1% 的二进制文件,或者 Clang 二进制文件占用更多内存或由于编译器错误而失败,这是一个交易破坏者。
Clang 拥有比 GCC 更快的编译速度和更低的编译时内存占用,但我对生成的已编译软件的基准测试/比较非常感兴趣 - 您能否指出一些预先存在的资源或您自己的基准测试?
【问题讨论】:
这个问题正在meta讨论。 【参考方案1】:确定这一点的唯一方法是尝试一下。 FWIW,我看到使用 Apple 的 LLVM GCC 4.2 与常规 GCC 4.2 相比有一些非常好的改进(对于具有大量 SSE 的 x86-64 代码),但 YMMV 用于不同的代码库。
假设您正在使用 x86/x86-64 并且您确实关心最后几个百分比,那么您也应该尝试英特尔的 ICC,因为这通常可以击败 GCC - 您可以获得 30 天来自 intel.com 的评估许可证并尝试一下。
【讨论】:
【参考方案2】:Phoronix did some benchmarks 关于这个,但它是关于几个月前 Clang/LLVM 的快照版本。结果是事情或多或少是一种推动。在所有情况下,GCC 和 Clang 都不是绝对更好。
由于您将使用最新的 Clang,因此它可能不太相关。话又说回来,显然,GCC 4.6 将有一些 major optimizations 用于 Core 2 和 Core i7。
我认为 Clang 更快的编译速度对于原始开发人员来说会更好,然后当您将代码推向世界时,Linux 发行版、BSD 等最终用户将使用 GCC 来获得更快的二进制文件。
【讨论】:
就在今天,我对 Clang 编译速度进行了一些基准测试,对于纯 C 语言来说非常令人失望。用 270 KLOC clang 编译 35 个 C 文件仅快 25%。当我看到 tinycc 在 linux 上的速度有多快时,这对于新编写的编译器来说是一个糟糕的结果。使用优化 -O2/-O3 会变得更好,但由于它们用于发布构建,因此在这种情况下编译器性能并不重要。 @mcandre 也许 Nietzche-jou 是用 Clang 编译的,而你是用 GCC 编译的。 未来的读者应该检查 Phoronix 的新文章。例如phoronix.com/… 用于 AMD Zen CPU 上的 AOCC 与 GCC 与 clang,或phoronix.com/… 用于 Intel i9-11900K(Rocket Lake,Ice Lake 的 14nm 反向移植)上的 GCC11 与 clang12【参考方案3】:Clang 编译代码更快的事实可能不如生成的二进制文件的速度重要。不过,这里是series of benchmarks。
【讨论】:
确实如此。在开发过程中,编译时间(以及编译导致的资源消耗)比二进制性能更成为瓶颈。毕竟,我们在这个阶段是在Debug模式下编译的。只有到了测试和发布阶段,您才切换到发布模式并尝试尽可能快地获取二进制文件。 @Matthieu M:我发誓那个回答说“可能……”,好像他提出了一个潜在的问题。我想也许值得一提,因为它与 OP 有关。 同意,虽然这里的所有优点。我宁愿投入第二个或第三个 RAID 0 驱动器、SSD 或更多更快的 RAM 并获得最佳的 .exe 性能 - 只要这些措施可以让您达到平价或接近。使用多个编译器进行开发有时也很有帮助。它可以让您了解不可移植的功能,并捕获否则无法检测到的错误,或者导致浪费数天时间尝试调试更好的编译器会警告/出错的代码。 我今天尝试比较我编写的一些性能严格的整数代码,并且使用 -O2 和 -O3 的 GCC 运行得更快(22S clang-llvm 25S)。考虑使用编译器开关(gcc 或 clang)涵盖大多数非标准功能和静态警告。在您自己的大型项目中,而不是批量编译其他 ppl 的代码,如果编译时间支配链接时间,那么您在构建系统中做错了。如果您经常进行清理,那么有像 ccache.samba.org 这样的工具会有所帮助。更改编译器的另一个问题是,在测试/验证上的所有时间投资都被丢弃了。 code.google.com/p/distcc 是另一个可以加快批量编译时间的项目,如果整个库由于数据结构更改或出于验证/验证目的而需要重新编译【参考方案4】:以下是我对 GCC 4.7.2 的一些最新发现,尽管范围很窄 和 C++ 的 Clang 3.2。
更新:GCC 4.8.1 v clang 3.3 比较附在下面。
更新:附加 GCC 4.8.2 v clang 3.4 比较。
我维护了一个 OSS 工具,它是为 Linux 构建的,带有 GCC 和 Clang, 并使用 Microsoft 的 Windows 编译器。 coan 工具是一个预处理器 和 C/C++ 源文件和代码行的分析器:它的 递归下降解析和文件处理的计算配置文件专业。 开发分支(与这些结果相关) 目前包括大约 90 个文件中的大约 11K LOC。它是编码的, 现在,在 C++ 中,它富含多态性和模板,但仍然 由于它在 C 中的不那么遥远的过去而陷入了许多补丁。 移动语义没有被明确利用。它是单线程的。一世 没有认真地优化它,而“架构” 主要是待办事项。
我只使用 3.2 之前的 Clang 作为实验性编译器 因为,尽管它具有卓越的编译速度和诊断能力,但它的 C++11 标准支持落后于当代 GCC 版本 coan 所行使的尊重。在 3.2 中,这个差距已经缩小。
我的 Linux 测试工具大致适用于当前的 coan 开发过程 70K 源文件混合单一文件解析器测试用例、压力 使用 1000 个文件的测试和使用
除了报告测试结果外,安全带还会累积和
显示在 coan 中消耗的文件总数和消耗的运行时间(它只是将每个 coan 命令行传递给 Linux time
命令并捕获并添加报告的数字)。由于任何数量的可测量时间为 0 的测试加起来都为 0,但这些测试的贡献可以忽略不计,这一事实让时间感到受宠若惊。计时统计信息显示在make check
的末尾,如下所示:
coan_test_timer: info: coan processed 70844 input_files.
coan_test_timer: info: run time in coan: 16.4 secs.
coan_test_timer: info: Average processing time per input file: 0.000231 secs.
我比较了 GCC 4.7.2 和 Clang 3.2,除了编译器之外,所有东西都是平等的。从 Clang 3.2 开始, 我不再需要任何预处理器区分代码 GCC 将编译的文件和 Clang 替代方案。我建到 在每种情况下都使用相同的 C++ 库(GCC)并运行所有比较 连续在同一个终端会话中。
我的发布版本的默认优化级别是 -O2。我也 在 -O3 处成功测试了构建。我测试了每个配置 3 背靠背时间并平均3个结果,具有以下 结果。数据单元中的数字是平均数 coan 可执行文件处理每一个所消耗的微秒 约 70K 输入文件(读取、解析和写入输出和诊断)。
| -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.7.2 | 231 | 237 |0.97 |
----------|-----|-----|-----|
Clang-3.2 | 234 | 186 |1.25 |
----------|-----|-----|------
GCC/Clang |0.99 | 1.27|
任何特定的应用程序都很可能具有可发挥作用的特征 对编译器的优点或缺点不公平。严格的基准测试 采用不同的应用程序。考虑到这一点,值得注意的是 这些数据的特点是:
-
-O3 优化对 GCC 略有不利
-O3 优化对 Clang 非常有益
在 -O2 优化时,GCC 比 Clang 快了一点点
在 -O3 优化时,Clang 明显比 GCC 快。
偶然出现了两个编译器的更有趣的比较
在这些发现之后不久。 Coan 大量使用智能指针和
其中之一是在文件处理中大量使用。这个特别
为了
编译器微分,如果是 std::unique_ptr<X>
配置的编译器对其用法有足够成熟的支持
那个,否则是std::shared_ptr<X>
。对std::unique_ptr
的偏见是
愚蠢的,因为这些指针实际上是被转移的,
但std::unique_ptr
看起来更适合替换
std::auto_ptr
在 C++11 变体对我来说很新颖的时候。
在测试 Clang 3.2 的持续需求的构建过程中
为了这个和类似的区别,我无意中建立了
std::shared_ptr<X>
当我打算构建 std::unique_ptr<X>
时,
并惊讶地发现生成的可执行文件,默认为 -O2
优化,是我见过最快的,有时达到184
毫秒。每个输入文件。通过对源代码的这一更改,
相应的结果是这些;
| -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.7.2 | 234 | 234 |1.00 |
----------|-----|-----|-----|
Clang-3.2 | 188 | 187 |1.00 |
----------|-----|-----|------
GCC/Clang |1.24 |1.25 |
这里的注意点是:
-
现在这两种编译器都无法从 -O3 优化中受益。
Clang 在每个优化级别都击败了 GCC。
GCC 的性能仅受智能指针类型的轻微影响
改变。
Clang 的 -O2 性能受智能指针类型的重要影响
改变。
在智能指针类型改变之前和之后,Clang 能够构建一个
在 -O3 优化下可以执行更快的 coan,并且它可以
在 -O2 和 -O3 处构建同样更快的可执行文件时
指针类型是最好的 - std::shared_ptr<X>
- 适合这项工作。
我无法评论的一个明显问题是为什么 Clang 应该能够在我的应用程序中找到 25% -O2 加速 大量使用的智能指针类型从唯一更改为共享, 而 GCC 对同样的变化无动于衷。我也不知道我是否应该 欢呼或嘘 Clang 的 -O2 优化所包含的发现 对我的智能指针选择的智慧如此敏感。
更新:GCC 4.8.1 v clang 3.3
现在对应的结果是:
| -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.8.1 | 442 | 443 |1.00 |
----------|-----|-----|-----|
Clang-3.3 | 374 | 370 |1.01 |
----------|-----|-----|------
GCC/Clang |1.18 |1.20 |
现在所有四个可执行文件的平均处理时间都比以前长得多 1 个文件不反映了最新编译器的性能。这是由于 事实上,测试应用程序的后期开发分支已经承担了很多 同时解析复杂性并以速度为代价。只有比率是 意义重大。
现在的注意点并不新颖:
GCC 对 -O3 优化无动于衷 clang 从 -O3 优化中获益非常少 clang 在每个优化级别都以同样重要的优势击败 GCC。将这些结果与 GCC 4.7.2 和 clang 3.2 的结果进行比较,可以看出: 在每个优化级别,GCC 已将 clang 的领先优势夺回了大约四分之一。但 由于测试应用程序在此期间已经大量开发,因此无法 自信地将这归因于 GCC 代码生成的追赶。 (这一次,我注意到了从中获取时间的应用程序快照 并且可以再次使用。)
更新:GCC 4.8.2 v clang 3.4
我完成了 GCC 4.8.1 v Clang 3.3 的更新,说我会 坚持使用相同的 coan 快照以进行进一步更新。但我决定 而是在该快照(rev. 301)上测试和最新的发展 快照我通过了它的测试套件(rev. 619)。这给出了结果 一点经度,我还有另一个动机:
我原来的帖子指出我没有投入任何精力来优化 coan 速度。截至 rev 时,情况仍然如此。 301.然而,在我建好之后 将计时装置放入 coan 测试工具中,每次我运行测试套件时 最新变化对性能的影响让我眼前一亮。我看到了 它通常大得惊人,而且趋势比负面趋势更陡峭 我觉得功能上的进步是值得的。
按转速。 308 测试套件中每个输入文件的平均处理时间 自从第一次在这里发帖以来,翻了一番还多。那时我做了一个 彻底改变我不关心性能的 10 年政策。在密集的 一连串的修改高达 619 性能始终是一个考虑因素和一个 他们中的许多人纯粹是从根本上重写关键的承载者 更快的行(尽管没有使用任何非标准的编译器功能来做到这一点)。看看每个编译器对此的反应会很有趣 掉头,
这是最新的两个编译器版本 rev.301 的现在熟悉的时序矩阵:
coan - rev.301 结果
| -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.8.2 | 428 | 428 |1.00 |
----------|-----|-----|-----|
Clang-3.4 | 390 | 365 |1.07 |
----------|-----|-----|------
GCC/Clang | 1.1 | 1.17|
这里的故事与 GCC-4.8.1 和 Clang-3.3 相比仅略有不同。海湾合作委员会的表现
好一点。 Clang的情况要差一些。噪音可以很好地解释这一点。
Clang 仍然领先 -O2
和 -O3
边距,这在大多数情况下都无关紧要
应用程序,但对很多人来说很重要。
这是 rev 的矩阵。 619.
coan - rev.619 结果
| -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.8.2 | 210 | 208 |1.01 |
----------|-----|-----|-----|
Clang-3.4 | 252 | 250 |1.01 |
----------|-----|-----|------
GCC/Clang |0.83 | 0.83|
将 301 和 619 数字并排看,有几点可以说出来。
我的目标是编写更快的代码,两个编译器都强调正确 我的努力。但是:
GCC 比 Clang 更慷慨地回报这些努力。在-O2
优化 Clang 的 619 构建比其 301 构建快 46%:-O3
Clang's
改善31%。很好,但在每个优化级别 GCC 的 619 构建都是
是 301 的两倍多。
GCC 不仅逆转了 Clang 以前的优势。并且在每次优化 GCC 级别现在比 Clang 高 17%。
Clang 在 301 构建中从 -O3
优化中获得比 GCC 更多的影响力的能力
在 619 版本中消失了。两个编译器都没有从-O3
中获得有意义的收益。
我对这种命运的逆转感到非常惊讶,我怀疑我 可能不小心构建了 clang 3.4 本身的缓慢构建(因为我构建了 它来自源)。所以我用发行版的 Clang 3.3 重新运行了 619 测试。这 结果实际上与 3.4 相同。
所以关于掉头的反应:关于这里的数字,Clang 做了很多 当我不给它时,在我的 C++ 代码中以极快的速度比 GCC 更好 帮助。当我下定决心提供帮助时,GCC 比 Clang 做得更好。
我没有将这种观察提升为原则,但我认为 “哪个编译器生成更好的二进制文件?”的教训是一个问题 即使您指定了与答案相关的测试套件, 仍然不是只为二进制文件计时的明确问题。
您的更好的二进制文件是最快的二进制文件,还是最好的二进制文件 补偿廉价制作的代码?或者最好补偿昂贵的 精心设计的代码优先考虑可维护性和重用性而不是速度?这取决于 您生成二进制文件的动机的性质和相对权重,以及 你这样做的限制条件。
无论如何,如果您非常关心构建“最好的”二进制文件,那么您 最好继续检查编译器的连续迭代如何在您的 在代码的连续迭代中“最好”的想法。
【讨论】:
为什么clang更快?例如,英特尔编译器使用了英特尔芯片的特色。铿锵使用什么来获得优势?可以重写代码让gcc有同样的性能吗? @krill_igum GCC 和 clang 是不同的(非常复杂的)程序,由不同的程序员群体编写,用于完成相同的工作:将源代码转换为目标代码。几乎不可避免的是,他们中的一个人会在任何时间点在任何选定的测试中比另一个人做得更好。获胜者不必“使用”任何特殊的“东西”来“获得优势”,而且由于这两个程序都是开源的,因此它们彼此之间没有秘密。 可以使用kcachegrind
来查明生成的可执行文件在性能上存在差异的功能。
Mike:当您进行优化工作时,您是使用 gcc 作为编译器、clang 还是两者都进行迭代?我希望您使用的任何编译器都能从优化的定向努力中获得最大的改进。
@DavidStone 对于例行的编辑/构建/测试周期,我使用 clang,因为它编译得更快,但是每当我构建包并运行 make check
(带有时间)时,我都会为 gcc 和 clang .【参考方案5】:
就生成的二进制文件的速度而言,GCC 4.8 和 Clang 3.3 之间的总体差异非常小。在大多数情况下,两个编译器生成的代码执行相似。这两个编译器都没有支配另一个。
表明 GCC 和 Clang 之间存在显着性能差距的基准测试纯属巧合。
程序性能受编译器选择的影响。如果开发人员或一组开发人员专门使用 GCC,则可以预期使用 GCC 的程序运行速度比使用 Clang 稍快,反之亦然。
从开发人员的角度来看,GCC 4.8+ 和 Clang 3.3 之间的一个显着区别是 GCC 具有 -Og
命令行选项。此选项启用不干扰调试的优化,例如,始终可以获得准确的堆栈跟踪。 Clang 中缺少此选项使得 Clang 更难用作某些开发人员的优化编译器。
【讨论】:
最近,(3.3 和 4.8)我看不到编译时间之间的差异。 (在“我的”程序中,编译时间在 10 秒到 30 秒之间)。【参考方案6】:基本上,答案是:视情况而定。 有许多基准针对不同类型的应用程序。
我的应用程序基准是:GCC > ICC > Clang。
I/O 很少,但 CPU 浮动和数据结构操作很多。
编译标志是-Wall -g -DNDEBUG -O3。
https://github.com/zhangyafeikimi/ml-pack/blob/master/gbdt/profile/benchmark
【讨论】:
【参考方案7】:我在 GCC 5.2.1 和 Clang 3.6.2 上注意到的一个特殊区别是 如果你有一个关键循环,比如:
for (;;)
if (!visited)
....
node++;
if (!*node)
break;
然后 GCC 将在使用 -O3
或 -O2
进行编译时,推测性地
展开循环八次。 Clang 根本不会展开它。通过
反复试验我发现在我的程序数据的具体情况下,
正确的展开量是 5,所以 GCC overshot 和 Clang
不足。然而,过冲对性能的不利影响更大,因此 GCC 在这里的表现要差得多。
我不知道展开的差异是普遍趋势还是 只是特定于我的场景的东西。
不久前,我写了一个 few garbage collectors 来教自己更多关于 C 中的性能优化的知识。我得到的结果在我的脑海中足以让 Clang 略微偏爱。尤其是因为垃圾 收集主要是关于指针追逐和复制内存。
结果是(以秒为单位的数字):
+---------------------+-----+-----+
|Type |GCC |Clang|
+---------------------+-----+-----+
|Copying GC |22.46|22.55|
|Copying GC, optimized|22.01|20.22|
|Mark & Sweep | 8.72| 8.38|
|Ref Counting/Cycles |15.14|14.49|
|Ref Counting/Plain | 9.94| 9.32|
+---------------------+-----+-----+
这都是纯 C 代码,我不对任何一个编译器的 编译 C++ 代码时的性能。
在 Ubuntu 15.10 (Wily Werewolf)、x86.64 和 AMD Phenom II X6 1090T 处理器上。
【讨论】:
以上是关于Clang vs GCC - 产生更快的二进制文件? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章
为啥这个结构文字在 VS2013 中通过地址而不是 gcc/clang 时会损坏?