为什么不在所有地方都使用JIT(即时编译器)?

12

我最近了解到JIT编译器是用来将平台无关的代码编译成本地代码的。JVM和.NET运行环境使用它来获得更好的性能和显著缩短编译时间。我的问题是,为什么不把直接编译成本地代码(如C编译器)的普通编译器也制作成JIT?是否有限制或规范适用于使用JIT编译器?

4个回答

8
JIT有优点和缺点。如果您将软件部署到许多不同的PC上,JIT编译器可以检测如何针对每个特定平台优化代码,因此JIT非常有用。
问题在于JIT在软件执行之前添加了另一个必须进行的步骤:首先将其编译为IL,然后将其编译为机器代码,这意味着额外的性能开销。但是,从IL转换为机器代码只需要在第一次运行软件时完成,因此每个后续调用都会快得多。
因此,基本上(作为经验法则),您可以说:如果软件是长时间运行的进程,则通常使用JIT很好;如果软件具有短暂的生命周期,则最好使用本机代码。

但是JIT本质上是否涉及IL和本机代码分层呢?我的问题是它不能用于简单的本地语言编译器吗? - Nithish Inpursuit Ofhappiness
我不同意那个经验法则。很多长时间运行的服务器软件(例如数据库系统如mysql,或者计算DNA或核反应堆数周CPU时间的复杂数值代码)从预先优化编译器中获益良多。 - Basile Starynkevitch
是的,这就是我为什么称它为经验法则的原因。请在维基百科上阅读“经验法则”的定义:http://en.wikipedia.org/wiki/Rule_of_thumb - Leon Cullens

6
现代的JavaScript实现也会进行即时编译(JIT),一些PHP、Python和Ruby的实现也是如此(至少有一部分)。然而,JIT的关键在于它们是相对较新的发展,而且使其正常运行的一部分原因是依赖于某种类型的框架或运行时,该框架或运行时存在于最终用户的计算机上,并能够为该计算机和应用程序实例进行正确的优化。
对于那些希望接近计算机底层的语言来说,依赖于额外的抽象层并不总是合理的选择。
JIT的另一个问题是它可能会增加应用程序的启动时间。自原始答案以来,软件开发整体上更多地转向云和持续集成/部署,纯冷启动更为常见,并且在过去并不重视JIT预编译步骤所使用的时间对于性能的感知影响。
这就是为什么例如.Net,在历史上一直全面支持JIT,最近的版本已经在更好的AoT(Ahead of Time)支持方面取得了很大进展的原因。

JIT 是否总是涉及到额外的抽象层? - Nithish Inpursuit Ofhappiness
是的,那个层就是JIT的全部意义所在。如果你规避了这个层(例如使用ngen.exe),那么你就不再使用JIT了。请参见http://msdn.microsoft.com/en-us/library/6t9t5wcf(v=vs.80).aspx。 - Leon Cullens

4
我能想到几个原因:
  • 预编译二进制文件可以使用高度优化,以实现最佳性能需要花费数天的时间,你不会希望在即时编译器中使用它们。
  • JIT编译的初始过程可能比直接解释要慢,但在常见情况下,后续运行中没有明显差异。
  • JIT编译可能占用用户资源,这些资源需要用于其他运行时进程(我没有看到很多3D JIT编译游戏)。
  • 如果有效地编写预编译解释器,可以实现足够接近的速度,同时允许对源进行即时修改而没有性能降级。

随着x86指令集的分歧和消费设备中越来越广泛使用arm、mips等不同操作系统,商业软件的情况正在发生变化。当你添加了JIT编译器可以使用视频卡上的本地代码(这也是多种多样的),就不再合理尝试为每个唯一组合优化编译版本的分发。在某个时候,一个合理的JIT编译器可以访问所有CPU和GPU将优于受限于(例如)x86子集的编译等效版本。

经常被问到的一件事是,你是否可以只分发字节码...几乎可以,但不完全相同。这对于Java来说是可行的,因为它被设计成虚拟的,因此不需要担心字节序和其他硬件问题,libc则需要考虑到这些问题,或者针对每种体系结构定制汇编语言代码(所有这些都在虚拟机中实现)。要真正获得强大立足点,就需要实现一个基本适用于JIT的libc(可能基于musl libc,由于其简化的代码和许可证)。


1

提前优化编译器,例如大多数C编译器(如GCC),可能会产生比JIT编译器更好的机器代码。但是提前优化编译器通常需要比JIT更多的时间来进行优化。例如,gcc -O3正在执行许多昂贵的优化,而大多数JIT JVM无法承担。因此,gcc -O3通常会产生非常高效的机器代码(比JVM能做的要好得多)。

然而,在某些情况下,JIT技术可能会提供更好的代码,因为它能够考虑到一些提前优化编译器不知道的动态属性。例如,JIT编译器可以考虑到在某个特定的调用点上,调用的参数通常具有一个给定的类(该类取决于调用点,并且是动态学习的)。


编译器的目标通常是生成机器代码,所以我对你上面的问题感到惊讶......你认为编译器在生成什么??? - Basile Starynkevitch
我对你回答中的“更好”的部分很感兴趣。 - Nithish Inpursuit Ofhappiness

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