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

Clang vs GCC - which produces better binaries?

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

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

  • 似乎仍然是一个有价值的问题和回应,许多人对此感兴趣。
  • @YasserAsmi:这两个指标-内存占用和执行速度-绝不是随意的,也不是"意见"。但是似乎Physics.SE的疾病在这里传播开来,人们也开始投票关闭,而没有在这里阅读问题文本的详细信息。
  • 该问题要求进行基准测试和比较,答案均同时给出...为什么使用此观点而不是事实比较?
  • 询问哪种编译器产生最佳二进制文件并没有基于观点的意见!
  • @BjrnLindqvist真的吗?那么,您认为由不同的编译器为同一源代码生成的二进制代码集是完全有序的集合吗?我们可以对它们进行排名吗?每个人都同意排名吗?对于大多数来源,排名是相同的吗?因此,有一个显然最好的编译器,另一个显然是第二好的,等等。因为所有这些都必须是真实的,所以这个问题不能基于观点。
  • 是的,真的。您可以以与订购世界上最快的跑步机完全相同的方式订购编译器。由于跑步者通常是按比赛(基准)排序的,因此显然也可以对编译器进行排序。
  • @BjrnLindqvist如果执行速度是唯一重要的事情,那么这是正确的,尽管即使在项目之间也可能有所不同。但是正确吗?稳定性?编译速度?快速修复错误的项目声誉如何?价钱?许可问题? 20年前,由于Microsoft不断升级,我加入了Linux。
  • @TomZych问题在于产生更好的二进制文件,编译速度,价格和其他属性无关。对于现代C / C ++程序员,比较来自同一源代码的二进制文件时,唯一相关的指标是执行速度。在理所当然的情况下并假设编译器工作正常-显然不希望使用1GB可执行文件。
  • 不明白为什么这个问题被关闭了。无论是基于观点还是基于事实,我们都想知道答案,并将其标记为"已关闭"会给它带来负面的色彩,在这里不应该有一个色彩。
  • @TomZych:如果您想了解这些因素,请提出不同的问题。这是非常具体且明确的-要求执行速度和内存占用。您可能对其他对您有益的因素感兴趣,这并不意味着这个问题无效,只是不符合您的个人利益。就像您是Java程序员一样,您想关闭每个C#问题,因为它没有谈论Java。


这是我最新的一些发现,尽管我在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好得多。

    好。

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

    好。

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

    好。

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

    好。

    好。

    • 为什么c更快?例如,intel编译器使用intel芯片的特殊功能。 c用来获得优势的是什么?可以重写代码以使gcc具有相同的性能吗?
    • @krill_igum GCC和clang是由不同的程序员小组编写的不同(非常复杂)的程序,它们可以完成相同的工作:将源代码转换为目标代码。几乎不可避免的是,在任何时候,任何选择的测试中,其中一个都会比另一个做得更好。获胜者不必使用任何特殊的"东西"来"获得优势",而且由于这两个程序都是开源的,所以彼此之间没有秘密。
    • 无论如何,您可以包括最快的优化吗? g ++ -Ofast和clang ++ -O4吗?看到带有链接时间优化的clang将会很有趣。
    • @krill_igum这将很有趣,但是我认为它使我的答案流向了"编译器战争"领域。没有自然的期望gccs -Ofast和clangs -O4具有"相当的可比性"。这些是非普通的速度压缩优化,实际上只应与经过仔细选择和深刻理解的代码进行比较,而我不愿意这样做。
    • 可以使用kcachegrind来精确定位所生成的可执行文件性能不同的功能。
    • -1:这更像是一部小说(或博客文章),而不是答案。
    • @JohnSaunders:对一个人来说,是一个详尽而深入的答案,对另一个人来说,是一部不值得他们注意的小说。告诉我,这两个人的区别所在。
    • 迈克:在执行优化工作时,您是在使用gcc作为编译器还是clang进行迭代?我希望您所使用的编译器都能从优化的直接努力中获得最大的改进。
    • @DavidStone对于常规的编辑/构建/测试周期,我使用clang,因为它编译速度更快,但是每当我构建软件包并运行make check(带有计时)时,我都会对gcc和clang进行此操作。
    • 您是否有机会将ICC加入比较范围?如果您刚上大学,他们有30天的免费许可证和1年的免费学生许可证。
    • @MikeKinghan您在rev.619中做了什么,以针对GCC进行优化?还是我错过了你的答案?
    • 您在什么硬件上测试?在Intel Haswell上,代码A可能比代码B更快,但在AMD Bulldozer上则相反。或是Atom或是Silvermont等等。甚至在英特尔Nehalem与英特尔Sandybridge之间。 (但是,对于一个可能没有一个小的热循环的大型代码库,这不太可能。)此外,您是否使用了-march=native或其他任何东西,以使编译器使用非基准指令(例如popcnt,因此,即使编译器不使用SSE / AVX自动向量化也可以提供帮助吗?或至少-mtune=native在不启用新指令的情况下进行优化。
    • @MikeKinghan对不起,我真的认为" coan"是大写的,因为在Sourceforge项目页面中是这样。
    • -0fast怎么样?


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

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

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

    • 就在今天,我在Clang编译速度上运行了一些基准测试,这对于纯C语言来说非常令人失望。使用270 KLOC clang编译35 C文件的速度仅快25%。当我看到tinycc在Linux上运行的速度有多快时,对于一个新的书面编译器来说是一个不好的结果。当使用优化-O2 / -O3时,它会变得更好,但是由于它们用于发行版本,因此在这种情况下编译器的性能无关紧要。
    • @mcandre也许Nietzche-jou是用Clang编译的,而您是用GCC编译的。


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

    • 实际上确实如此。在开发过程中,编译时间(以及由于编译导致的资源消耗)比二进制性能更是瓶颈。毕竟,我们在此阶段以Debug模式进行编译。只有在进入测试和交付阶段时,您才切换到发布模式,并尝试尽快获取二进制文件。
    • // @ Matthieu M:我发誓这个回答说"可以..",好像他在引起潜在的关注。我想也许值得一提,因为它与OP有关。
    • 同意,尽管这里有很多优点。宁愿放入第二个或第三个RAID 0驱动器,SSD或更多更快的RAM,并获得最佳的.exe性能-只要这些措施可以使您达到同等或相近的水平。有时使用多个编译器进行开发也很有帮助。它可以使您意识到不可移植的功能,并捕获否则无法发现的错误,或者浪费大量时间尝试调试代码,而更好的编译器将警告/错误提示。
    • 我今天尝试比较我编写的一些严格的性能关键整数代码,并且同时使用-O2和-O3来使GCC运行更快(22S clang-llvm 25S)。认为使用编译器开关(gcc或clang)涵盖了大多数非标准功能和静态警告。在您自己的大型项目中,而不是批量编译其他ppls代码,如果编译时间在链接时间中占主导地位,则您在构建系统中可能会出错。诸如ccache.samba.org之类的工具可以帮助您经常清洁。不断变化的编译器的另一个问题是,始终浪费在测试/验证上的投资。
    • code.google.com/p/distcc是另一个项目,如果整个库由于数据结构更改或出于验证/验证目的而需要重新编译,则该项目可以加快批量编译时间


    就生成二进制文件的速度而言,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秒之间)。


    确定这一点的唯一方法是尝试一下。 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