为什么实际上解释器比编译器慢?
难道它们都必须在某个时刻转换为机器代码才能执行,还是我错过了一些更基本的东西?
编辑:
请考虑更复杂的解释方案,例如将代码转换为字节代码并且仅在源代码更改时重新生成字节代码的情况,例如Python的CPython实现? 我对逐行重新执行的古代解释器不太感兴趣......
谢谢!
Don't they both have to convert to machine code at some point to execute or am i missing something more basic?
EDIT:
Please consider more sophisticated interpreting schemes e.g. cases where the code is translated to Byte code and the byte code is only regenerated when source code changes e.g. CPython implementation of Python?
I am not really interested in ancient interpreters that re-execute line by line....
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
像 C 这样的编译语言通常直接编译成机器代码。当你运行代码时,它是由CPU直接执行的。
像 BASIC 或 PHP 这样的完全解释语言通常在每次运行时都会被解释。当您执行代码时,CPU 会执行解释器,解释器会读取并执行您的源代码。 (PHP 可以归类为完全解释型,因为虽然它确实使用操作码,但它们通常在执行后被丢弃。)
像 Python 这样的字节码解释语言,是从源代码编译为字节码 由虚拟机执行。 CPU运行VM,VM执行每条字节码指令。在Python中,字节码是在第一次执行代码时编译的。
在 Java 中,字节码在执行之前进行编译。 Java VM 还有一个特殊功能,称为即时编译。这意味着在执行过程中,它可能会将一些字节码编译为机器代码,然后将其发送到CPU直接执行。
总之,对于编译语言,CPU 直接运行代码。在解释型语言中,CPU 通常运行解释器或虚拟机。由于运行虚拟机或解释器的开销,这使得解释语言通常比编译语言慢。
注意:当我们谈论解释型和编译型语言时,我们真正讨论的是语言的通常执行风格。 PHP 可以被编译(使用 HipHop),C 可以被解释(使用 Parrot VM)。
A compiled language like C is usually compiled directly into machine code. When you run the code, it is executed directly by the CPU.
A fully interpreted language like BASIC or PHP is usually interpreted each time it runs. When you execute your code, the CPU executes the interpreter, and the interpreter reads and executes your source code. (PHP can be classified as fully interpreted, since while it does use opcodes, they are usually thrown away after the execution.)
A bytecode interpreted language like Python, is compiled from source code to bytecode that is executed by a virtual machine. The CPU runs the VM, and the VM executes each bytecode instruction. In Python, the bytecode is compiled the first time code is executed.
In Java, bytecode is compiled ahead of execution. The Java VM also has a special feature called Just-in-time compilation. This means that during execution, it may compile some of the bytecode to machine code, which it can send to the CPU to execute directly.
In conclusion, with compiled languages, the CPU runs the code directly. In interpreted languages, the CPU usually runs the interpreter or virtual machine. This makes interpreted languages generally slower than compiled languages, due to the overhead of running the VM or interpreter.
NOTE: While we speak of interpreted and compiled languages, what we are really discussing is the usual execution style of a language. PHP can be compiled (using HipHop), and C can be interpreted (using Parrot VM).
好吧,这里有很多不正确的帖子,是时候做一个长答案了。
编译器基本上是清晰的 - 它将程序从源语言翻译为目标语言。两种语言都可以是任何语言——高级语言、虚拟机字节码、机器代码。
另一方面,解释器不执行翻译,而是直接执行由源语言构造规定的操作,即解释它。
让我们考虑一下基于堆栈的机器中的假设
add
指令,该指令将堆栈的两个顶部元素相加并将结果推回。解释器将直接执行“添加两个顶部元素并将结果推回”,其方式类似于:如您所见,对于单个
add
insn,执行了许多操作:读取和写入内存、递增和递减堆栈指针、添加操作本身、开关的开销(如果解释器是这样实现的)、读取每个后续 insn 并决定的循环的开销如何处理等等。如果解释器在 AST 上工作,它可能看起来像:
再次,有很多很多 insn 来执行
add
语义所需的任何操作。OK, a lot of incorrect posts here, time for a long answer.
A compiler is basically clear - it translates a program form the source language to the target language. Both languages can be whatever - high level language, virtual machine bytecode, machine code.
An interpreter, on the other hand does not perform a translation, but directly performs the actions, prescribed by the source language construct, a.k.a. interprets it.
Let's consider a hypothetical
add
instruction in a stack based machine, which adds the two top elements of the stack and pushes the result back. An interpreter will directly perform that "add the two top elements and push the result back", in a manner similar to:As you can see, for a single
add
insn, there are many operations performed: reading and writing memory, incrementing and decrementing the stack pointer, the add operation itself, the overhead of the switch (if the interpreter is implemented that way), the overhead of the loop which reads each subsequent insn and decides how to process it, etc.If the interpeter worked on an AST, it may look like:
Again, many, many insns to perform whatever is required by the
add
semantics.在运行程序之前,编译器会将程序翻译为机器代码。它在编译时解析所有变量、类型。
解释器通常在每次执行语句时将每个语句翻译成机器代码。
A compiler translates a program to machine code before you run the program. It resolves all variables, types at compile time.
An interpreter typically translates each statement to machine code each time the statement is executed.
编译和解释之间的界限是模糊的。一般来说,某些形式的解释比直接编译的代码运行得慢。在具体情况下不一定是这样。
例如,一些解释器直接执行虚拟机指令(有时翻译成直接或间接线程代码),由于明显的原因,这比本机代码慢。
由于VM的语义(例如,它们中的大多数是基于堆栈的)和本机代码的语义之间存在阻抗不匹配,因此从一种到另一种的任何临时翻译都将不是最佳的。为了执行重量级优化,您需要一个虚拟机(或任何其他形式的中间表示),其中包含一定量的目标平台特定语义。 LLVM 是这种表示的一个很好的例子。
其他一些解释器甚至在抽象语法树级别上评估代码。有些正在执行字符串替换(一个臭名昭著的例子是 Tcl)。
所有这些技术都很容易实现,它们为语言语义提供了一些有趣的动态属性,但所有这些都是以执行速度变慢为代价的。
另一件值得一提的重要事情是超级编译。通过针对要执行的代码的特定实例专门化解释器实现,该技术实际上将任何解释器转变为编译器。这种方法的存在使得编译和解释之间的区别更加模糊。
A borderline between compilation and interpretation is blurred. In general, some forms of interpretation are working slower than a directly compiled code. It may not necessarily be true in specific cases.
For example, some interpreters are directly executing virtual machine instructions (sometimes translated into a direct or indirect threaded code), which is slower than a native code for obvious reasons.
Since there is an impedance mismatch between semantics of the VM (e.g., most of them are stack-based) and semantics of the native code, any ad hoc translation from one to another will be sub-optimal. In order to perform heavyweight optimisations you'll need a virtual machine (or any other form of an intermediate representation) with a certain amount of a target platform specific semantics in it. LLVM is a good example of such a representation.
Some other interpreters are even evaluating the code on an abstract syntax tree level. Some are performing string substitution (a notorious example is Tcl).
All that techniques are easy to implement, they provide some interesting dynamic properties to the language semantics, but all at a cost of a slower execution.
Another important thing to mention is a supercompilation. This technique practically turns any interpreter into a compiler, by specialising the interpreter implementation against a specific instance of a code to be executed. Existence of such approaches renders a difference between compilation and interpretation even more vague.
根据维基百科:
As per WikiPedia:
编译器执行一次(需要一些时间),然后代码运行得很快。
由于这可能需要相当长的时间,因此优化代码也可能花费相当多的时间。
当您想要运行代码时解释器会执行此操作,因此每次运行它时它都会进行编译。
The compiler does it once (which takes some time), then the code runs fast.
As it can take quite a while, it can spend quite some time optimizing the code too.
The interpreter does it when you want to run the code, so it compiles each time you run it.
解释器逐行执行,并在运行时将每一行转换为机器指令。而编译器在编译时将整个程序从源语言转换为目标语言(很可能是目标处理器的机器指令)。
Interpreter executes line by line and converts each line to machine instruction at run time. Whereas compiler converted entire program from source language to target language ( most probably machine instruction of the target processor ) in the compile time.