Clang vs GCC——哪个生成更快的二进制文件?

我目前正在使用GCC,但我最近发现了Clang,我正在考虑切换。但是有一个决定因素——它产生的二进制文件的质量(速度、内存占用、可靠性)——如果__abc0可以产生运行速度快1%的二进制文件,或者Clang二进制文件占用更多内存,或者由于编译器错误而失败,这是一个交易破坏者。

Clang拥有比GCC更好的编译速度和更低的编译时内存占用,但我真正感兴趣的是最终编译软件的基准测试/比较——您能给我指出一些已有的资源或您自己的基准测试吗?

149978 次浏览

唯一能确定的方法就是试一试。FWIW,我已经看到了一些非常好的改进使用苹果的LLVM GCC 4.2相比常规的GCC 4.2 (x86-64代码有相当多的SSE),但YMMV不同的代码库。

假设你正在使用x86/x86-64,并且你真的关心最后的百分之几,那么你应该尝试英特尔的国际刑事法庭,因为它通常可以击败GCC -你可以从intel.com获得30天的评估许可并尝试它。

Phoronix做了一些基准测试关于这个,但它是关于几个月前Clang/LLVM的快照版本。结果是,事情或多或少是一种推动;GCC和Clang在所有情况下都不是更好的。

因为你使用的是最新的Clang,所以它可能不太相关。然后,GCC 4.6显然为酷睿2酷睿i7指定了一些主要的优化

我认为Clang更快的编译速度对原始开发人员来说更好,然后当你把代码推向世界时,Linux发行版,BSD等最终用户将使用GCC来获得更快的二进制文件。

Clang编译代码更快的事实可能没有生成二进制代码的速度重要。然而,这里有一个一系列基准测试

以下是我对GCC 4.7.2的一些最新发现 和Clang 3.2 c++ .

更新:GCC 4.8.1 v clang 3.3比较附加在下面。

更新:GCC 4.8.2 v clang 3.4比较附加到此。

我维护一个OSS工具,它是为GCC和Clang Linux构建的, 以及微软的Windows编译器。这个工具可恩是一个预处理器 和C/ c++源文件和代码线的分析器:its 计算概要文件主要研究递归下降解析和文件处理。 开发分支(这些结果属于该分支) 目前约有90个档案,约有11K LOC。它是编码的, 现在,在c++中,这是丰富的多态性和模板,但仍然是 由于其不太久远的过去在C。 没有明确地利用Move语义。它是单线程的。我 没有认真的努力去优化它,而“架构”;

我使用Clang在3.2之前只是作为一个实验性的编译器 因为,尽管它具有卓越的编译速度和诊断能力,但它 c++ 11标准的支持落后于当代GCC版本 尊重coan的行为。在3.2版本中,这个差距已经被缩小了

我的Linux测试工具目前coan开发过程大致 70K源文件混合在一个文件解析器测试用例中,强调 测试消耗1000个文件,场景测试消耗<1 k文件。< / p >

除了报告测试结果外,还会累积和 显示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个结果的平均值,如下 结果。数据单元格中的数字是的平均值 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|
任何特定的应用都很可能具有发挥作用的特征 不公平的编译器的优势或弱点。严格的基准测试 采用多种应用程序。考虑到这一点,值得注意的是 这些数据的特征是:

  1. -O3优化对GCC不利
  2. -O3优化对Clang有重要的帮助
  3. 在-O2优化时,GCC比Clang快一点点
  4. 在-O3优化时,Clang明显快于GCC。
对这两个编译器的进一步有趣的比较偶然出现 在这些发现之后不久。Coan大量使用智能指针和 其中一个在文件处理中得到了大量的练习。这个特殊的 智能指针类型在之前的版本中已经进行了类型定义 编译器-微分,如果 配置编译器已经足够成熟的支持其用法 ,否则是std::shared_ptr<X>。对std::unique_ptr的偏向是 愚蠢,因为这些指针实际上被转移了, 但是std::unique_ptr看起来更适合替换 在c++ 11变体对我来说是新奇的时候std::auto_ptr

在测试构建过程中,以衡量Clang 3.2的持续需求 对于这种和类似的分化,我不经意间就建立起来了 当我打算构建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 |

这里需要注意的是:

  1. 这两个编译器现在都不能从-O3优化中获益。
  2. Clang在每个优化级别上都击败了GCC。
  3. GCC的性能仅受到智能指针类型的轻微影响 李改变。< / > Clang的-O2性能受到智能指针类型的重要影响 李改变。< / >
在智能指针类型改变之前和之后,Clang能够构建一个 实质上更快的coan可执行在-O3优化,它可以 在-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文件反映最新编译器的性能。这是由于 事实上,测试应用程序的后期开发分支已经承担了大量的工作 与此同时,解析的复杂性并以速度为代价。只有比率 重要。< / p >

现在的要点并不新奇:

  • 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 snaphot进行进一步更新。但我决定 而是在最新开发的快照(rev. 301) 而且上进行测试 我拥有的快照通过了它的测试套件(rev. 619)。这给出了结果a 位经度,我有另一个动机:

我最初的帖子指出,我没有花任何精力优化coan for 速度。在第301章中仍然是这样。然而,在我建成之后 每次我运行测试套件时,都要把计时装置放进coan测试装置中 最新更改对性能的影响一目了然。我看到了 它往往惊人的大,而且趋势比

到rev. 308,测试套件中每个输入文件的平均处理时间 比第一个帖子翻了一倍多。那时我做了一个 彻底改变我10年来不计较业绩的政策。在密集状态下 大量的修改到619性能一直是一个考虑和一个 他们中的许多人纯粹从根本上重写了关键负载 更快的行(尽管没有使用任何非标准编译器特性来实现)。看看每个编译器对此的反应会很有趣 掉头,< / p >

下面是我们现在熟悉的最新两个编译器版本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相比只有轻微的变化。GCC的显示 好一点。Clang的稍微差一点。噪音可以很好地解释这一点。 Clang仍然在-O2-O3边缘领先,这在大多数情况下并不重要

这是619版的矩阵。

coan - rev.619 results

          | -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数字放在一起比较,有几点是显而易见的。

    我的目标是编写更快的代码,两个编译器都强调证明了这一点 我的努力。但是:< / p > GCC比Clang更慷慨地回报了这些努力。在-O2 优化Clang的619构建比301构建快46%:在-O3 Clang的 改善31%。很好,但是在每个优化级别GCC的619版本都是

  • GCC超过了Clang以前的优势。在每个优化点

    . GCC现在比Clang提高17%
  • Clang在301构建中的能力从-O3优化中获得比GCC更多的杠杆 在619版本消失了。两个编译器都没有从-O3中获得有意义的东西
我对这种命运的逆转感到非常惊讶,我怀疑我自己可能会失败 可能会意外地使clang 3.4本身的构建变得缓慢(因为我构建了 它来自源代码)。所以我用发行版的Clang 3.3重新运行了619测试。的

所以关于对180度大转弯的反应:在这里的数字上,Clang已经做了很多 比GCC在压榨速度从我的c++代码,当我给它没有 的帮助。当我想要帮忙的时候,GCC做得比Clang好得多 我没有把那个观察结果提升为原则,但我认为 “哪个编译器生成的二进制文件更好?”是个问题 那就是,即使你指定了与答案相关的测试套件, 仍然不是一个明确的问题,只是计时的二进制

你更好的二进制是最快的二进制,还是最好的二进制 补偿廉价的代码?或最好地补偿昂贵 精心设计的代码优先考虑可维护性和重用而不是速度?这取决于 你产生二进制的动机的性质和相对权重,以及 你这样做的约束。

无论如何,如果你非常关心打造“最好的”;然后是二进制 最好继续检查编译器的连续迭代是如何交付您的 “最好”的理念;

GCC 4.8和Clang 3.3在生成的二进制文件的速度方面有很小的整体差异。在大多数情况下,两个编译器生成的代码执行相似。这两个编译器都不能支配另一个编译器。

GCC和Clang之间存在显著性能差距的基准测试是巧合。

程序性能受编译器选择的影响。如果一个开发人员或一组开发人员只使用GCC,那么使用GCC可以预期程序比使用Clang运行得稍微快一些,反之亦然。

从开发人员的角度来看,GCC 4.8+和Clang 3.3之间的显著区别是GCC有-Og命令行选项。该选项支持不干扰调试的优化,因此,例如,总是可以获得准确的堆栈跟踪。Clang中缺少这个选项使得Clang难以作为一些开发人员的优化编译器使用。

基本上来说,答案是:视情况而定。 有很多很多的基准测试专注于不同类型的应用程序

我对我的应用程序的基准是:GCC >国际刑事法庭祝辞叮当声。

很少有I/O,但有很多CPU浮动和数据结构操作。

编译标志是-Wall -g -DNDEBUG -O3

https://github.com/zhangyafeikimi/ml-pack/blob/master/gbdt/profile/benchmark

我注意到GCC 5.2.1和Clang 3.6.2的一个特殊差异是 如果你有一个关键循环:

for (;;) {
if (!visited) {
....
}
node++;
if (!*node)
break;
}

那么GCC在使用-O3-O2编译时会推测 展开这个循环八次。叮当根本无法展开它。通过 我发现,在我的程序数据的特定情况下, 正确的展开量是5,所以GCC溢出和Clang 下颚突出的。然而,超调对性能更不利,因此GCC在这里的表现要差得多

我有不知道如果展开的差异是一个总体趋势或 只是一些特定于我的场景。

前段时间,我写了一个垃圾收集者少来教自己更多关于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代码,我对编译器没有任何要求

Ubuntu 15.10 (Wily Werewolf)上,x86.64,和一个AMD 杰出人才 II X6 1090T处理器上。