GCC与CLANG/LLVM:C/C++编译器的深度比较

2020-05-21 22:34:10

介绍了GNU编译器集合(GCC)和Clang/LLVM(低级虚拟机),比较了两种C/C++编译器的性能。

Visual C++、GNU编译器集合(GCC)和Clang/Low Level Virtual Machine(LLVM)是业界三大主流C/C++编译器。Visual C++提供图形用户界面(GUI),易于调试,但不适合Linux平台。因此,本文主要将GCC与Lang/LLVM进行比较。

GCC是GNU开发的程序语言编译器。它是根据GNU通用公共许可证(GPL)和GNU宽松通用公共许可证(LGPL)发布的一组自由软件。它是GNU和Linux系统的官方编译器,也是编译和创建其他UNIX操作系统的主要编译器。

LLVM包含一系列模块化的编译器组件和工具链。它可以在编译、运行时和空闲时间优化程序语言和链接,并生成代码。LLVM可以作为多语言编译器的后台。Clang是基于LLVM用C++编译的C、C++、Objective-C或Objective-C++编译器,在Apache 2.0许可下发布。铿锵主要是用来提供优于GCC的表演。

经过长期的开发和迭代,GCC、Cang、LLVM已经成为业内成熟的编译器。那么,哪种编译器更好呢?我们应该使用哪一个来编译和构建程序和系统?

现代处理器都具有超标量、长流水线和复杂的内部结构,它们支持复杂指令集计算机(CISC)或精简指令集计算机(RISC)体系结构中的向量扩展单元。对于许多包含通用计算密集型内核的程序,程序员可以使用向量扩展命令来大幅提高程序执行性能。例如,在矩阵和向量运算中,组合使用乘法和加法命令来提高性能和精度。位掩码命令用于矢量运算中的分支处理。然而,为了实现最高的性能,程序员和编译器仍然需要花费大量的精力来处理具有复杂的内存访问模式和非标准内核的任务。

此外,现代高级语言的标准不断抽象底层硬件和数据结构的细节,以生成更具逻辑性和数学性的通用代码,而不是特定的操作指令和存储器访问路径。C++标准越来越具有表现力和抽象性。Python很受欢迎,因为它更具可读性和表现力,即使是以较低的运行速度为代价。更高的表现力增加了编译器从程序员编译的复杂结构生成良好汇编代码的负担。编译器必须更聪明,更努力地工作,通过使用代码来最大化性能。并不是所有的编译器都能做到这一点。在选择编译器时,我们必须首先考虑相同的代码段是否可以生成更高效的汇编命令。

除了生成高性能的可执行程序外,现代编译器本身也必须具有高性能。一个用C++编写的大型软件项目可能包含数百到数千个单独的翻译单元。每个翻译单元可以包含数千行代码。C++代码还可以使用大量基于模板的编程技术。这些技术需要编译器多次传输相关信息才能生成目标文件。编译大型C++项目可能需要几个小时,并且在开发期间必须同时提交多个相互依赖的更改。每次提交都需要开发人员重新编译大多数代码库。因此,更快的编译器(构建工具)对于实现大型团队的高生产率至关重要。

在语言扩展方面,具有多个内核、向量处理能力和加速器的现代计算系统提供了优于普通编程语言的自然功能。因此,特定的高性能计算(HPC)框架,如OpenMP和openacc,可以填补这一空白。这些框架提供了应用程序接口(API),程序员可以使用这些API来表达代码中的并行性。编译器和相应的运行时库必须将并行代码映射到处理器体系结构。许多HPC项目依赖于OpenMP和Openacc标准,这些标准正在由开发人员和硬件制造商进行扩展。因此,编译器必须跟上语言扩展标准的发展。

总而言之,一个好的编译器可以让我们专注于编程过程,而不是与其缺点作斗争。它可以支持最新的语言标准,从最抽象的代码生成优化的命令,并在更短的时间内编译源代码。

在学习GCC之前,你需要先了解一下GNU项目。理查德·斯托尔曼在1984年发起了GNU项目,目的是构建一个类似UNIX的开源软件系统。GNU操作系统并没有随着时间的推移而广泛发展。然而,它孵化了许多优秀和有用的开源软件工具,如Make、sed、Emacs、Glibc、gdb和GCC。这些GNU开源软件和Linux内核共同构成了GNU/Linux系统。最初,GCC为GNU系统提供了稳定可靠的基于C语言的编译器。它的全称是GNU C编译器。后来,支持更多语言(如FORTRAN、OBJ-C和ADA),GCC的全名改为GNU编译器集合。

GCC-1由理查德·斯托尔曼于1987年发布,距今已有30多年的历史。从软件的角度来看,它是非常古老的。有人收集了1989年至2012年GCC的开发记录,制作了一部30分钟的动画视频(GNU编译器收藏集开发历史1989-2012年),直观地展示了GCC的发展过程。我们可以从GCC的版本中了解它的发展历史:

GCC:1992年发布,支持C++。后来,GCC社区出现分裂,因为理查德·斯托尔曼将GCC定义为GNU系统可靠的C编译器,认为GCC当时对GNU系统来说已经足够了,应该把开发重点从GCC转移到GNU系统本身。其他主要开发商希望继续完善GCC,在各个方面做出更激进的发展和改进。这些活跃的开发人员于1997年离开GCC社区,开发了EGCS分支。

GCC:显然,开发人员普遍对优秀的编译器有着强烈的愿望。EGCS分支开发得很顺利,得到了越来越多的开发人员的认可。最终,EGCS被用作新的GCC主干,GCC-3.0于2001年发布。分裂的社区再次合并,但理查德·斯托尔曼的影响力在一定程度上受到了削弱。另外,GCC工委已经开始决定GCC的发展方向。

GCC:2005年发布。这个版本被集成到树形串行存储体系结构中,GCC发展成为一名现代编译器。

GCC:2015年上映。后来,GCC的版本政策进行了调整,每年都会发布一个主要版本。一个意想不到的好处是版本号与年份相对应。比如2017年上映的《GCC-7》,2019年上映的《GCC-9》。

如今,GCC的发展已进入“现代编年史”。面对LLVM的竞争压力,GCC社区积极做了很多调整,比如加快编译、完善编译警示信息等。在过去的30年里,GCC从编译器行业的挑战者演变为linux系统的主流编译器,现在面临着llvm的挑战。幸运的是,GCC社区正在进行调整,加速GCC的发展。我们可以预期,这两种编译技术之间的竞争将继续为软件开发人员提供更好的编译器。

LLVM起源于2000年Chris Lattner对UUIC的研究。Chris Lattner希望为所有静态和动态语言创建一种动态编译技术。LLVM是在BSD许可下开发的一种开源软件。最初的版本1.0是在2003年发布的。2005年,苹果公司(Apple Inc.)。聘请克里斯·拉特纳(Chris Lattner)和他的团队为苹果电脑开发编程语言和编译器,此后LLVM的开发进入快车道。从LLVM2.5开始,每年都会发布两个次要的LLVM版本(通常在3月和9月)。2011年11月,LLVM3.0发布,成为默认的XCode编译器。默认情况下,Xcode5开始使用Clang和LLVM5.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已经成为MacOSX、iOS、FreeBSD和Android系统的默认编译器。

Clang的设计目的是提供一个可以取代GCC的前端编译器。苹果公司。(包括后面)一直使用GCC作为官方编译器。作为开源社区的标准编译器,GCC一直表现出色。然而,苹果公司(Apple Inc.)。对编译工具有自己的要求。一方面,苹果公司。为Objective-C语言(甚至后来的C语言)添加了许多新功能。然而,GCC开发人员并不接受这些功能,并对这些功能的支持给予了较低的优先级。后来,它们被简单地分成两个分支进行单独开发,因此苹果公司发布了GCC版本。比官方版本要早得多。另一方面,GCC代码是高度耦合的,很难单独开发。此外,在以后的版本中,代码质量继续下降。然而,苹果公司要求的许多功能。(如改进的集成开发环境支持)必须将GCC作为模块调用,但GCC从未提供过这样的支持。此外,GCC运行时库豁免从根本上限制了LLVM GCC的发展。同样受到许可证限制的是,苹果公司(Apple Inc.)。基于GCC的代码生成质量不能用低成本虚拟机来进一步提升。因此,苹果公司。决定从头开始编写C,C++和Objective-C语言的前端代码,完全取代GCC。

顾名思义,Clang只支持C、C++和Objective-C。开发始于2007年,第一次完成了C编译器。Cang for Objective-C云将在2009年全面用于生产环境。对C++的支持也进展很快。Clang 3.3完全支持C++11,Clang 3.4完全支持C++14,Clang 5完全支持C++17,都明显领先于当时的GCC。

和其他开源软件社区一样,GCC社区由自由软件爱好者和黑客主导。在发展过程中,GCC社区管理和参与机制在今天逐步形成。目前,GCC社区是一个相对稳定、界定明确的熟人社会,每个人都有明确的角色和职责:

理查德·斯托尔曼与自由软件基金会:虽然很少参与GCC公关

GCC工委:管理GCC社区事务,管理技术独立的GCC开发课题,管理审查员、维护员的聘任和公告。它目前有13名成员。

全球维护者:他们主导着GCC的发展活动。他们在一定程度上决定了GCC的发展走向。目前,全球有13名维护员,他们并不都在GCC工委任职。

前端、中间端和后端维护人员:他们是前端、后端和其他模块的维护人员。他们负责对应的GCC模块的代码,其中很多人是模块代码的主要贡献者。值得注意的是,评审员一般都属于这一类。不同的是,审核者不能批准他们自己的补丁,而维护人员可以在他们的职责范围内提交自己的修改,而不需要审查者的批准。

贡献者:他们是GCC社区中最广泛的开发群体。在签署版权协议后,任何开发者都可以向社区申请审批后编写权限,然后自行提交代码。

和其他开源社区一样,成熟的GCC社区不再由黑客主导。商业公司开始在社区中扮演重要角色,例如招募开发人员和赞助开发会议。目前,GCC社区主要由以下类型的商业公司主导:

基于Ada语言的专业供应商,如CodeSourcery和工具链服务提供商,如AdaCore。CodeSourcery有着辉煌的历史,招募了许多著名的开发人员,但在被Mentor收购后拒绝了。

在目前的GCC社区,芯片厂商主导后端开发,系统厂商引导其他开发领域。在社区开发方面,GCC代码目前托管在它自己的svn服务器上。为了方便开发和提交,我们提供了Git API。补丁审查类似于Linux内核社区中的补丁审查,并使用邮件列表形式。如上所述,GCC社区是一个相对稳定(或封闭)的熟人社会。社区每年基本上有150到200名活跃贡献者,每年9月都会召开开发者大会。2019年9月,开发者大会将在加拿大蒙特利尔举行。

LLVM社区是一个对noob友好的编译器社区。它可以快速响应新用户的问题和补丁审查。这也是随后LLVM基金会讨论和通过LLVM社区行为准则的基础和来源,并引发了一系列政治上正确的讨论。

所有LLVM项目和问题都通过DevExpress电子邮件列表进行讨论,并通过提交电子邮件列表通知代码提交。所有错误和功能修改都会通过错误列表进行跟踪。建议将提交的补丁程序用于主分支。该样式符合LLVM编码标准,并通过Phabricator执行代码审查。目前,LLVM代码库已经迁移到GitHub。

与GCC社区不同,LLVM社区只有LLVM基金会。LLVM基金会有八名成员。除了管理LLVM社区事务之外,LLVM基金会的每个成员都必须指导与技术相关的LLVM开发问题。目前,总统是克里斯·拉特纳(Chris Lattner)的妻子坦尼娅·拉特纳(Tanya Lattner)。Chris Lattner本人也是该基金会的成员,对LLVM社区和LLVM的发展方向拥有强大的控制权。

LLVM社区的代码审查政策与GCC社区的基本一致。不同的是,由于LLVM的快速发展,许多贡献者没有提交访问权限,必须通过维护者提交代码。目前,Clang和LLVM社区每年有1000多名贡献者。一般来说,开发者大会在每年的4月和10月举行。2019年10月的开发者大会,将在美国圣何塞举行。

LLVM许可从UIUC许可更改为Apache 2.0许可,但LLVM例外。主要用于解决LLVM运行库基于MIT License,项目所需专利授权过多的问题。在此许可下,LLVM允许任何人不受任何限制地从LLVM派生商业产品,并且不要求任何衍生品提供开源,从而促进LLVM的广泛使用,包括:

出于个人、内部或商业目的全部或部分下载或使用LLVM。修改LLVM代码而不将其贡献回项目的能力。

创建包含LLVM的包或发布版本。LLVM与所有其他主要开源许可证(包括BSD、MIT、GPLv2和GPLv3)授权的代码的关联。

再次分发LLVM时,您必须保留版权声明。您不能删除或替换版权标头。包含LLVM的二进制文件必须包含版权声明。

体系结构:x86_64处理器:英特尔(R)至强(R)白金(R)8163 [email protected] GHz一级数据高速缓存:32 KB二级高速缓存:1,024 KB三级高速缓存:33,792KB内存:800 GB操作系统:阿里巴巴集团Enterprise Linux Server版本7.2(Paladin)内核:4.9.151-015.ali3000.alios7.x86_64编译器:CLANG/LVM 8.0 GCC8.3.1

SPEC CPU 2017是一套CPU子系统测试工具,用于测试CPU、高速缓存、内存和编译器。包含测试整数速度和浮点运算速度的SPECSpeed 2017 int和FP以及测试整数并行率和浮点并行率的SPECrate 2017 int和fp四类43个测试。Clang不支持Fortran语言。因此,在本例中,使用SPEC Speed测试集中的C/C++程序来测试Clang和GCC生成的二进制程序之间的单核性能差异。下表列出了SPEC CPU2017 C和C++集:

使用LLVM-LNT自动化框架进行测试和性能比较。它的运行方式与SPEC CPU的runcpu相同。在LLVM-LNT运行之前,清除缓存(echo 3>;/proc/sys/vm/drop_caches),然后运行测试数据集。接下来,REF数据集运行三次。使用三个REF测试运行结果的平均值作为最终结果。为了减少CPU迁移或上下文切换引起的性能波动,通过使用CPU亲和性工具将运行在测试数据集和REF数据集上的进程绑定到CPU核心。对于编译时测试,此方法使用线程1构建测试程序,并比较编译了很长时间的测试项。编译时间不包括链接器执行时间。它只包括所有测试程序中所有源文件生成的时间。

GCC的编译过程是:读取源文件,对源文件进行预处理,转换成IR,优化生成汇编文件。然后汇编器生成一个目标文件。Clang和LLVM不依赖独立的编译器,而是在后端集成自实现的编译器。在生成目标文件的过程中省略了生成部件文件的过程。生成目标文件。

..