关于优化:Clang vs GCC-哪个生成更好的二进制文件?

Clang vs GCC - which produces better binaries?

我当前正在使用GCC,但最近发现了Clang,并且正在考虑切换。 但是,有一个决定性因素-它生成的二进制文件的质量(速度,内存占用量,可靠性)-如果gcc -O3可以生成运行速度快1%或占用内存少1%的二进制文件,那将是一个大问题。

Clang具有比GCC更好的编译速度和更低的编译时内存占用量,但是我对由此产生的编译软件的基准测试/比较非常感兴趣-您能指出我的观点还是描述您的经验?


这是我最新的一些发现,尽管我在GCC 4.7.2中的发现很窄
和Clang 3.2 for C ++。

好。

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

好。

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

好。

我维护的OSS工具是专为Linux使用GCC和Clang构建的,
以及适用于Windows的Microsoft编译器。工具coan是预处理器
以及C / C ++源文件和以下代码行的分析器:
递归下降解析和文件处理方面的计算机配置文件专业。
开发分支(这些结果与之相关)
目前包含大约90个文件中的11K LOC。它被编码,
现在,在C ++中,它具有丰富的多态性和模板,但是仍然
由于其在被砍在一起的C语言中的不那么遥远的过去而陷入了许多补丁。
未明确利用移动语义。它是单线程的。一世
并没有认真致力于优化它,而"架构"
仍然在很大程度上待办事项。

好。

我在3.2之前使用Clang作为实验编译器
因为尽管它具有出色的编译速度和诊断功能,
C ++ 11标准支持落后于当代的GCC版本
尊重科恩。在3.2版本中,这个差距已经缩小。

好。

我的Linux测试工具大致适用于当前的Coan开发流程
70K源文件混合在一个文件解析器测试用例中,压力很大
测试消耗1000个文件,而场景测试消耗<1K个文件。 除了报告测试结果外,线束还会累积并 显示消耗的文件总数和消耗的运行时间 (它只是将每个coan命令行传递给Linux time命令,
捕获并汇总报告的数字)。时机受宠若惊
事实上,任何数量的测试需要花费0个可测量的时间
全部加起来为0,但此类测试的贡献可忽略不计。的
计时统计信息显示在make check的末尾,如下所示:

好。

1
2
3
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的输入文件(读取,解析和写入输出以及诊断信息)。

好。

1
2
3
4
5
6
7
          | -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自由地使用智能指针和
    其中一种在文件处理中被大量使用。这个特别
    为了实现以下目的,智能指针类型已在以前的版本中进行了typedef定义
    编译器微分,如果std::unique_ptr
    配置的编译器对其用法有足够成熟的支持
    ,否则为std::shared_ptr。对std::unique_ptr的偏差是
    愚蠢的是,因为实际上这些指针被转移了,
    但是std::unique_ptr看起来像替换的安装选项
    std::auto_ptr在C ++ 11变体对我来说是新颖的时候。

    好。

    在实验性构建过程中,以评估Clang 3.2的持续需求
    为此和类似的差异,我无意中建立
    std::shared_ptr当我打算构建std::unique_ptr时,
    很惊讶地看到生成的可执行文件(默认为-O2)
    优化是我所见过的最快的,有时达到184
    毫秒每个输入文件。更改了源代码后,
    相应的结果是这些;

    好。

    1
    2
    3
    4
    5
    6
    7
              | -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-适合该工作。

    好。

    我无法评论的一个显而易见的问题是为什么
    当我在应用程序中使用Clang时,应该能够将O2速度提高25%
    大量使用的智能指针类型从唯一变为共享,
    而海湾合作委员会对相同的变化无动于衷。我也不知道我是否应该
    欢呼或嘘Clang的-O2优化包含的发现
    对我的智能指针选择的智慧是如此的敏感。

    好。

    更新:GCC 4.8.1 v clang 3.3

    好。

    现在相应的结果是:

    好。

    1
    2
    3
    4
    5
    6
    7
              | -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的更新,说我会
    坚持使用相同的方式进行进一步更新。但是我决定
    而是在该快照(版本301)和最新开发版本上进行测试
    我拥有通过其测试套件的快照(修订版619)。这给结果
    一点经度,我还有另一个动机:

    好。

    我的原始帖子指出,我没有为优化Coan做出任何努力
    速度。截至转速仍然如此。 301.但是,在我建造完之后
    每次我运行测试套件时,计时设备都将放入Coan测试工具中
    最新变化对性能的影响令我满目疮.。我看见了
    它通常大得令人惊讶,而且趋势比
    功能增强使我感到很值得。

    好。

    通过转。 308测试套件中每个输入文件的平均处理时间为
    自从在此发布第一篇文章以来,数量增加了一倍多。那时我做了一个
    放弃我十年不打扰性能的政策。在密集
    一直到619性能的大量修改始终是一个考虑因素,
    他们中的许多人纯粹是为了从根本上重写主要的承重人
    速度更快(尽管无需使用任何非标准的编译器功能)。看到每个编译器对此的反应将会很有趣
    掉头,

    好。

    这是最新熟悉的两个编译器版本rev.301的时序矩阵:

    好。

    柯南-301版结果

    好。

    1
    2
    3
    4
    5
    6
    7
              | -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的演出
    更好一点。 lang声更糟。噪音很可能是造成这种情况的原因。
    Clang仍然以-O2-O3的边距领先,这在大多数情况下都不重要
    应用程序,但对很多应用程序来说很重要。

    好。

    这是转速表。 619。

    好。

    柯南-rev.619结果

    好。

    1
    2
    3
    4
    5
    6
    7
              | -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的
    改善率为31%。不错,但是在每个优化级别,GCC的619版本都是
    是301的两倍以上。

    好。

  • GCC不仅扭转了Clang以前的优势。并且在每次优化时
    现在,GCC等级比Clang胜了17%。

    好。

  • Clang在301版本中具有通过-O3优化获得比GCC更多的杠杆作用的能力
    在619版本中消失了。这两个编译器都没有从-O3获得有意义的收益。

    好。

  • 好。

    我对这种命运的逆转感到惊讶,我怀疑自己
    可能是因为不小心使clang 3.4本身构建缓慢(自从我构建以来
    它来自源代码)。因此,我使用发行版的股票Clang 3.3重新运行了619测试。的
    结果实际上与3.4相同。

    好。

    因此,对于掉头的反应:在这里的数字上,C做了很多
    当我不给它任何帮助时,以比我的C ++代码快的速度胜过GCC
    救命。当我下定决心要提供帮助时,GCC的工作要比Clang好得多。

    好。

    我没有将这种观察提升为原则,但我认为
    "哪个编译器生成更好的二进制文件?"这一课是一个问题
    即使您指定答案相对应的测试套件,
    仅对二进制文件进行计时仍然不是明确的问题。

    好。

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

    好。

    在任何情况下,如果您非常关心构建"最佳"二进制文件,那么您
    最好继续检查编译器的连续迭代如何交付给您
    在代码的连续迭代中"最好"的想法。

    好。

    好。


    Phoronix对此做了一些基准测试,但是它是几个月前Clang / LLVM的快照版本。结果是事情或多或少地得到了推动。在所有情况下,GCC和Clang都没有绝对更好的选择。

    由于您将使用最新的Clang,因此相关性可能会降低一些。再说一次,GCC 4.6显然计划对Core 2和i7进行一些重大优化。

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


    Clang更快地编译代码的事实可能并不像生成二进制文件的速度那么重要。但是,这是一系列基准。


    就生成二进制文件的速度而言,GCC 4.8和clang 3.3之间的总体差异很小。在大多数情况下,两个编译器生成的代码执行情况相似。这两个编译器都不能控制另一个。

    基准表明GCC和clang之间存在明显的性能差距是偶然的。

    程序性能受编译器选择的影响。如果一个开发人员或一组开发人员专门使用GCC,那么使用GCC的程序运行速度可能会比使用clang的运行速度快一些,反之亦然。

    从开发人员的角度来看,GCC 4.8+和clang 3.3之间的显着区别是GCC具有-Og命令行选项。此选项启用了不干扰调试的优化,因此例如始终可以获取准确的堆栈跟踪。在clang中缺少此选项使clang难以用作某些开发人员的优化编译器。


    确定这一点的唯一方法是尝试一下。 FWIW与常规的gcc 4.2(用于具有很多SSE的x86-64代码)相比,我已经看到使用Apple的LLVM gcc 4.2确实取得了一些非常好的改进,但是针对不同的代码库使用了YMMV。假设您使用的是x86 / x86-64,并且确实在乎最后几个百分比,那么您也应该尝试使用Intel的ICC,因为它通常可以胜过gcc-您可以从intel.com获得30天的评估许可证尝试一下。


    我在gcc 5.2.1和clang 3.6.2上注意到的一个特殊区别是
    如果您有一个关键循环,例如:

    1
    2
    3
    4
    5
    6
    7
    for (;;) {
        if (!visited) {
            ....
        }
        node++;
        if (!*node) break;
      }

    然后,当使用-O3-O2进行编译时,gcc将以推测方式
    展开循环八次。 Clang根本不会展开它。通过
    反复试验发现,在我的特定情况下,我的程序数据中
    正确的展开量是五,所以gcc过度拍摄和叮当响
    下击。但是,过度射击更不利于表现,因此
    海湾合作委员会在这里的表现要差得多。

    我不知道展开差异是大趋势还是
    只是我的情况所特有的东西。

    前阵子我写了一些垃圾
    收藏家多教自己
    有关C语言中的性能优化的信息。
    有足够的心态来稍微喜欢c。特别是因为垃圾
    收集主要是关于指针的追逐和复制内存。

    结果是(以秒为单位的数字):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    +---------------------+-----+-----+
    |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,x86.64和AMD Phenom(tm)II X6 1090T处理器上。


    基本上,答案是:取决于情况。
    有许多针对不同应用程序的基准测试。

    我对我的应用程序的基准是:gcc> icc> clang。

    很少有IO,但是有许多CPU浮动和数据结构操作。

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

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