JIT编译器与传统编译器相比有哪些劣势?

5

可能是重复问题:
JIT编译器与离线编译器的区别

直到几分钟前,我并不真正理解JIT编译器和解释器之间的区别。在SO上浏览时,我找到了答案,这也引发了标题中的问题。据我所知,JIT编译器的好处是能够使用它运行的特定处理器,并因此可以制作更好优化的程序。请问有人能给我比较每种方法的优缺点吗?


1
请见JIT编译器与离线编译器 - Matthew Flaschen
1
我认为这与其他问题完全不是重复的,因为另一个问题问的正好与这个问题相反:“是否存在JIT编译器比C++等其他编译器更快的情况?”(如果此问题被关闭,我将投票重新开放。) - Ken Bloom
6个回答

12

解释器、JIT编译器和“离线”编译器

JIT编译器和解释器的区别

简单来说,解释器将运行字节码(中间代码/语言)。当虚拟机/解释器决定这样做更好时,JIT编译机制将翻译相同的字节码为针对特定硬件的本地代码,并侧重于请求的优化类型。

所以基本上JIT可能会产生更快的可执行文件,但编译需要更长时间?

我认为您所忽略的是JIT编译发生在运行时而不是编译时(与“离线”编译器不同)

JIT编译开销

编译代码不是免费的,也需要时间。如果它花时间进行编译,然后只运行几次就停止了,那么它可能没有做出一个好的交易。因此,虚拟机仍然需要决定什么被定义为“热点”,并进行JIT编译。

请允许我举例Java虚拟机(JVM):

JVM可以采取开关,通过该开关您可以定义代码被JIT编译的阈值。 -XX:CompileThreshold=10000

为了说明JIT编译时间的成本,假设您将该阈值设置为20,并且有一段需要运行21次的代码。运行20次后,虚拟机将花费一些时间进行JIT编译。现在您有了来自JIT编译的本地代码,但它只能再运行一次(第21次),这可能没有带来任何性能提升以弥补JIT过程。

我希望这说明了问题。

这是一个JVM开关,显示JIT编译的时间-XX:-CITime“打印JIT编译花费的时间”

旁注: 我不认为这是一个“大问题”,只是因为你提出了这个问题,所以我想指出来。


所以基本上JIT可能会生成更快的可执行文件,但编译时间会更长? - Maulrus
我不会说它比较长,但在程序运行时会有开销。 - TofuBeer
哦,我想我误解了 JIT 编译器的功能。我曾认为它只会在第一次运行时完全编译程序。但它是否也像解释器一样工作呢? - Maulrus
1
@Maulrus:这取决于JIT编译器的目标以及设计师想要支持的优化类型。有些JIT在启动时进行完整的重新编译,而其他JIT则根据确定需要最多优化的部分进行编译。 - Ken Bloom
好的,我现在理解得足够好了。谢谢! - Maulrus
有没有可能预编译JIT?为什么我找不到任何解决方案?比如我有一台专门的机器,我想将我的应用程序(NodeJs)的速度提高到最大,那么我可以让机器预先编译然后以后再使用吗? - Đinh Anh Huy

2
JIT编译并不意味着易于反汇编,这更依赖于实现方式,例如Java二进制文件。然而,需要注意的是,JIT可以应用于任何类型的可执行文件,无论是Java、Python还是来自C++等语言的已编译二进制文件。(如果我没记错的话,Dynamo项目涉及在运行时即时重新编译这些二进制文件以提高性能。)
JIT编译的权衡是,虽然它的目标是提高运行时性能,但该过程实际上也发生在运行时,并因分析、编译和验证代码片段而产生开销。如果实现效率低下或优化不足,则实际上会导致性能下降。
另一个权衡是,在某些情况下,JIT编译可能非常浪费资源。例如,考虑一个自修改的可执行文件。如果你编译了一段代码片段,然后可执行文件修改了该片段,你必须丢弃已编译的片段,然后重新分析该片段以确定是否值得重新编译。如果这种情况经常发生,将会有显著的性能损失。
最后,由于编译后的代码片段必须驻留在内存中才能发挥作用,因此会带来内存消耗的负担。这可能使得在内存有限的设备上实现变得不切实际,或者极为困难。

苹果的Rosetta可以将PowerPC代码即时编译成x86代码。 - Ken Bloom
苹果的Mac 68K仿真器(在PCI PowerMacs上)也使用JIT编译。 - Ken Bloom
这两个例子都是一种特殊形式的JIT编译,被称为二进制翻译(参见http://en.wikipedia.org/wiki/Binary_translation)。我对它们都不是特别熟悉(“我是PC”),但我想它们都采用了动态二进制翻译。 - Zac

2

对我来说,至少缺乏内联汇编是一个大问题。有时,你只是想要对程序的某个小部分完全控制CPU的每个细节。即使我不需要它来完成手头的任务,我也喜欢这个想法:在原则上,我的语言可以做到计算机所能做到的一切。


1
JIT编译器的内存开销要大得多,因为它们需要加载编译器和解释器,以及运行时库和AOT(预先编译)编译程序所需的已编译代码。

1

JIT 编译器更难编写(这不是全部,但值得一提)。


0
我认为使用JIT编译器的一个真正的缺点(实际上更像是一个副作用)是,它很容易将IL反汇编成人类可读的代码。

不是这样的!Java语言和Java字节码展示了这种特性,但是编译成Java字节码的JRuby程序无法被清晰地反编译。同样,使用苹果的Rosetta将PowerPC程序JIT到x86机器代码也是如此。 - Ken Bloom
Scala程序在编译成Java字节码之前需要经过多个语法糖层级的解析(这使得它们更难读,但并非不可能), - Ken Bloom
JIT不一定只用于IL。惠普实验室的Dynamo项目基本上是针对HP/UX机器码的JIT。JIT和底层架构是完全独立的。 - Ken
@Ken:我不记得说过“JIT和底层架构有依赖关系”了?如果你在使用JIT技术,那么你就必须有中间语言(IL)。 - Mitch Wheat
Mitch: 我无法理解你的意思。当 Dynamo 被创建时,PA-RISC 机器码变成了 IL?还是因为 PA-RISC 芯片存在,Dynamo 不是 JIT?(虽然硬件 JVM 芯片存在,但我不认为有人声称它们使 Hotspot JIT 不是 JIT。)还是其他什么原因? - Ken
显示剩余2条评论

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