GraalVM中的多层编译优点


GraalVM 21.1引入了一项新功能,称为多层编译,适用于在Truffle上实现的语言。多层模式改善了预热行为,对于包含大型代码库的程序特别有用,可将启动时间缩短30%-50%。多层模式下的核心思想是将调用目标的编译分为两层-更快的第一层和较慢的第二层。第一层编译执行的优化较少,但是可以快速获得,因此可以帮助程序更快地启动。第二层编译的速度较慢,因为它们会进行更多的优化,但它们可以确保程序最终达到峰值性能。从21.1版本开始,GraalVM中的多层编译默认情况下处于打开状态。
有几种实验选项可以控制多层编译的行为。通常,默认值最有效,但是喜欢冒险的用户可能希望在其工作负载上尝试以下选项:

  • MultiTier-此选项启用多层编译。自GraalVM 21.1起,默认情况下为true。
  • FirstTierCompilationThreshold —在计划进行第一层编译之前在调用目标中执行的调用或循环迭代的次数。
  • LastTierCompilationThreshold-在计划将其安排用于第二层编译之前,在调用目标中执行的调用或循环迭代的次数。
  • FirstTierInliningPolicy-确定在第一层编译中应使用哪种内联策略。默认情况下,在GraalVM 21.1中将其设置为TrivialOnly,这意味着仅内联小的方法。
  • FirstTierUseEconomy —此选项确定快速编译是否应使用最少数量的优化。默认情况下为true。
  • FirstTierBackedgeCounts-此选项确定是否应将循环迭代计入第二层编译阈值。默认情况下为true。
  • SingleTierCompilationThreshold-此选项确定关闭多层编译时的编译阈值。它具有与CompilationThreshold相同的效果,后者已不建议使用-它已有效地重命名为此选项。

 
缩短启动时间
为了演示多层编译的好处,我们首先介绍一个预热曲线的概念。程序内部运行的相同工作量越长,程序趋向于变得越来越快,这是因为程序代码最初并未进行编译,而是在VM中运行程序时进行编译的。程序越快越快,预热就越好。预热曲线是一种表征程序执行速度如何随时间变化的方式。一般而言,预热曲线绘制了性能指标如何随程序的每次重复而变化。在本文的大部分内容中,性能指标将是自VM启动以来的总执行时间。总执行时间越短,预热效果越好。
在多层执行中,程序花费大约33%的时间到达第一次迭代的末尾,而花费大约43%的时间到达第十次迭代的末尾。快速的第一层编译可更快地减小预热曲线的斜率,这意味着完成每次迭代所需的时间更少。单层编译最终还会逐渐减小曲线的斜率。
 
“一次性”指标
“一次性”指标是VM启动到工作负载的第一次重复结束之间的时间量。
“一次性”度量特别有趣,因为它在执行开始时捕获了动态。在许多实际场景中,我们对短期运行的程序(例如命令行工具或单个单元测试执行)感兴趣。在其他情况下,我们希望该程序尽早达到良好的执行性能,即使它在很晚之后达到最佳的峰值性能也是如此。
定义了“一次性”度量标准的概念后,现在让我们通过查看“一次性”度量标准而不是预热曲线来检查其他几种GraalVM语言的预热行为:
在“一次性”度量标准上,多层执行比单层执行快约33%;
多层TruffleRuby的“一次性”指标比MRI快2.75倍;
 
编译时间
在预热动力学中起作用的另一个因素是编译时间。编译器速度越快,程序就越早达到峰值性能。
编译性能的提高将预热曲线上的点(包括峰点)移到左侧。
通常,有两种方法可以使优化编译器更快。第一种方法是检查编译器的实现,并改善代码在编译器性能欠佳的位置。假设程序不是最佳程序,则可以优化任何程序。第二种方法是减少编译器所做的优化量。虽然这可以使优化编译器的速度更快,但也会降低峰值性能。

更多点击标题