4

好的,首先,我不想在这里发生任何形式的火焰战争或类似的事情。我更大的问题更具理论性,并且将包含一些示例。

所以,正如我所写的,我无法理解解释语言怎么可能效率很低。由于它是现代的,我将以 Java 为例。

让我们回到没有 JIT 编译器的日子。Java有它的虚拟机,它基本上是它的硬件。您编写代码,而不是将其编译成字节码以至少从虚拟机中移除一些工作,这很好。但是考虑到 RISC 指令集在硬件中的复杂程度,我什至想不出在软件模拟硬件上的方法。

我没有编写虚拟机的经验,所以我不知道它是如何在最有效的水平上完成的,但我想不出比测试匹配和执行适当操作的每条指令更有效的方法。你知道的,比如:if(instruction=="something") { (do it) } else if(instruction=="something_diffrent"){ (do it) }等等......

但这必须非常缓慢。而且,即使有文章说 Java 在 JIT 编译器之前很慢,他们仍然说它并没有那么慢。但是为了模拟它必须花费真实硬件的许多时钟周期来执行一条字节码指令。

而且,甚至整个平台都是基于 java 的。例如,安卓。Android 的第一个版本没有 JIT 编译器。他们被解释了。但是不应该比Android慢的多吗?然而事实并非如此。我知道,当你从 Android 库中调用一些 API 函数时,它们是用机器代码编写的,所以它们很高效,所以这很有帮助。

但是想象一下,您将从头开始编写自己的游戏引擎,使用 API 来显示图像。您将需要执行许多数组复制操作,许多计算在模拟时会非常慢。

现在是我承诺的一些例子。由于我主要使用 MCU,因此我为 Atmel AVR MCU 找到了 JVM。Thay 表示 8MHZ MCU 每秒可以执行 20K java optcodes。但是由于 AVR 可以在一两个周期内完成大多数指令,因此平均可以说 6000000 条指令。这让我们知道,没有 JIT 编译器的 JVM 比机器代码慢 300 倍。那么为什么在没有 JIT 编译器的情况下让 java 如此流行呢?这不是太糟糕的性能损失吗?我只是无法理解。谢谢。

4

4 回答 4

3

我们已经使用字节码很长时间了。在旧的 Apple II 上,USCD p 系统非常流行,它将 Pascal 编译成字节码,由可能以 2 MHz 运行的 8 位 6502 解释。这些程序确实运行得相当快。

字节码解释器通常基于跳转表而不是if/then/else语句链。在 C 或 C++ 中,这将涉及switch语句。从根本上说,解释器相当于一个处理代码的数组,并使用字节码指令中的操作码作为数组的索引。

也有可能拥有比机器指令更高级别的字节码,因此一个字节码指令将转换为多个,有时是大量的机器码指令。为特定语言构建的字节码可以很容易地做到这一点,因为它只需要匹配该特定语言的控制和数据结构。这扩展了解释开销并使解释器更有效率。

与编译语言相比,解释语言可能会有一些速度损失,但这通常并不重要。许多程序以人类的速度处理输入和输出,这会留下大量可能被浪费的性能。即使是网络绑定程序也可能拥有比它需要的更多的可用 CPU 能力。有些程序可以利用它们所能获得的所有 CPU 效率,并且出于显而易见的原因,它们往往不是用解释语言编写的。

而且,当然,还有一个问题是你会因为效率低下而得到什么,这可能会或可能不会有所作为。解释的语言实现往往比编译的实现更容易移植,并且实际的字节码通常是可移植的。将更高级别的功能放入语言中会更容易。它允许编译步骤更短,这意味着执行可以更快地开始。如果出现问题,它可能允许更好的诊断。

于 2010-09-02T20:35:44.350 回答
0

12 MHz would be an ATtiny, which is an 8-bit microprocessor. That means (for example) that a native 'Add" instruction can only add two 8-bit numbers together to get a 9-bit result. The JVM is basically a virtual 32-bit processor. That means its add instruction adds two 32-bit numbers together to produce a 33-bit result.

As such, when you're comparing instruction rates, you should expect a 4:1 reduction in instruction rate as an absolute minimum. In reality, while it's easy to simulate a 32-bit add with 4 8-bit adds (with carries), some things don't scale quite like that. Just for example, according to Atmel's own app note, a 16x16 multiplication producing a 32-bit result executes in ~218 clock cycles. The same app note shows a 16/16 bit division (producing an 8-bit result) running in 255 cycles.

Assuming those scale linearly, we can expect 32-bit versions of the multiplication to take ~425-450 clock cycles, and the division ~510 cycles. In reality, we should probably expect a bit of overhead, which would reduce speed still more -- adding at least 10% to those estimates probably makes them more realistic.

Bottom line: when you compare apples to apples, it becomes apparent that a whole lot of the speed difference you're talking isn't real at all (or isn't attributable JVM overhead anyway).

于 2010-09-02T20:52:26.677 回答
0

但是不应该是Android非常慢吗?

定义“非常慢”。这是一部电话。在您拨第二位之前,它必须处理“拨第一位”。

在任何交互式应用程序中,限制因素始终是人的反应时间。它可能比用户慢 100 倍,但仍然比用户快。

所以,回答你的问题,是的,解释器很慢,但它们通常足够快,尤其是在硬件越来越快的情况下。

请记住,当 Java 被引入时,它是作为一种 Web 小程序语言出售的(取代并现在被 Javascript 取代——它也解释了)。只是在 JIT 编译之后才在服务器上流行起来。

通过使用跳转表,字节码解释器可以比 if() 行更快:

 void (*jmp_tbl)[256] = ...;  /* array of function pointers */
 byte op = *program_counter++;
 jmp_tbl[op]();
于 2010-09-02T20:26:50.940 回答
0

有两种不同的方法可以解决这个问题。

(i) “为什么运行慢代码可以”

正如 James 上面已经提到的那样,有时执行速度并不是您感兴趣的全部。对于许多在解释模式下运行的应用程序可能“足够快”。您必须考虑如何使用您编写的代码。

(ii) “为什么解释代码无效”

有很多方法可以实现解释器。在您的问题中,您谈到了最天真的方法:基本上是一个大开关,在读取每条 JVM 指令时对其进行解释。

但是您可以对其进行优化:例如,您可以查看它们的序列并寻找可以更有效地解释的模式,而不是查看单个 JVM 指令。Sun 的 JVM 实际上在解释器本身中做了一些优化。在之前的工作中,一个人花了一些时间以这种方式优化解释器,并且解释后的 Java 字节码在他的更改后运行得明显更快。

但是在包含 JIT 编译器的现代 JVM 中,解释器只是 JIT 完成工作之前的垫脚石,因此人们并没有真正花太多时间优化解释器。

于 2010-09-02T20:34:18.987 回答