JIT与非JIT编译器都最终产生机器码吗?

7

好的,我已经阅读了关于JIT和非JIT启用解释器之间差异以及为什么JIT通常提高性能的几篇讨论。

然而,我的问题是:

最终,非JIT启用的解释器难道不必将字节码(逐行)转换为机器/本地代码以执行,就像JIT编译器所做的那样吗?我看过一些帖子和教科书,它们说是这样的,也有一些帖子说不是。后者的观点是,解释器/JVM直接执行这个字节码,没有与机器/本地代码交互。

如果非JIT解释器确实将每行都转换为机器代码,那么JIT的主要优点似乎是...

  1. 缓存所有(普通JIT)或经常遇到的(热点/自适应优化)字节码部分的智能,以便不需要每次都进行机器代码编译步骤。

  2. JIT编译器在将字节码转换为机器代码时可以执行的任何优化。

这是否准确?除了可能的优化或块与逐行JIT之外,在通过非JIT和JIT启用的解释器将字节码转换为机器代码方面似乎没有什么区别。

提前致谢。

2个回答

10

非JIT解释器不会将字节码转换为机器码。你可以将非JIT字节码解释器的工作想象成这样(我将使用类似Java的伪代码):

int[] bytecodes = { ... };
int   ip        = 0; // instruction pointer
while(true) {
  int code = bytecodes[ip];
  switch(code) {
    case 0;
      // do something
      ip += 1; break;
    case 1:
      // do something else
      ip += 1; break;
    // and so on...
  }
}

因此,对于每个执行的字节码,解释器都必须检索代码,根据其值进行切换以决定要执行什么操作,并在继续下一次迭代之前增加其“指令指针”。

使用JIT,所有这些开销都将被消除。它只需要获取适当的 switch 分支(那些说 "// do something" 的部分),将它们在内存中串联起来,并跳转到第一个分支的开头执行。不需要软件“指令指针”,只需要CPU的硬件指令指针。也不需要从内存中检索字节码并根据它们的值进行切换。

编写虚拟机并不困难(如果它不需要极高的性能),而且可能是一项有趣的练习。我曾经为一个嵌入式项目编写过一个虚拟机,其中程序代码必须非常紧凑。


谢谢您的回复!我正在阅读《C++游戏和图形入门》一书,它提到:“解释器将每个高级指令转换为其等效的机器语言指令,并立即执行它。”这就是混淆开始的地方。然而,它是在讨论解释器的一般情况。 - B Mac
解释器通过跳转到实现高级命令所代表操作的机器语言部分,将高级命令“翻译”成机器语言。 - Alex D

0
几十年前,似乎普遍认为编译器会将整个程序转换成机器代码,而解释器则会将语句翻译成机器代码,执行它,丢弃它,然后翻译下一个语句等等。这种观念99%是错误的,但其中确实有两个微小的真相。在一些微处理器上,一些指令需要使用在代码中指定的地址。例如,在8080上,有一条指令可以读取或写入指定的I/O地址0x00-0xFF,但没有指令可以读取或写入寄存器中指定的I/O地址。如果用户代码执行了类似“out 123,45”的操作,语言解释器通常会将指令“out 7Bh/ret”存储到三个字节的内存中,将累加器加载为2Dh,并调用这些指令中的第一个。在这种情况下,解释器确实会生成一个机器码指令来执行解释的指令。然而,这种代码生成大多局限于IN和OUT指令之类的操作。
许多常见的针对6502(以及可能也适用于8080)的Microsoft BASIC解释器在RAM中存储了一些代码,但是存储在RAM中的代码并不显著依赖于正在执行的程序;大多数RAM例程在程序执行期间不会改变,但下一条指令的地址将作为例程的一部分内联保留,从而允许使用绝对模式“LDA”指令,这至少可以节省每个字节提取的一个周期。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接