我最近了解到 JIT 编译器用于将平台无关代码编译为本机代码。JVM 和 .net 运行时环境使用它,以获得更好的性能并显着减少编译时间。我的问题是,为什么不将直接编译为本机代码的普通编译器(如 c 编译器)也制作为 JIT?如果使用 JIT 编译器,是否有使用限制或规范?
4 回答
JIT 有优点也有缺点。如果您将软件部署到许多不同的 PC,JIT 会非常有用,因为 JIT 编译器可以检测如何针对每个特定平台优化代码。
问题是 JIT 增加了在软件可以首先执行之前必须采取的另一个步骤:首先必须将其编译为 IL,然后再编译为机器代码,这意味着额外的性能开销。然而,这种从 IL 到机器代码的转换只需要在软件第一次运行时完成,因此每次后续调用都会快得多。
所以基本上(根据经验)你可以说:如果软件是一个长期运行的过程,那么使用 JIT 通常会很好,如果软件的生命周期很短,最好使用本机代码。
现代 javascript 实现也执行 JIT,一些 PHP、Python 和 Ruby 实现(至少)也是如此。不过,JIT 的诀窍在于它们是相对较新的开发,使它们工作的部分原因是您依赖于最终用户机器上的某种类型的框架或运行时,它能够生成正确的该机器和应用程序实例的优化。
对于想要接近计算机“裸机”的语言,依赖那个额外的抽象层并不总是有意义的。
我能想到几个原因:
预编译的二进制文件可以使用需要数天才能达到最佳性能的高级优化,您不会希望在 JIT 编译器中这样做
初始 JIT 编译可能比直接解释花费更长的时间,在常见情况的后续运行中差异不明显
JIT 编译会占用其他运行时进程所需的用户资源(我没有看到很多 3d JIT 编译的游戏)
预编译的解释器,如果编写得好,可以达到足够接近的速度,同时允许用户即时修改源代码而不会降低性能
随着 x86 指令集的分歧以及 arm、mips 等在消费设备以及不同操作系统中的日益广泛使用,商业软件的这种情况开始发生变化。当您添加 JIT 编译器在视频卡上使用本机代码的能力(也有很大差异)时,尝试分发针对每个独特组合优化的编译版本变得越来越不合理。在某些时候,可以访问所有 cpu 和 gpu 的合理 JIT 编译器的性能将优于仅限于(例如)x86 子集的已编译等效项。
经常被问到的一件事是您是否可以仅分发字节码……几乎,但不完全。它适用于 java,因为它被设计为虚拟的,因此不需要担心 libc 所做的字节顺序和其他硬件问题,或者每个架构的自定义汇编语言位(所有这些都在虚拟机中实现)要真正获得稳固的基础,需要实现一个为 JIT 设计的基本 libc(可能基于 musl libc,因为它的简化代码和许可许可)
像大多数 C 编译器(例如 GCC)一样,提前优化编译器可能会产生比 JIT 编译器更好的机器代码。但是提前编译器通常需要更多的时间(比 JIT)来优化。例如,gcc -O3
正在进行大多数 JIT JVM 无法承受的大量优化。因此,gcc -O3
通常会产生非常高效的机器代码(比 JVM 可以做的要好得多)。
但是,在某些情况下,JIT 技术可能会提供更好的代码,因为它能够考虑一些提前编译器未知的动态属性。例如,JIT 编译器可以考虑到,在某个特定的调用站点上,调用的参数通常具有一个给定的类(并且该类取决于调用站点,并且是动态学习的)。