背景
Visual C ++,GNU编译器集合(GCC)和Clang /低级虚拟机(LLVM)是业界三种主流的C / C ++编译器。Visual C ++提供了图形用户界面(GUI),易于调试,但不适用于Linux平台。因此,本文主要比较GCC与Clang / LLVM。
GCC是GNU开发的一种程序语言编译器。它是根据GNU通用公共许可证(GPL)和GNU较小通用公共许可证(LGPL)发布的一组免费软件。它是GNU和Linux系统的官方编译器,也是用于编译和创建其他UNIX操作系统的主要编译器。
LLVM包含一系列模块化的编译器组件和工具链。它可以在编译,运行时和空闲时间优化程序语言和链接,并生成代码。LLVM可以作为多种语言的编译器的背景。Clang是一种C,C ++,Objective-C或Objective-C ++编译器,它基于LLVM用C ++编译,并根据Apache 2.0许可发行。Clang主要用于提供优于GCC的性能。
通过长期的开发和迭代,GCC,Clang和LLVM已成为业界成熟的编译器。那么,哪个编译器更好?我们应该使用哪一个来编译和构建程序和系统?
好的编译器的意义
现代处理器都具有超标量和较长的流水线以及复杂的内部结构,并且它们支持复杂指令集计算机(CISC)或精简指令集计算机(RISC)架构中的向量扩展单元。对于许多包含通用计算密集型内核的程序,程序员可以使用矢量扩展命令来大大提高程序执行性能。例如,在矩阵和向量运算中,组合的乘法和加法命令用于提高性能和准确性。位掩码命令用于矢量操作中的分支处理。但是,要获得最高的性能,程序员和编译器仍需要花费大量精力来处理具有复杂内存访问模式和非标准内核的任务。
另外,现代高级语言的标准不断地抽象底层硬件和数据结构的细节,以生成更具逻辑性和数学性的通用代码,而不是特定的操作指令和内存访问路径。C ++标准越来越具有表现力和抽象性。Python之所以受欢迎,是因为Python更具可读性和表达力,即使以降低运行速度为代价。较高的表达力增加了编译器从程序员编译的复杂结构中生成良好的汇编代码的负担。编译器必须更聪明并且更努力地工作,以通过使用代码来最大化性能。并非所有的编译器都可以做到这一点。在选择编译器时,我们必须首先考虑同一代码段是否可以生成更有效的汇编命令。
除了生成高性能的可执行程序外,现代编译器本身也必须具有高性能。C ++中的大型软件项目可能包含数百至数千个单独的翻译单元。每个翻译单元可能包含数千行代码。C ++代码也可以使用大量基于模板的编程技术。这些技术要求编译器多次传输相关信息以生成目标文件。大型C ++项目的编译可能需要几个小时,并且在开发过程中必须同时提交多个相互依赖的更改。每个提交都需要开发人员重新编译大多数代码库。因此,对于大型团队来说,更快的编译器(构建工具)对于实现高生产率至关重要。
在语言扩展方面,具有多个内核,向量处理功能和加速器的现代计算系统所提供的功能要优于普通编程语言的自然功能。因此,特定的高性能计算(HPC)框架(例如OpenMP和OpenACC)可以填补这一空白。这些框架提供了程序员可以用来表达代码并行性的应用程序接口(API)。编译器和相应的运行时库必须将并行代码映射到处理器体系结构。许多HPC项目依赖于OpenMP和OpenACC标准,开发人员和硬件制造商正在扩展这些标准。因此,编译器必须跟上语言扩展标准的发展。
总而言之,一个好的编译器使我们能够专注于编程过程,而不是克服其缺点。它可以支持最新的语言标准,可以从最抽象的代码生成优化的命令,并且可以在更短的时间内编译源代码。
GCC发展历程
在学习GCC之前,您需要首先了解GNU工程。理查德·斯托曼(Richard Stallman)于1984年发起了GNU工程,以构建一个类似UNIX的开源软件系统。随着时间的流逝,GNU操作系统尚未广泛发展。但是,它已经孵化了许多出色且有用的开源软件工具,例如Make,Sed,Emacs,Glibc,GDB和GCC。这些GNU开源软件和Linux内核共同构成了GNU / Linux系统。最初,GCC为CNU语言提供了基于C编程语言的稳定可靠的编译器。它的全名是GNU C编译器。后来,支持了更多的语言(例如Fortran,Obj-C和Ada),并且GCC的全名更改为GNU Compiler Collection。
GCC-1.0由理查德·斯托曼(Richard Stallman)于1987年发布,距今已有30多年的历史。从软件角度来看,它已经很老了。有人收集了1989年至2012年之间的GCC开发记录,并制作了一个三十分钟的动画视频(1989-2012年GNU Compiler Collection开发历史),直观地展示了GCC的开发过程。我们可以从GCC的版本中了解其发展历史:
- GCC-1.0:由Richard Stallman在1987年发布。
- GCC-2.0:1992年发布并支持C ++。后来,GCC社区分裂了,因为Richard Stallman将GCC定义为GNU系统的可靠C编译器,并认为当时的GCC对于GNU系统已经足够了,开发重点应从GCC转移到GNU系统本身。其他主要开发商希望继续改善GCC,并在各个方面做出更根本的发展。这些活跃的开发人员于1997年离开GCC社区,并开发了EGCS fork。
- GCC-3.0:显然,开发人员通常对好的编译器有强烈的渴望。EGCS分支发展顺利,并得到越来越多开发人员的认可。最终,EGCS被用作新的GCC主干,并于2001年发布了GCC-3.0。分裂的社区再次被合并,但是Richard Stallman的影响力在一定程度上被削弱了。此外,海湾合作委员会工业委员会已开始决定海湾合作委员会的发展方向。
- GCC-4.0:2005年发布。此版本已集成到树串行存储体系结构(SSA)中,并且GCC演变为现代编译器。
- GCC-5.0:2015年发布。后来,GCC版本政策进行了调整,并且每年都会发布主要版本。意外的好处是版本号与年份相对应。例如,GCC-7于2017年发布,GCC-9于2019年发布。
现在,海湾合作委员会的发展已进入“现代纪事”。面对LLVM的竞争压力,GCC社区积极进行了许多调整,例如加快编译速度和改进编译警告信息。在过去的30年中,GCC已从编译器行业的挑战者发展成为Linux系统的主流编译器,现在正面临LLVM的挑战。幸运的是,GCC社区正在进行调整以加快GCC的发展。可以预期,两种编译技术之间的竞争将继续为软件开发人员提供更好的编译器。
Clang和LLVM的发展历史
虚拟机
LLVM源自Chris Lattner在2000年对UUIC的研究。ChrisLattner希望为所有静态和动态语言创建一种动态编译技术。LLVM是根据BSD许可开发的一种开源软件。最初的1.0版本于2003年发布。2005年,Apple Inc.聘请Chris Lattner及其团队为Apple计算机开发编程语言和编译器,此后LLVM的开发进入了快车道。从LLVM 2.5开始,每年都会发布两个次要的LLVM版本(通常在三月和九月)。2011年11月,LLVM 3.0发布,成为默认的XCode编译器。默认情况下,XCode 5开始使用Clang和LLVM 5.0。版本策略针对LLVM 5.0和更高版本进行了调整,并且每年发布两个主要版本。当前的稳定版本是8.0。
LLVM的名称最初是Low Level Virtual Machine的缩写。由于该项目不仅限于创建虚拟机,因此经常会缩写LLVM。LLVM开发之后,它成为许多编译工具和低级工具技术的统称,因此名称不太合适。开发人员决定放弃此缩写背后的含义。现在,LLVM已成为正式的商标名称,适用于LLVM下的所有项目,包括LLVM中间表示(LLVM IR),LLVM调试工具和LLVM C ++标准库。LLVM可用作传统的编译器,JIT编译器,汇编器,调试器,静态分析工具,以及与编程语言相关的其他功能。
在2012年,LLVM与UNIX,WWW,TCP / IP,TeX和Java等传统系统一起获得了计算机协会(ACM)的软件系统奖。LLVM大大简化了新编程语言工具链的实施。近年来,许多新的编程语言,例如Swift,Rust和Julia,都使用LLVM作为其编译框架。此外,LLVM已成为Mac OS X,iOS,FreeBSD和Android系统的默认编译器。
铛
Clang旨在提供可以替代GCC的前端编译器。苹果公司(后来包括NeXT)一直在使用GCC作为官方编译器。作为开源社区中的标准编译器,GCC始终表现良好。但是,Apple Inc.对编译工具有其自己的要求。一方面,Apple Inc.为Objective-C语言(甚至后来的C语言)添加了许多新功能。但是,GCC开发人员不接受这些功能,因此分配了较低的优先级来支持这些功能。后来,它们被简单地分为两个分支以进行单独的开发,因此Apple Inc.发布的GCC版本远远早于正式版本。另一方面,GCC代码是高度耦合的,很难单独开发。此外,在更高版本中,代码质量持续下降。但是,Apple Inc.所需的许多功能(例如改进的集成开发环境(IDE)支持)必须将GCC称为模块,但是GCC从未提供这种支持。而且,GCC运行时库豁免从根本上限制了LLVM GCC的开发。同样受许可证限制,Apple Inc.无法使用LLVM进一步提高基于GCC的代码生成质量。因此,Apple Inc.决定从头开始编写C,C ++和Objective-C语言的前端Clang,以完全取代GCC。
顾名思义,Clang仅支持C,C ++和Objective-C。开发工作于2007年开始,C编译器首次完成。Objective-C云的Clang将于2009年在生产环境中完全使用。对C ++的支持也在迅速发展。Clang 3.3完全支持C ++ 11,Clang 3.4完全支持C ++ 14,Clang 5完全支持C ++ 17,当时所有这些都大大领先于GCC。
Clang / LLVM和GCC社区
海湾合作委员会社区
与其他开源软件社区一样,GCC社区也由*软件爱好者和黑客主导。在发展过程中,海湾合作委员会的社区管理和参与机制逐渐形成。当前,GCC社区是一个相对稳定且定义明确的熟人社会,其中每个人都有明确的角色和职责:
- Richard Stallman和*软件基金会(FSF):尽管很少参与GCC社区管理,但Richard Stallman和FSF仍然在执照和法律事务方面与众不同。
- 海湾合作委员会工业委员会:它负责管理海湾合作委员会社区事务,与技术无关的海湾合作委员会发展主题以及审阅者和维护者的任命和宣布。目前有13个成员。
- 全球维护者:他们主导着GCC开发活动。它们在一定程度上决定了海湾合作委员会的发展趋势。目前,全球有13位维护者,他们都不在GCC工业委员会中任职。
- 前端,中端和后端维护者:它们是前端,后端和其他模块的维护者。它们负责相应GCC模块的代码,其中许多是模块代码的主要贡献者。值得注意的是,审稿人通常分为这一类。不同之处在于,审阅者无法批准自己的补丁程序,而维护者可以在其职责范围内提交自己的修改,而无需获得审阅者的批准。
- 贡献者:他们是GCC社区中最广泛的开发人员群体。签署版权协议后,任何开发人员都可以从社区申请“批准后写入”许可,然后自行提交代码。
与其他开源社区一样,成熟的GCC社区不再由黑客主导。商业公司开始在社区中扮演重要角色,例如招募开发人员和赞助开发会议。当前,海湾合作委员会社区由以下类型的商业公司所主导:
- 系统供应商,主要包括RedHat和SUSE。
- 芯片供应商,主要包括Intel,ARM,AMD和IBM(PowerPC)。
- 专门的供应商(例如CodeSourcery)和工具链服务提供商(例如基于Ada语言的AdaCore)。CodeSourcery拥有辉煌的历史,并招募了许多著名的开发人员,但在被Mentor收购后拒绝了。
在当前的GCC社区中,芯片厂商主导着后端开发,而系统厂商则主导着其他开发领域。在社区发展方面,GCC代码当前托管在其自己的SVN服务器上。提供了一个Git API来促进开发和提交。补丁程序审查与Linux Kernel社区中的补丁程序审查相似,并使用“邮件列表”表单。如上所述,海湾合作委员会社区是一个相对稳定(或封闭)的熟人社会。社区基本上每年有150至200名活跃的贡献者,并且每年9月举行一次开发者大会。2019年9月,开发者大会将在加拿大蒙特利尔举行。
LLVM社区
LLVM社区是一个对菜鸟友好的编译器社区。它可以快速响应新用户和补丁程序审查的问题。这也是随后的LLVM基金会讨论和采用LLVM社区行为准则的基础和来源,并引起了一系列政治上正确的讨论。
通过DevExpress电子邮件列表讨论了所有LLVM项目和问题,并通过提交电子邮件列表通知了代码提交。通过错误列表跟踪所有错误和功能修改。建议将提交的修补程序用于master分支。该样式符合LLVM编码标准,并且代码审查通过Phabricator执行。当前,LLVM代码存储库已迁移到GitHub。
与GCC社区不同,LLVM社区只有LLVM Foundation。LLVM基金会有八名成员。除了管理LLVM社区事务外,LLVM基金会的每个成员还必须指导与技术有关的LLVM开发问题。目前,总统是克里斯·拉特纳(Chris Lattner)的妻子Tanya Lattner。克里斯·拉特纳本人也是基金会的成员,并且对LLVM社区和LLVM的发展方向拥有强大的控制权。
LLVM社区中的代码审查策略与GCC社区中的代码审查策略基本相同。区别在于,由于LLVM的快速发展,许多贡献者没有提交访问权限,而必须通过维护者提交其代码。目前,Clang和LLVM社区每年有1000多个贡献者。通常,开发人员会议每年4月和10月举行。开发者大会将于2019年10月在美国圣何塞举行。
LLVM许可证从UIUC许可证更改为Apache 2.0许可证(LLVM例外除外)。它主要用于解决LLVM运行时库基于MIT许可证且项目所需的专利授权范围太广的问题。根据此许可,LLVM允许任何人不受限制地从LLVM衍生商业产品,并且不需要任何衍生产品提供开源代码,从而促进LLVM的广泛使用,包括:
- 为个人,内部或商业目的而全部或部分下载或使用LLVM。修改LLVM代码而不将其贡献回项目的能力。
- 创建包含LLVM的软件包或发行版。LLVM与所有其他主要开放源代码许可(包括BSD,MIT,GPLv2和GPLv3)授权的代码的关联。
- 再次分发LLVM时,您必须保留版权声明。您不能删除或替换版权标题。包含LLVM的二进制文件必须包含版权声明。
GCC和LLVM之间的性能比较
测试服务器
体系结构:x86_64
处理器:英特尔(R)至强(R)铂8163 CPU @ 2.50 GHz
L1数据缓存:32 KB
L2缓存:1,024 KB
L3缓存:33,792 KB
内存:800 GB
操作系统:阿里巴巴集团企业Linux服务器版本7.2( Paladin)
内核:4.9.151-015.ali3000.alios7.x86_64
编译器:Clang / LLVM 8.0 GCC8.3.1
基准测试
SPEC CPU 2017是一组CPU子系统测试工具,用于测试CPU,缓存,内存和编译器。它包含四个类别的43个测试,其中包括SPECspeed 2017 INT和FP,用于测试整数速度和浮点运算速度;以及SPECrate 2017 INT和FP,用于测试整数并发率和浮点并发率。Clang不支持Fortran语言。因此,在此示例中,SPEC Speed测试集中的C / C ++程序用于测试Clang和GCC生成的二进制程序之间的单核性能差异。下表列出了SPEC CPU2017 C和C ++集:
CINT2017速度 | CFP2017速度 |
600.perlbench_s | 619.lbm_s |
602.gcc_s | 644.nab_s |
605.mcf_s | |
620.omnetpp_s | |
623。 | |
625.x264_s | |
631。 | |
641。 | |
657.xz_s |
测试方法
该LLVM-LNT自动化框架用于执行测试并比较性能。它的运行方式与SPEC CPU的runcpu相同。在LLVM-lnt运行之前,先清除缓存(回显3> / proc / sys / vm / drop_caches),然后运行测试数据集。接下来,ref数据集运行3次。三个参考测试运行结果的平均值用作最终结果。为了减少由CPU迁移或上下文切换引起的性能波动,使用CPU亲和力工具将在测试数据集和ref数据集上运行的进程绑定到CPU内核。对于编译时测试,此方法使用线程1来构建测试程序并比较已编译很长时间的测试项。编译时间不包括链接程序执行时间。它仅包括生成所有测试程序中的所有源文件的时间。
编译性能比较
GCC的编译过程如下:读取源文件,对源文件进行预处理,将其转换为IR,优化并生成汇编文件。然后,汇编器生成一个目标文件。Clang和LLVM不依赖独立的编译器,而是在后端集成自实现的编译器。在生成目标文件的过程中省略了生成程序集文件的过程。目标文件直接从IR生成。此外,与GCC IR相比,LLVM IR的数据结构更加简洁。它在编译期间占用较少的内存,并支持更快的遍历。因此,Clang和LLVM在编译时间方面具有优势,这可以通过从SPEC编译获得的数据来证明,如下图所示。与GCC相比,Clang将单线程编译时间减少了5%到10%。
SPEC编译时间比较
执行性能比较
大多数云工作负载要求应用程序可以在不同的群集中运行。创建这些应用程序时,请勿指定与机器相关的参数。为了适应需求变化导致的快速迭代,本地工作负载也必须是可调试的。因此,除了一些稳定且通用的库可以实现较高的编译优化级别之外,工作负载本身的编译和优化级别也很低(O2或更低)。为了满足此要求,本文档比较了INT Speed程序在O2和O3优化级别上不同编译器的性能,如下图所示:
SPEC CPU2017 INT Speed的性能比较
对于O2和O3级别的大多数程序,GCC在Clang和LLVM上具有1%至4%的性能优势,而SPEC CPU2017 INT Speed平均具有约3%的性能优势。就600.perlbench_s和602.gcc_s / O2而言,GCC具有很大的性能优势(超过10%)。这两个测试项目没有突出的热点,可以反映编译器的综合优化效果。测试结果表明,GCC在性能优化方面始终具有优势。但是,对于两个新添加到SPEC测试中的AI相关程序,包括631.deepsjeng_s和641.leela_s,与GCC相比,Clang和LLVM的性能提高了3%以上。这也反映了LLVM在优化方面的快速发展。对于625。x264_s O2优化,LLVM将性能提高了40%,因为案例的热点符合矢量化规则。但是Clang和LLVM在O2级别优化矢量,而GCC在O3级别优化矢量。除了矢量化程序外,与O2级别相比,GCC在O3级别上没有显着提高性能。换句话说,这些程序对GCC O3优化不敏感。相反,Clang和LLVM在O3级别显着提高了某些程序(例如600. perlbench_s和602. gcc_s)的性能。这些程序对GCC O3优化不敏感。相反,Clang和LLVM在O3级别显着提高了某些程序(例如600. perlbench_s和602. gcc_s)的性能。这些程序对GCC O3优化不敏感。相反,Clang和LLVM在O3级别显着提高了某些程序(例如600. perlbench_s和602. gcc_s)的性能。
诸如FP Speed之类的HPC程序通常在高端服务器上运行。它们具有稳定的核心算法,对与性能相关的矢量化和并行性有很高的要求,并且可以进行高水平的优化(O3或更高)。因此,本文档比较了O3 + march =本机(skylake-avx512)优化级别的性能,如下所示:
SPEC CPU2017 FP Speed性能比较
对于这两个FP程序,GCC还可将性能提高约3%。Clang和LLVM在循环优化方面比较保守,因此在性能上不利。但是,Clang和LLVM的Polly子项目提供了高级循环和数据局部性优化器,已广泛应用于机器学习,高性能计算和异构计算优化中。我相信Polly可以极大地提高包含符合矢量化和并行规则的热点循环的程序的性能。我还将在一系列基准测试和工作负载中分析Polly的性能。
结束语
从上面的基准测试中,我们可以看到Clang在大型项目的构建方面具有更多优势,而GCC在性能优化方面始终具有优势。bla取决于您的特定应用
除了性能比较之外,我还要分享GCC和Clang和LLVM的优缺点:
海湾合作委员会的优势
- 与Clang和LLVM相比,GCC支持更多的传统语言,例如Ada,Fortran和Go。
- GCC支持较少流行的架构,并且比Clang和LLVM更早支持RISC-V。
- 与Clang和LLVM相比,GCC支持更多的语言扩展和更多的汇编语言功能。GCC仍然是编译Linux内核的唯一选择。尽管业界也报道了使用Clang和LLVM进行内核编译的研究,但是如果不修改源代码和编译参数就无法编译内核。
Clang和LLVM的优势
- 新兴语言正在使用LLVM框架,例如Swift,Rust,Julia和Ruby。
- Clang和LLVM比GCC更严格地遵守C和C ++标准。在GCC升级期间,不会发生GNU内联和其他问题。
- Clang还支持一些扩展,例如线程安全检查的属性。
- Clang提供了其他有用的工具,例如用于静态分析的scan-build和clang静态分析器,用于语法分析的clang-format和clang-tidy以及编辑器插件Clangd。
- Clang提供更准确和友好的诊断信息,并突出显示错误消息,错误行,错误行提示和维修建议。Clang将诊断信息视为功能。诊断信息仅从GCC 5.0开始改进,并在GCC 8中变得成熟。