17

我刚刚和一位同事进行了一次对话,在哪里谈论 V8 JavaScript 引擎。根据维基百科,

V8 在执行之前将 JavaScript 编译为本机机器码 [...],而不是更传统的技术,例如解释字节码或将整个程序编译为机器码并从文件系统执行。

其中(如果我错了,请纠正我)“解释字节码”是 Java 的工作方式,而“编译整个程序”将适用于 C 或 C++ 等语言。现在我们想知道、辩论和提出关于差异、相似性的错误断言和假设。为了结束这一点,我建议向专家咨询 SO。

那么,谁能

  1. 命名、解释和/或引用所有主要方法(例如预编译与运行时解释)
  2. 可视化或提供关于源、编译和解释之间关系的方案
  3. 为#1的主要方法举例(命名编程语言)。

笔记:

  • 我不是在寻找关于不同范式的冗长平淡的文章,而是在寻找有视觉支持的快速概述。
  • 我知道 Stackoverflow 并非旨在成为程序员的百科全书(而是针对更具体问题的问答平台)。但是由于我可以找到很多流行的问题,那种为某些主题(例如[1][2][3][4][5])提供了百科全书式的观点,我开始了这个问题。
  • 如果这个问题更适合任何其他 StackExchange 站点(例如cstheory),请让我知道或标记这个问题以进行审核。
4

2 回答 2

17

由于一个简单的原因,几乎不可能回答您的问题:没有几种方法,它们是一个连续体。这个连续体中涉及的实际代码也相当相同,唯一的区别是事情发生的时间,以及是否以某种方式保存了中间步骤。这个连续体中的各个点(不是一条线,一个级数,而是更多的具有不同角的矩形,您可以靠近这些点)是:

  1. 阅读代码
  2. 理解代码
  3. 执行你所理解的
  4. 沿途缓存各种中间数据,甚至将它们持久保存到磁盘。

例如,一种纯解释型编程语言几乎不会做 #4 和 #2 有点隐式发生在 1 和 3 之间,所以你几乎不会注意到它。它只是读取代码的一部分,并立即对它们做出反应。这意味着实际开始执行的开销很小,但是例如在循环中,相同的文本行被读取并再次重新读取。

解释器的平衡图(没有太多的缓存)

在矩形的另一个角落,有传统的编译语言,通常,第 4 项包括将实际机器代码永久保存到文件中,然后可以在以后运行。这意味着您在开始时要等待相当长的时间,直到整个程序被翻译(即使您只在其中调用一个函数),但 OTOH 循环更快,因为不需要再次读取源代码。

编译器的平衡图(主要是缓存)

然后是介于两者之间的东西,例如虚拟机:为了可移植性,许多编程语言不会编译为实际的机器代码,而是编译为字节码。然后有一个编译器生成字节码,还有一个解释器获取这个字节码并实际运行它(有效地“将其转换为机器码”)。虽然这通常比编译并直接转为机器代码要慢,但将这种语言移植到另一个平台更容易,因为您只需移植通常用高级语言编写的字节码解释器,这意味着您可以使用现有的编译器来执行这种“有效地转换为机器代码”,并且不必为您要运行的每个平台制作和维护后端。还有,这可以如果您可以执行一次字节码的编译,然后只分发编译的字节码,那么速度会更快,这样其他人就不必花费 CPU 周期来例如在您的代码上运行优化器,而只需支付字节码到本机的费用翻译,在您的用例中可能可以忽略不计。此外,您没有分发源代码。

介于两者之间的另一件事是即时编译器(JIT),它实际上是一个解释器,它以编译的形式保存它曾经运行过的代码。这种“保留”使其比纯解释器慢(例如,增加的开销和 RAM 使用导致交换和磁盘访问),但在重复执行一段代码时使其更快。它也可以比纯代码编译器更快,例如只重复调用一个函数,因为如果不使用它,它不会浪费时间编译程序的其余部分。

最后,您可以在此矩形上找到其他位置,例如通过不永久保存已编译代码,而是再次从缓存中清除已编译代码。通过这种方式,您可以例如节省嵌入式系统上的磁盘空间或 RAM,但代价是可能不得不再次编译一段很少使用的代码。许多 JIT 编译器都这样做。

于 2014-06-19T13:52:46.547 回答
3

现在许多执行环境使用字节码(或类似的东西)作为代码的中间表示。因此,源代码首先被编译成中间语言,然后由虚拟机解释(解码字节码指令集)或进一步编译成机器代码,并由硬件执行。

很少有生产语言在没有被预编译成某种中间形式的情况下被解释。但是,很容易将这样的解释器概念化:只需考虑一个类层次结构,其中包含每种语言元素(if语句、for等)的子类,并且每个类都有一个Evaluate评估给定节点的方法。这也通常称为解释器设计模式

例如,考虑以下代码片段if在假设的解释器中实现语句(在 C# 中实现):

class IfStatement : AstNode {
    private readonly AstNode condition, truePart, falsePart;

    public IfStatement(AstNode condition, AstNode truePart, AstNode falsePart) {
        this.condition = condition;
        this.truePart = truePart;
        this.falsePart = falsePart;
    }

    public override Value Evaluate(EvaluationContext context) {
        bool yes = condition.Evaluate(context).IsTrue();
        if (yes)
            truePart.Evaluate(context);
        else
            falsePart.Evaluate(context);
        return Value.None; // `if` statements have no value.
    }
}

这是一个非常简单但功能齐全的解释器。

于 2014-06-19T12:47:58.630 回答