TG1*_*103 18 ruby python compiler-construction
为什么像Python和Ruby这样的语言被解释(开箱即用)而不是编译的技术原因是什么?在我看来,对于熟悉这一领域的人来说,不应该像现在这样解释这些语言,我们会看到显着的性能提升.所以我肯定错过了一些东西.
Dig*_*oss 32
几个原因:
想想如果不解释系统会发生什么.假设您使用translation-to-C作为机制.编译后的代码会定期检查它是否已被元编程取代.类型的情况出现了eval()类型的情况.在这些情况下,它必须再次运行编译器,这是一个极其缓慢的过程,或者无论如何它还必须在运行时使用解释器.
这里唯一的选择是JIT编译器.这些系统非常复杂和复杂,并且具有比所有其他替代方案更大的运行时间.它们启动速度非常慢,使得它们对于脚本编写起来不切实际.曾经见过Java脚本吗?我没有.
所以,你有两个选择:
通常,主要实现与第二种选择一致,这并不奇怪.有一天我们可能会看到像编译器这样的二次实现出现.Ruby 1.9和Python有字节码VM; 那些是½方式.编译器可能只针对非动态代码,或者它可能具有各种级别的语言支持,可以作为选项声明.但是,由于这样的事情不能成为主要的实施方式,因此它代表了许多工作,以获得非常小的利益.Ruby已经有200,000行C ...
我想我应该补充一点,总是可以添加一个编译的C(或者,通过一些努力,任何其他语言)扩展.所以,假设你有一个缓慢的数值运算.如果你添加,比如Array#newOp使用C实现,那么你得到加速,程序保留在Ruby(或其他),你的环境获得一个新的实例方法.大家都赢了!因此,这减少了对有问题的二次实现的需求.
Ale*_*lli 16
与Java或C#(在典型实现中)完全一样,Python首先被编译成某种形式的字节码,具体取决于实现(CPython使用自己的专用形式,Jython使用JVM就像典型的Java一样,IronPython只使用CLR就像一个典型的C#,等等) - 该字节码然后被进一步处理以供虚拟机(AKA解释器)执行,该虚拟机也可以"及时"生成机器代码 - 称为JIT - 如果有必要的话(CLR和JVM实现经常这样做,CPython自己的虚拟机通常不会这样做,例如使用psyco或Unladen Swallow).
JIT可以为足够长时间运行的程序(如果内存比CPU周期便宜)付出代价,但它可能不会(由于启动时间较慢和内存占用较大),特别是当类型也必须被推断或专门作为一部分时代码生成.如果你想要的话,生成没有类型推断或专业化的机器代码很容易,例如冻结为你做,但它确实没有提供"机器代码fetishists"属性的优点.例如,你得到的1.5至2 MB可执行的二进制代替一个微小的"Hello World"的.pyc-没有多大意义- !).该可执行文件是独立的,可以自行分发,但它只适用于非常特定的操作系统和CPU体系结构,因此在大多数情况下,这种权衡是非常不确定的.并且,准备可执行文件所花费的时间确实很长,因此将该操作模式设置为默认操作将是一个疯狂的选择.
仅仅用编译器替换解释器并不会像Python那样对您的语言提供如此大的性能提升.当大部分时间实际花费在字典中对对象成员进行符号查找时,如果对执行此类查找的函数的调用被解释,或者是本机机器代码并不重要 - 差异虽然不是可以忽略不计,但却相形见绌.通过查找开销.
要真正提高性能,您需要优化编译器.这里的优化技术与你使用C++,甚至Java JIT都有很大的不同 - 对于动态类型/鸭类型语言(如Python)的优化编译器需要做一些非常有创意的类型推断(包括概率 - 即"90%的几率"它是T"然后为那个案例生成有效的机器代码,前面有一个检查/分支)和逃逸分析.这很难.
我认为解释语言的最大原因是可移植性.作为程序员,您可以编写将在解释器而非特定操作系统中运行的代码.因此,您的程序在不同平台上的表现更加统一(比编译语言更多).我能想到的另一个优点是在解释语言中使用动态类型系统更容易.我认为语言的创造者正在考虑使用一种语言,程序员可以通过自动内存管理,动态类型系统和元编程获得更高效率,因为语言被解释会导致任何性能损失.如果您担心性能,可以使用JIT编译等技术将语言编译为本机代码.
今天,"编译"和"解释"语言之间不再存在强烈的区别.事实上,Python的编译与Java一样多,唯一的区别是:
Python甚至有一个函数叫做compile()编译器的接口.
听起来你所做的区别在于"动态类型"和"静态类型"语言.在Python等动态语言中,您可以编写如下代码:
def fn(x, y):
return x.foo(y)
Run Code Online (Sandbox Code Playgroud)
请注意,类型x和y未指定.在运行时,此函数将x查看是否具有名为的成员函数foo,如果是,则将其调用y.如果没有,它将抛出一个运行时错误,表明没有找到这样的函数.这种运行时查找使用像字节码这样的中间表示更容易表示,其中运行时VM执行查找而不必生成机器代码来执行查找本身(或者,调用函数来执行查找,这就是字节码无论如何都会这样做.
Python有像Psyco,PyPy和Unladen Swallow这样的项目,它们采用各种方法将Python对象代码编译成更接近本机代码的东西.在这方面有积极的研究,但还没有(现在)一个简单的答案.
创建一个好的编译器来为新语言生成本机代码所需的工作是惊人的.小型研究小组通常需要5至10年(例如:SML/NJ,Haskell,Clean,Cecil,lcc,Objective Caml,MLton等等).当有问题的语言需要在运行时进行类型检查和其他决策时,编译器编写者必须更加努力才能获得良好的本机代码性能(例如,请参阅Craig Chambers和后来的Urs Hoelzle的工作.自). 您可能希望的性能提升比您想象的更难实现.这种现象部分解释了为什么解释了如此多的动态类型语言.
如上所述,一个体面的解释器也可以立即移植,而将编译器移植到新的机器架构需要花费大量的精力(这是我个人已经工作了20多年的一个问题,有一段时间可以获得良好的行为).因此,口译员可以快速覆盖广大受众.
最后,虽然存在快速编译器和慢速解释器,但使用解释器通常更容易使编辑 - 转换 - 循环更快.(有关快速编译器的一些很好的例子,请参阅前面提到的lcc以及Ken Thompson的go编译器.有关相对较慢的解释器的示例,请参阅GHCi.
| 归档时间: |
|
| 查看次数: |
2442 次 |
| 最近记录: |