Answers:
编译器的实现与编译器的输出之间没有必要的关系。你可以写在像Python或Ruby,其最常见的实现是很慢的一个语言的编译器,而编译器可以输出高度优化的能够跑赢C.编译器本身就需要很长时间来运行的机器代码,因为它的代码以慢速语言编写。(更准确地说,是用慢速执行的语言编写的。正如拉斐尔在评论中指出的那样,语言并不是天生就快或慢。我在下面对此进行扩展。)编译后的程序将与其运行速度一样快。允许自己的实现-我们可以用Python编写一个编译器,该编译器生成与Fortran编译器相同的机器代码,并且即使编译时间很长,我们的编译程序也将与Fortran一样快。
如果我们谈论的是口译员,那就另当别论了。解释程序必须在解释程序运行时运行,因此在实现解释程序的语言与解释代码的性能之间存在联系。要使解释后的语言运行得比实现解释器时所用的语言快,就需要进行一些巧妙的运行时优化,最终的性能取决于代码对这种优化的满意程度。许多语言(例如Java和C#)将运行时与混合模型一起使用,该模型将解释器的某些优点与编译器的某些优点结合在一起。
作为一个具体的例子,让我们更仔细地看一下Python。Python有几种实现。最常见的是CPython,它是用C编写的字节码解释器。还有PyPy,它是用称为RPython的Python专用方言编写的,它使用类似于JVM的混合编译模型。在大多数基准测试中,PyPy比CPython快得多。它使用各种惊人的技巧在运行时优化代码。但是,PyPy运行的Python语言与CPython运行的Python语言完全相同,除非有一些不影响性能的差异。
假设我们用Python语言为Fortran编写了一个编译器。我们的编译器产生与GFortran相同的机器代码。现在,我们编译一个Fortran程序。我们可以在CPython之上运行编译器,也可以在PyPy上运行,因为它是用Python编写的,并且这两种实现都运行相同的Python语言。我们会发现,如果我们在CPython上运行编译器,然后在PyPy上运行,然后使用GFortran编译相同的Fortran源代码,我们将获得完全相同的机器代码,因此,编译过的程序将始终运行以大约相同的速度。但是,生成该编译程序所需的时间将有所不同。CPython可能比PyPy花费更长的时间,而PyPy可能比GFortran花费更长的时间,即使它们最后都将输出相同的机器代码。
通过扫描Julia网站的基准测试表,看来解释器上运行的所有语言(Python,R,Matlab / Octave,Javascript)都没有比C更好的基准测试。这通常与我所期望的一致,尽管我可以想象用Python的高度优化的Numpy库(用C和Fortran编写)编写的代码会击败类似代码的某些C实现。正在编译等于或优于C的语言(Fortran,Julia)或使用带有部分编译的混合模型(Java,可能还有LuaJIT)。PyPy还使用了混合模型,因此如果我们在PyPy而非CPython上运行相同的Python代码,则完全有可能在某些基准测试中击败C。
一个男人制造的机器怎么能比一个男人强大呢?这是完全相同的问题。
答案是编译器的输出取决于该编译器实现的算法,而不取决于实现该编译器的语言。您可以编写一个速度非常慢,效率低下的编译器,该编译器会产生非常有效的代码。编译器没有什么特别的:它只是一个需要一些输入并产生一些输出的程序。
我想反对一个普遍的假设,我认为这是错误的,以至于在选择工作工具时有害。
没有慢速或快速语言之类的东西。¹
在我们实际去做CPU的路上,有很多步骤²。
每个项目都会对您可以衡量的实际运行时间有所贡献,有时甚至很大。不同的“语言”侧重于不同的事物³。
仅举一些例子。
1比2-4:就正确性和效率而言,普通C程序员产生的代码可能比普通Java程序员差得多。那是因为程序员在C中承担更多责任。
1/4 vs 7:在像C这样的低级语言中,您可能可以作为程序员利用某些CPU功能。在高级语言中,只有编译器/解释器可以知道目标CPU 才能这样做。
1/4 vs 5:您是否想要或必须控制内存布局以最好地使用手头的内存架构?有些语言可以控制您,有些则不能。
2/4 vs 3:解释的Python本身非常慢,但是流行绑定到高度优化的,本地编译的科学计算库。因此,如果大多数工作是由这些库完成的,那么最终使用Python进行某些操作很快。
2 vs 4:标准的Ruby解释器很慢。另一方面,JRuby可能非常快。使用另一种编译器/解释器,这就是相同的语言。
1/2 vs 4:使用编译器优化,可以将简单的代码转换为非常有效的机器代码。
最重要的是,您发现的基准没有多大意义,至少在归结到您所包含的表时至少没有意义。即使您只对运行时间感兴趣,也需要指定从程序员到CPU 的整个链。换掉任何元素都可以极大地改变结果。
明确地说,这回答了问题,因为它表明编写编译器(第4步)所用的语言只是一个难题,并且可能根本不相关(请参阅其他答案)。
我故意不在这里讨论不同的成功指标:运行时间效率,内存效率,开发人员时间,安全性,安全性(可证明的)正确性,工具支持,平台独立性,...
即使将语言设计用于完全不同的目标,也只能将它们进行比较,这是一个巨大的谬误。
关于优化,这里有一件被遗忘的事情。
关于fortran优于C的争论由来已久。将格式错误的争论放在一起:用C和fortran编写相同的代码(正如测试人员认为的那样),并根据相同的数据测试性能。问题是,这些语言不同,C允许指针别名,而fortran不允许。
因此代码并不相同,在经过C测试的文件中没有__restrict会产生差异,在重写文件以告知编译器它可以优化指针之后,运行时变得相似。
这里的要点是,某些优化技术在新创建的语言中更容易(或开始合法)。
其次,VM可以在运行时执行压力测试,因此它可以提取压力代码并对其进行优化,甚至可以在运行时对其进行预先计算。预先编译的C程序不会期望压力很大,或者(大多数时间)不会为通用机器系列提供可执行文件的通用版本。
在此测试中,还有JS,而且VM的速度比V8快,并且在某些测试中它的性能也比C快。
我已经检查过了,在C编译器中还没有独特的优化技术。
C编译器必须立即对整个代码进行静态分析,然后再使用给定的平台并解决内存对齐问题。
VM只是将部分代码音译为优化的程序集并运行它。
关于Julia-我检查了它是否在AST的代码上运行,例如,GCC跳过了这一步,最近才开始从那里获取一些信息。这加上其他限制和VM技术可能会有所解释。
例子:让我们做一个简单的循环,它从变量开始到终点,并将部分变量加载到运行时已知的计算中。
C编译器从寄存器生成加载变量。
但是在运行时,这些变量是已知的,并且在执行过程中被视为常量。
因此,与其从寄存器中加载变量(并且不执行缓存,因为它可能会更改,而且从静态分析中尚不清楚),不如将它们完全视为常量并进行折叠,传播一样。
前面的答案几乎都是从实用角度出发进行解释的,尽管这个问题是有意义的,但拉斐尔的答案很好地解释了这个问题。
除了这个答案外,我们还应该指出,当今,C编译器是用C编写的。当然,正如Raphael所指出的那样,它们的输出及其性能可能尤其取决于它所运行的CPU。但这也取决于编译器完成的优化量。如果您使用C编写了一个针对C的更好的优化编译器(然后您可以使用旧的编译器运行该编译器),那么您将获得一个新的编译器,使C成为比以前更快的语言。那么,C的速度是多少? 请注意,您甚至可以自己编译新的编译器,作为第二遍,这样,尽管仍提供相同的目标代码,但编译效率更高。而充分就业定理表明,他们是没有止境的这样的改进(感谢拉斐尔的指针)。
但是,我认为将这个问题正式化可能是值得的,因为它很好地说明了一些基本概念,尤其是事物的指称性与操作性观点。
优化参数后,我们可能希望编译器具有良好的效率,以便可以在合理的时间内执行转换。因此,编译器程序的性能对用户很重要,但对语义没有影响。我说的是性能,因为某些编译器的理论复杂性可能比人们预期的要高得多。
这将说明区别,并显示出实际应用。
根据Blum的加速定理,有些程序是在最快的计算机/编译器组合上编写和运行的,其运行速度要比第一台运行BASIC的PC上运行的程序慢。只是没有一种“最快的语言”。您可以说的是,如果您用多种语言编写相同的算法(实现;如前所述,周围有很多不同的C编译器,我什至遇到过一个功能强大的C解释器),那么它在每种语言中的运行速度会变慢或变慢。
不可能有一个“总是较慢”的层次结构。这是每个人都精通几种语言的现象:每种编程语言都是为特定类型的应用程序设计的,并且更常用的实现已针对该类型的程序进行了优化。我非常确定,例如,一个用Perl编写的字符串鬼混的程序可能会击败用C编写的相同算法,而用C对大型整数数组进行压缩的程序将比Perl更快。
让我们回到原始行:“用C编写编译器的语言怎么会比C更快?” 我认为这真的是要说:用Julia编写的程序(用C编写的核心)比用C编写的程序还要快?具体来说,用Julia编写的“ mandel”程序如何在用C编写的等效“ mandel”程序的87%的运行时间中运行?
到目前为止,Babou的论文是对该问题的唯一正确答案。到目前为止,所有其他答复或多或少都在回答其他问题。babou的文章的问题在于,对“什么是编译器”进行了多段篇幅的理论描述,其用语是原始海报可能难以理解。掌握“语义”,“象征性”,“实现”,“可计算”等词语所指概念的任何人都已经知道该问题的答案。
较简单的答案是C代码或Julia代码均不能直接由计算机执行。两者都必须进行翻译,并且该翻译过程引入了许多方法,可以使可执行的机器代码变慢或变快,但仍产生相同的最终结果。C和Julia都进行编译,这意味着将一系列翻译转换为另一种形式。通常,人类可读的文本文件会转换为某种内部表示形式,然后作为计算机可以直接理解的一系列指令写出。对于某些语言,它还不止于此,而且Julia是其中之一-它具有“ JIT”编译器,这意味着整个翻译过程不必整个过程都一次完成。但是,任何语言的最终结果都是不需要进一步翻译的机器代码,可以直接发送给CPU的代码以使其执行某些操作。最后,这就是“计算”,有多种方法可以告诉CPU如何获得所需的答案。
可以想象一种既有“加”运算符又有“乘”运算符的编程语言,以及只有“加”运算符的另一种语言。如果您的计算需要乘法,那么一种语言将会“慢”,因为CPU当然可以直接执行这两种操作,但是如果您没有任何办法表示需要乘以5 * 5,则只需写“ 5”即可。 + 5 + 5 + 5 + 5“。后者将花费更多时间来获得相同的答案。大概,朱莉娅(Julia)正在进行其中的一些事情。也许该语言允许程序员以无法直接用C语言表达的方式陈述计算Mandelbrot集的预期目标。
用于基准测试的处理器被列为Xeon E7-8850 2.00GHz CPU。C基准测试使用gcc 4.8.2编译器为该CPU生成指令,而Julia使用LLVM编译器框架。gcc的后端(为特定CPU架构生成机器代码的部分)在某种程度上可能不如LLVM后端先进。这可能会影响性能。还有许多其他事情在发生-编译器可以通过以不同于程序员指定的顺序发布指令来“优化”,或者如果它可以分析代码并确定它们不是,则根本不做任何事情。需要获得正确答案。而且程序员可能以使其变慢的方式编写了C程序的一部分,但并没有
所有这些都是说的方式:有很多方法可以编写用于计算Mandelbrot集的机器代码,并且所使用的语言对如何编写机器代码具有重要影响。您对编译,指令集,缓存等了解得越多,获得所需结果的能力就越强。从Julia所引用的基准测试结果中得出的主要结论是,没有一种语言或工具可以胜任所有工作。实际上,整个图表中最好的速度因素是Java!
用X语言编写的代码(其编译器是用C编写的)可以胜过用C语言编写的代码,但前提是,与语言X相比,C编译器的优化效果较差。如果我们不进行优化讨论,那么X的编译器能否产生更好的性能目标代码要比C编译器生成的目标代码好,那么用X编写的代码也可能赢得比赛。
但是,如果语言X是一种解释性语言,并且解释器是用C编写的,并且我们假设语言X的解释器和用C编写的代码是由同一C编译器编译的,那么用X编写的代码绝不会胜过代码如果两个实现都遵循相同的算法并使用等效的数据结构,则用C语言编写。