【问题标题】:Why is it hard to beat AOT compiler with a JIT compiler (in terms of app. performance)?为什么用 JIT 编译器(在应用程序性能方面)很难击败 AOT 编译器?
【发布时间】:2011-11-27 07:36:10
【问题描述】:

我认为 JIT 编译器最终会在编译代码的性能方面击败 AOT 编译器,这是由于 JIT 的固有优势(可以使用仅在运行时可用的信息)。一个论点是 AOT 编译器可以花费更多时间编译代码,但服务器 VM 也可能花费大量时间。

我确实了解 JIT 在某些情况下似乎确实击败了 AOT 编译器,但在大多数情况下它们似乎仍然落后。

所以我的问题是,阻止 JIT 编译器击败 AOT 编译器的具体棘手问题是什么?

编辑:
一些常见的论点:

  • AOT 编译器可以花费更多时间进行高级优化->如果您运行服务器虚拟机数天,您可以花费相同的时间,甚至更长。
  • 字节码解释成本-> 现在大多数 JIT 编译器都会缓存本机机器指令。

又一次修改:
具体示例见这篇文章:Improving Swing Performance: JIT vs AOT Compilation。根据我从这篇文章中可以收集到的信息,作者基本上是在说,当没有热点时,拥有运行时信息的优势会降低,因此没有 JIT 开销的 AOT 会获胜。但是40%??这似乎没有多大意义。仅仅是被比较的 JIT 编译器没有针对这种情况进行调整吗?还是更基本的东西?

【问题讨论】:

  • 您似乎完全错过了“公正”的概念。编译器性能是由人而不是机器来衡量的。没有人能忍受 10 秒的等待。
  • @Hans Passant:在客户端虚拟机中,我明白了这一点。但是在服务器虚拟机中?例如 JVM 就有这种区别。
  • 我想这可以归结为“为什么很难编写一个能够发出优化良好的机器代码的编译器?”。此外,将 Java/JIT 与 C++/AOT 进行比较就是将 Apple 与 Orange 进行比较。 (除非正在讨论特定 Java AOT/JIT?)虽然AOT确实“有更多时间”花在整个过程上,但不要忘记javac已经做得很好编译块——词法分析、解析、一些优化,尽管是“虚拟机”——本身。
  • @Enno Shioji:来自你链接的文章:“40% 的性能胜利从何而来?我们在进一步提高 Swing 性能时意外地找到了答案:没有 hot方法。”
  • @Rob:是的,但这是否会导致 40% 的性能差异?至少,一旦 JIT 检测到热点优化不值得,它就会开始表现得像 AOT。这似乎是一件相当容易解决的事情。

标签: performance compiler-construction jit vm-implementation


【解决方案1】:

编译和优化是代码、上下文和时间的函数。

原始权衡

AOT 可以根据需要使用尽可能多的时间,并具有深入的代码分析、执行平台知识和其他优化提示。缺点是构建速度较慢、代码可移植性较差、基于错误假设的不良优化以及缺乏对运行时反射等高级功能的支持。

JIT 更便携,可以在运行时支持高级代码更改,但启动时间更长,时间和资源有限,这意味着优化有限。

现代发展

最新的 JIT 编译器速度更快,启动速度更快,同时仍能生成良好的代码,在启动延迟方面与 AOT 缩小了大部分差距。

多个编译过程允许 JIT 在程序运行时不断优化程序,从而在预热期后获得与 AOT 相似的性能。 JIT 可以查看实际使用的代码、使用频率以及其他因素,例如在 AOT 编译期间不可用的正在运行的硬件。

最先进的 JIT 编译器可以达到或超过 AOT 性能并在更多场景下保持。 AOT 仍然是快速启动和一致/可预测操作的最佳选择,但不再是默认选项最佳整体性能。

【讨论】:

  • AOT 可以像 JIT 一样可移植(在某些情况下,AOT 和 JIT 使用完全相同的字节码 - 例如 Microsoft 的 CIL);并且 JIT 可以“根本不可移植”(例如,您要使用的系统没有 JIT 编译器)。 JIT 也可能缺少或不正确的优化(并且几乎总是故意错过优化,因为它们在运行时执行起来成本太高)。
  • 就性能而言,AOT 通常比它可能/应该要慢(例如,开发人员针对通用目标进行了优化,而不针对用户的特定机器进行了优化),但这是当前工具的弱点(不是AOT 本身);而现代 JIT 仍然不如“不是很好的 AOT”。为了弥补“高级 JIT”性能有多糟糕,他们还(几乎总是)依赖充满“AOT 编译”本机代码的大型库来隐藏性能灾难。在公平的比较中,从来没有任何“高级 JIT”能够击败 AOT(永远不会)。
  • @Brendan 这些都是迂腐的假设,而不是关于概念的争论。 CIL 无关紧要。 JIT 更具可移植性,因为它可以在许多机器上运行相同的工件,而不需要匹配的预构建二进制文件。没有可用的 JIT 编译器与没有对平台的 AOT 支持相同(实现细节)。如前所述,二次传递通过在应用程序生命周期内摊销编译同时匹配实际使用情况来克服任何运行时限制。 JIT 可以做 AOT 可以做的所有事情,但也可以通过使用 AOT 根本无法使用的输入因素做得更好。
  • 帮我个忙 - 在计算机上安装 Gentoo Linux,然后再告诉我 AOT 无法针对特定机器进行优化,和/或了解什么是“运行时调度”(即 Intel 的 ICC编译器确实)是;然后尝试在 MS-DOS 上运行 Java 以了解它的可移植性。然后尝试对“纯”Java 代码进行基准测试(不使用充满 AOT 编译的本机代码的库;并且不使用 AOT 先将其编译为字节码)。最后,尝试在一个短暂的小型实用程序上分摊不必要的性能灾难(AOT 程序将在您甚至加载一个巨大的 JIT 编译器之前完成)。
  • @Brendan 显然,如果 JIT 没有像 AOT 那样多的时间,那么在物理上实现相同的结果是不可能的。这已经包含在答案中。您忙于轶事和一些与 JIT 的教条斗争,以至于您将概念、实现和边缘案例混为一谈。到此结束吧。
【解决方案2】:

在 JIT 和 AOT(提前)编译之间存在明确的权衡

正如您所说,JIT 可以访问有助于优化的运行时信息。这包括有关它正在执行的机器的数据,从而实现特定于平台的本机优化。但是,JIT 也有将字节码转换为本机指令的开销。

在需要快速启动或接近实时响应的应用程序中,这种开销通常会很明显。如果机器没有足够的资源进行高级优化,或者代码的性质使得它无法“积极优化”,JIT 也不会那么有效。

例如取自the article you linked:

...我们应该怎么做 在没有明显的性能瓶颈的情况下改进?如你所愿 猜到了,profile-guided JIT也存在同样的问题 编译器。而不是对几个热点进行积极优化, 有很多“温暖的地方”完好无损。

AOT 编译器也可以根据需要花费尽可能多的时间进行优化,而 JIT 编译受时间要求(以保持响应能力)和客户端计算机资源的约束。因此,AOT 编译器可以执行在 JIT 期间成本太高的复杂优化。

另请参阅这个 SO 问题:JIT compiler vs offline compilers

【讨论】:

  • 一个人愿意花在构建一段代码上的时间越多,它的运行速度就越快。如果 AOT 编译器的用户愿意给构建过程更多的时间,它将能够生成更高效的代码。然而,JIT 编译的一个优点是,对于从不运行的代码而言,它比 AOT 编译快无限。在某些涉及 .NET 泛型的情况下,有限(甚至很少)数量的源代码可以指定一个无限的方法族,每个方法都需要单独的机器代码;如果只运行十几个这样的方法,那么只有这十二个会被编译成机器码。
  • 配置文件引导的 AOT 编译怎么样?
猜你喜欢
  • 2019-03-01
  • 1970-01-01
  • 2018-01-23
  • 2023-04-02
  • 2011-02-23
  • 2011-02-19
  • 2020-06-20
  • 2017-03-13
  • 2010-09-10
相关资源
最近更新 更多