即时编译与提前编译有什么好处?

时间:2010-01-21 01:46:40

标签: compilation jit

我最近一直在考虑这个问题,在我看来, JIT 编译的大部分优势应该或多或少地归结为中间格式,而且jint本身并不是这是生成代码的好方法。

所以这些是我经常听到的主要的 pro-JIT 编译论据:

  1. 即时编译可以提高可移植性。这不是可归因于中间格式吗?我的意思是,一旦你的虚拟字节码在你的机器上得到它,没有什么能阻止你将它们编译成本机字节码。可移植性在“分发”阶段是一个问题,而不是在“运行”阶段。
  2. 那么,那么在运行时生成代码呢?嗯,同样适用。没有什么可以阻止您将实时编译器集成到您的本机程序中。
  3. 但是运行时只需将其编译为本机代码一次,并将生成的可执行文件存储在硬盘驱动器某处的某种缓存中。是的,当然。但是它在时间限制下优化了你的程序,而且从那时起它并没有让它变得更好。见下一段。
  4. 它不像提前编译也没有优势。 即时编译有时间限制:您无法让最终用户在程序启动时永远等待,因此需要权衡某些事情。大多数时候他们只是减少优化。我的一个朋友有分析证据内联函数和“手动”展开循环(在过程中混淆源代码)对他的 C#数字运算的性能产生了积极影响程序;在我这方面做同样的事情,我的 C 程序填写相同的任务,没有产生任何积极的结果,我相信这是由于我的编译器允许进行广泛的转换。

    然而我们被jitted程序所包围。 C# Java 无处不在,Python脚本可以编译成某种字节码,我确信其他一些编程语言也是如此。我必须有一个很好的理由让我失踪。那么是什么让及时编译优于提前编译?


    编辑为了清除一些混淆,也许重要的是要声明我全都是可执行文件的中间表示。这有很多优点(实际上, just-in-time 编译的大多数参数实际上是中间表示的参数)。我的问题是如何将它们编译为本机代码。

    大多数运行时(或编译器)都希望能够及时或提前编译它们。由于提前编译看起来像是一个更好的替代方案,因为编译器有更多的时间来执行优化,我想知道为什么微软,Sun和所有其他人都在反过来。我对与性能分析相关的优化很有疑问,因为我对即时编译程序的经验显示出很差的基本优化。

    我只使用了C代码示例,因为我需要一个提前编译与即时编译的示例。 C 代码没有发布到中间表示的事实与情况无关,因为我只需要表明提前编译可以立即产生更好的效果结果

9 个答案:

答案 0 :(得分:34)

  1. 更大的便携性: 可交付(字节码)保持不变 便携

  2. 同时,更具体平台性:因为 JIT编译发生在 与代码运行相同的系统,它 可以非常非常精细地调整 那特定的系统。如果你这样做 提前编译(仍然是 想要运送相同的包裹 大家),你必须妥协。

  3. 编译器技术的改进会对其产生影响 现有的计划。一个更好的C. 编译器根本没有帮助你 已部署的程序。一个 更好的JIT编译器将改进 现有计划的表现。 你十年前写的Java代码今天运行得更快。

  4. 适应运行时指标。 JIT编译器不仅可以查看 代码和目标系统,但是 也是如何使用代码。它可以 检测运行代码,以及 做出如何优化的决定 根据,例如,什么 通常重视方法参数 碰巧有。

  5. 你是对的,JIT会增加启动成本,所以它有一个时间限制, 而提前编译可以占用它想要的所有时间。这样做 更适合服务器类型的应用程序,其中启动时间不是那么重要 在代码变得非常快之前的“预热阶段”是可以接受的。

    我想可以在某处存储JIT编译的结果,以便下次可以重用它。这将为您提供第二个程序运行的“提前”编译。也许Sun和微软的聪明人都认为新的JIT已经足够好了,额外的复杂性不值得麻烦。

答案 1 :(得分:14)

ngen tool page溢出了bean(或至少提供了原生图像与JIT编译图像的良好比较)。提前编译的可执行文件通常具有以下好处:

  1. 本机映像加载速度更快,因为它们没有太多启动活动,并且需要静态更少的内存(JIT编译器所需的内存);
  2. 原生图像可以共享库代码,而JIT编译的图像则不能。
  3. 在这些情况下,即时编译的可执行文件通常具有优势:

    1. 原生图像大于字节码对应物;
    2. 每当修改原始程序集或其中一个依赖项时,都必须重新生成本机映像。
    3. 每当其中一个组件对本机图像的巨大不利时,需要重新生成一个提前编译的图像。另一方面,JIT编译的图像不能共享库代码的事实可能导致严重的内存损失。操作系统可以在一个物理位置加载任何本机库,并与每个想要使用它的进程共享它的不可变部分,从而节省大量内存,特别是对于几乎每个程序都使用的系统框架。 (我想这可以通过JIT编译的程序只编译它们实际使用的东西来抵消。)

      微软在这个问题上的一般考虑是大型应用程序通常会提前编译,而小型应用程序通常不会。

答案 2 :(得分:6)

简单的逻辑告诉我们,即使从字节码编译巨大的MS Office大小程序也只需要花费太多时间。你最终会有很长的开始时间,这会吓到你的产品。当然,您可以在安装期间进行预编译,但这也会产生后果。

另一个原因是并非所有应用程序部分都会被使用。 JIT只会编译那些用户关心的部分,可能会使80%的代码保持不变,从而节省时间和内存。

最后,JIT编译可以应用普通编译器无法实现的优化。就像使用trace trees内联虚拟方法或部分方法一样。从理论上讲,这可以使它们更快。

答案 3 :(得分:4)

  1. 更好的反思支持。原则上可以在提前编译的程序中完成,但在实践中似乎几乎不会发生。

  2. 通常只能通过动态观察程序来计算优化。例如,内联虚函数,转义分析以将堆栈分配转换为堆分配,并锁定粗化。

答案 4 :(得分:2)

我一直试图理解这一点,因为我看到谷歌正在用Android运行时(ART)取代他们的Dalvik虚拟机(本质上是另一个Java虚拟机),这是一个AOT编译器,但Java通常使用HotSpot,它是一个JIT编译器。显然,ARM比Dalvik快〜2倍......所以我对自己说“为什么Java也不使用AOT?”。 无论如何,从我可以收集到的,主要的区别是JIT在运行时使用自适应优化,(例如)只允许频繁执行的字节码的那些部分被编译成本机代码;而AOT将整个源代码编译成本机代码,而较少量的代码比代码量更大的代码运行得更快。
我必须想象大多数Android应用程序都是由少量代码组成的,因此平均而言,将整个源代码编译为本机代码AOT更有意义,并避免解释/优化所带来的开销。

答案 5 :(得分:2)

我在此处未列出的JIT的一个优点是能够在单独的程序集/ dll / jar中进行内联/优化(为简单起见,我将使用"程序集&#34 ; 从这里开始了)。

如果您的应用程序引用了安装后可能会更改的程序集(例如预安装的库,框架库,插件),那么就可以安装"编译安装"模型必须避免跨程序集边界内联方法。否则,当引用的程序集更新时,我们必须在引用系统上的程序集中找到所有这些内联的代码位,并用更新的代码替换它们。

在JIT模型中,我们可以自由内联程序集,因为我们只关心为单个运行生成有效的机器代码,在此期间底层代码不会发生变化。

答案 6 :(得分:2)

似乎这个想法已经用Dart语言实现了:

https://hackernoon.com/why-flutter-uses-dart-dd635a054ebf

  

在开发期间使用JIT编译,使用特别快的编译器。然后,当应用程序准备好发布时,它将被编译为AOT。因此,借助先进的工具和编译器,Dart可以提供两全其美的优势:极快的开发周期,快速的执行和启动时间。

答案 7 :(得分:1)

也许它与现代编程方法有关。你知道,很多年前你会在一张纸上写下你的程序,其他一些人会把它变成一堆穿孔卡并送到电脑里,明天早上你会在一卷纸上撞到一个纸屑半磅。所有这些都迫使你在编写第一行代码之前想了很多。

那些日子早已过去。使用PHP或JavaScript等脚本语言时,可以立即测试任何更改。虽然appservers为您提供热部署,但Java的情况并非如此。所以Java程序可以快速编译非常方便,因为字节码编译器非常简单。

但是,没有JIT专用语言。 Ahead-of-time compilers 已经有一段时间可用于Java了,最近Mono将它引入了CLR。事实上,由于AOT编译, MonoTouch 是可能的,因为Apple的应用商店禁止使用非原生应用。

答案 8 :(得分:0)

平台浏览器动态和平台浏览器之间的区别在于您的角度应用程序的编译方式。 使用动态平台可以将Just-in-Time编译器角度发送到前端和应用程序。这意味着您的应用程序正在客户端编译。 另一方面,使用平台浏览器会导致应用程序的Ahead-of-Time预编译版本被发送到浏览器。这通常意味着将更小的包发送到浏览器。 https://angular.io/docs/ts/latest/guide/ngmodule.html#!#bootstrap引导的angular2文档更详细地解释了它。