77

我最近一直在考虑它,在我看来,JIT编译的大部分优势应该或多或少地归功于中间格式,而 jitting 本身并不是生成代码的好方法。

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

  1. 即时编译允许更大的可移植性。这不是归因于中间格式吗?我的意思是,一旦你在机器上安装了虚拟字节码,没有什么能阻止你将它编译为本机字节码。可移植性是“分发”阶段的问题,而不是“运行”阶段的问题。
  2. 好的,那么在运行时生成代码呢?嗯,同样适用。没有什么能阻止您将实时编译器集成到您的本地程序中,以满足真正的实时需求。
  3. 但是运行时无论如何只将其编译为本机代码一次,并将生成的可执行文件存储在硬盘驱动器某处的某种缓存中。当然可以。但是它在时间限制下优化了您的程序,并且从那里开始并没有使它变得更好。见下一段。

提前编译也不是没有优势。即时编译有时间限制:您不能让最终用户在程序启动时永远等待,因此需要在某个地方进行权衡。大多数时候,他们只是优化较少。我的一个朋友有分析证据表明,“手动”内联函数和展开循环(在过程中混淆源代码)对他的C#数字运算程序的性能产生了积极影响;在我这边做同样的事情,我的C程序完成相同的任务,没有产生积极的结果,我相信这是由于我的编译器被允许进行的广泛转换。

然而,我们被jittered 的程序所包围。C#Java无处不在,Python 脚本可以编译成某种字节码,我敢肯定一大堆其他编程语言也能做到这一点。我失踪一定有充分的理由。那么,是什么让即时编译优于提前编译呢?


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

大多数运行时(或就此而言的编译器)将更喜欢即时编译或提前编译它们。由于提前编译对我来说似乎是一个更好的选择,因为编译器有更多的时间来执行优化,我想知道为什么 Microsoft、Sun 和所有其他公司都在反其道而行之。我对与性能分析相关的优化有点怀疑,因为我对即时编译程序的经验表明基本优化很差。

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

4

9 回答 9

41
  1. 更高的可移植性:可交付成果(字节码)保持可移植性

  2. 同时,更特定于平台:因为 JIT 编译发生在代码运行的同一系统上,所以可以针对该特定系统进行非常非常精细的调整。如果您进行提前编译(并且仍想将相同的包发送给每个人),您必须妥协。

  3. 编译器技术的改进可能会对现有程序产生影响。更好的 C 编译器根本无法帮助您处理已经部署的程序。更好的 JIT 编译器将提高现有程序的性能。你十年前写的 Java 代码今天会运行得更快。

  4. 适应运行时指标。JIT 编译器不仅可以查看代码和目标系统,还可以查看代码的使用方式。它可以检测正在运行的代码,并根据例如方法参数通常恰好具有的值来决定如何优化。

JIT 增加了启动成本是对的,因此它有时间限制,而提前编译可能会占用它想要的所有时间。这使得它更适合服务器类型的应用程序,在这些应用程序中,启动时间并不那么重要,并且在代码变得非常快之前的“热身阶段”是可以接受的。

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

于 2010-01-21T01:57:57.540 回答
18

ngen 工具页面溢出了 bean(或者至少提供了本机图像与 JIT 编译图像的良好比较)。提前编译的可执行文件通常具有以下优点:

  1. 原生镜像加载速度更快,因为它们没有太多的启动活动,并且需要更少的静态内存(JIT 编译器所需的内存);
  2. 原生镜像可以共享库代码,而 JIT 编译的镜像则不能。

在这些情况下,即时编译的可执行文件通常占上风:

  1. 原生图像比其对应的字节码大;
  2. 每当修改原始程序集或其依赖项之一时,都必须重新生成本机映像。

每次它的一个组件都需要重新生成一个提前编译的图像,这对于原生图像来说是一个巨大的缺点。另一方面,JIT 编译的图像不能共享库代码这一事实可能会导致严重的内存命中。操作系统可以在一个物理位置加载任何本机库,并与每个想要使用它的进程共享它的不可变部分,从而显着节省内存,尤其是几乎每个程序都使用的系统框架。(我想这在一定程度上被 JIT 编译的程序只编译它们实际使用的东西所抵消。)

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

于 2010-01-24T03:36:48.020 回答
7

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

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

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

于 2010-01-21T01:59:23.353 回答
5
  1. 更好的反射支持。这原则上可以在提前编译的程序中完成,但在实践中似乎几乎从未发生过。

  2. 通常只能通过动态观察程序才能找出优化。例如,内联虚函数、转义分析以将堆栈分配转换为堆分配,以及锁粗化。

于 2010-01-21T02:00:06.880 回答
4

也许它与现代编程方法有关。你知道,很多年前你会在一张纸上写你的程序,其他人会把它变成一堆打孔卡并输入计算机,明天早上你会在一卷纸上称重半磅。所有这一切都迫使您在编写第一行代码之前进行大量思考。

那些日子已经一去不回。使用 PHP 或 JavaScript 等脚本语言时,您可以立即测试任何更改。Java 并非如此,尽管应用服务器为您提供热部署。因此,可以快速编译Java 程序非常方便,因为字节码编译器非常简单。

但是,没有 JIT-only 语言这样的东西。提前编译器可用于 Java 已经有一段时间了,最​​近 Mono 将其引入了 CLR。事实上,MonoTouch是完全可能的,因为 AOT 编译,因为非本地应用程序在 Apple 的应用程序商店中是被禁止的。

于 2010-01-21T05:57:49.623 回答
3

看来这个想法已经用 Dart 语言实现了:

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

在开发过程中使用 JIT 编译,使用速度特别快的编译器。然后,当应用程序准备好发布时,它会被编译为 AOT。因此,在高级工具和编译器的帮助下,Dart 可以实现两全其美:极快的开发周期,以及快速的执行和启动时间。

于 2018-03-02T14:57:24.980 回答
2

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

于 2014-03-07T16:54:46.643 回答
2

我在此处未列出的 JIT 的一个优点是能够跨单独的程序集/dll/jar 进行内联/优化(为简单起见,我将从此开始使用“程序集”)。

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

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

于 2016-11-05T18:33:02.483 回答
0

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

于 2017-12-12T09:32:47.037 回答