我目前正在使用GCC,但最近发现了Clang,并且正在考虑切换。但是,有一个决定性因素-它生成的二进制文件的质量(速度,内存占用量,可靠性)-如果gcc -O3
可以生成运行速度快1%或占用内存减少1%的二进制文件,那么这是一个决定性的因素。
与GCC相比,Clang具有更好的编译速度和更低的编译时内存占用空间,但是我对生成的编译软件的基准测试/比较非常感兴趣-您能指出我的观点还是描述您的经验?
我目前正在使用GCC,但最近发现了Clang,并且正在考虑切换。但是,有一个决定性因素-它生成的二进制文件的质量(速度,内存占用量,可靠性)-如果gcc -O3
可以生成运行速度快1%或占用内存减少1%的二进制文件,那么这是一个决定性的因素。
与GCC相比,Clang具有更好的编译速度和更低的编译时内存占用空间,但是我对生成的编译软件的基准测试/比较非常感兴趣-您能指出我的观点还是描述您的经验?
Answers:
这是我最新的一些信息,尽管我在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以及Microsoft的Windows编译器构建的。该工具coan是C / C ++源文件及其代码行的预处理器和分析器:其计算配置文件主要涉及递归下降解析和文件处理。开发分支(与这些结果有关的分支)目前包含约90个文件,大约11K LOC。现在,它使用C ++进行编码,该C ++具有丰富的多态性和模板,但是由于其在被砍在一起的C语言中的不那么遥远的过去而仍然陷入许多补丁中。移动语义未得到明确利用。它是单线程的。尽管“架构”仍然主要是待办事项,但我并未花大力气对其进行优化。
我在3.2之前只使用Clang作为实验性编译器,因为尽管Clang 11具有出色的编译速度和诊断功能,但其C ++ 11标准支持在coan方面落后于当代的GCC版本。在3.2版本中,这个差距已经缩小。
我的用于当前coan开发的Linux测试工具在一个文件解析器测试用例,压力测试(消耗1000个文件)和方案测试(小于1K文件)的混合中,大约处理了70K个源文件。除了报告测试结果之外,该工具还累积并显示消耗的文件总数和用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|
任何特定的应用程序很可能具有对编译器的优缺点不公平地发挥作用的特征。严格的基准测试使用多种应用程序。牢记这一点,这些数据的值得注意的特征是:
在发现之后不久,偶然地出现了两个编译器的进一步有趣的比较。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 |
这里的要点是:
在更改智能指针类型之前和之后,Clang能够在优化-O3的情况下构建速度显着更快的coan可执行文件,并且当该指针类型是最佳指针类型时,它可以在-O2和-O3处构建同样更快的可执行文件std::shared_ptr<X>
--为了工作。
我无法评论的一个显而易见的问题是, 当频繁使用的智能指针类型从唯一更改为共享,而GCC却无动于衷时,为什么 Clang应该能够在我的应用程序中将O2速度提高25%进行相同的更改。我也不知道我该为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 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)和最新开发快照我有通过其测试套件(REV。619)。这使结果有点经度,而我还有另一个动机:
我的原始帖子指出,我没有投入任何精力来优化速度。截至转速仍然如此。301.但是,当我将计时装置内置到普通测试装置中后,每次运行测试套件时,最新变化的性能影响使我无所适从。我发现它通常大得令人惊讶,而且趋势比我认为功能增强带来的负面影响要严重得多。
通过转。308自从第一次发布这里以来,测试套件中每个输入文件的平均处理时间已增加了一倍以上。那时,我对10年不打扰性能的政策做了个大转变。在大量的修订中,始终要考虑到高达619个性能,并且其中许多纯粹用于在根本上更快的行上重写关键的负载承受力(尽管这样做没有使用任何非标准的编译器功能)。看到每个编译器对这次掉头的反应都会很有趣,
这是最新熟悉的两个编译器版本rev.301的时序矩阵:
柯南-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的表现要好得多。lang声更糟。噪音很可能造成了这种情况。Clang仍然遥遥领先,-O2
其-O3
利润在大多数应用程序中并不重要,但对很多应用程序而言却至关重要。
这是转速表。619。
柯南-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的情况下提高31%。不错,但是在每个优化级别,GCC 619的构建速度都是其301的两倍以上。
GCC不仅扭转了Clang以前的优势。现在,在每个优化级别,GCC都比Clang高出17%。
Clang在301版本中从-O3
优化中获得比GCC更大的杠杆作用的能力在619版本中消失了。编译器都没有从中获得有意义的收益-O3
。
运气的逆转让我感到十分惊讶,以至于我怀疑自己可能不小心制造了c铛3.4本身(因为我是从源头构建的)。因此,我使用发行版的股票Clang 3.3重新运行了619测试。结果实际上与3.4相同。
因此,对于掉头的反应:在这里的数字上,当我没有提供帮助时,Clang的工作速度比GCC快得多,比我的C ++代码快得多。当我下定决心要提供帮助时,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难以用作某些开发人员的优化编译器。
我在gcc 5.2.1和clang 3.6.2上注意到的一个特殊区别是,如果您有一个关键循环,例如:
for (;;) {
if (!visited) {
....
}
node++;
if (!*node) break;
}
然后gcc将在使用-O3
或进行编译时-O2
推测性地将循环展开八次。Clang根本不会展开它。通过反复试验,我发现在我的程序数据特定情况下,正确的展开量为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代码,在编译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