【GraalVM JVM 即时编译器】GraalVM 21.2 发布,大量实用性改进
一、前言
GraalVM 21.2已经发布,在这篇文章中,我们将重点介绍这个版本中最明显、最重要、最令人兴奋的更新。
首先,GraalVM 21.2版本可从下列地方下载:
-
GraalVM: https://www.graalvm.org/downloads -
GraalVM Enterprise: https://www.oracle.com/downloads/graalvm-downloads.html
GraalVM 由几个组件组成,在每一个版本中,我们都在努力改进它们。因此,如果你对某个特定组件感兴趣,并希望获得更改的更详细概述,请参阅文档[1]。
二、Native Image
让我们先从Native Image中值得注意的新内容开始。早在6月份,我们就为Native Image发布了新的Gradle和Maven插件,并且提供了最初的JUnit5的支持。它简化了构建你的应用程序的Native Image的过程,并且允许用JUnit的方式在Native Image模式下测试你的代码运行情况。
从最初的版本发布以来,已经有两个小版本对各种bug进行了修复,改进和清理,所以如果你维护了一个使用 Native Image 的应用程序或库,考虑运行这两个测试去验证在 Native Image中的运行情况[2]!
另一个改进是现在 Native Image 会自动从 image 中删除不必要的 security providers 这些可以被访问的 security providers 会被静态分析程序检测。这意味着像 --enable-all-security-services
之类的参数会被弃用,并且在未来会被移除。也可以使用 -H:-EnableSecurityServicesFeature
完全禁用自动检测并手动注册provider。你可以从这个文档[3]中了解到更多细节.
21.2 中一个非常受欢迎的新增功能是实现 class 预定义,以支持运行时的ClassLoader.loadClass
调用。需要在运行时加载的所需 class 必须在构建时提供给静态分析,以便将它们包含在封闭世界中分析,包含在运行时任意时刻加载类的代码模式现在可以在 native images 中工作,正如您所期望的那样。
GraalVM 21.2 的另一个有趣的基础增强是使用-H:+AllowVMInspection
构建的 Native Images 现在支持用Java 编写 JFR 事件[4]。要在运行时记录 JFR 事件,必须使用命令行选项-XX:+FlightRecorder
和-XX:StartFlightRecording
来启用 JFR 支持和 JFR 记录。实际需要实现的事件并不多,但实现它们或从应用程序代码发出它们的体系结构是可用的。
你可以尝试以下示例来查看自定义事件在实践中的效果:
import jdk.jfr.Event;
import jdk.jfr.Description;
import jdk.jfr.Label;
public class Example {
@Label("Hello World")
@Description("Helps programmer getting started")
static class HelloWorld extends Event {
@Label("Message")
String message;
}
public static void main(String... args) {
HelloWorld event = new HelloWorld();
event.message = "hello, world!";
event.commit();
}
}
将其构建到本机映像中,使用-XX:+FlightRecorder -XX:StartFlightRecording=”filename=recording.jfr”
运行,你可以在 VisualVM 中看到事件的样子:
三、编译器更新
每次更新都会在编译器中带来增量的改进和新的优化,从而增加一些工作性能。随着时间的推移,这些收益积累起来,并导致显著的性能提升。如果你最近没有更新,现在是利用所有这些改进的好时机。在21.2中,向编译器添加了许多有趣的优化。让我们从GraalVM Enterprise中可用的组件开始,它们是Oracle Java SE订阅的一部分。
我们改进了计数循环的循环限制分析,因此编译器还分析循环前的控制流,以推断归纳变量。这可以产生更多的未计数循环,就像下面的例子一样,可用于高级优化
long i = 0;
if (end < 1) return;
do {
// body
i++;
} while (i != end);
在这里,编译器可以结合循环之前i
为0
且end ≥ 1
的信息来证明end > i
并使用该数据来优化循环体。
我们还为非计数循环添加了一种新颖的条带挖掘优化,它将循环迭代拆分为多个部分,使它们更易于以后优化。默认情况下是禁用的;使用-Dgraal.StripMineNonCountedLoops=true
选项启用它。
我们改进了使用 StringBuilder 模式的代码的编译,并通过使基于 JDK11 的 GraalVM 构建 JDK11 中的紧凑字符串来增强对这些模式的支持。
然后就 GraalVM 社区版而言,编译器的一个显着改进是增加了Speculative Guard Movement优化,它试图将循环不变保护(例如:数组边界检查)从循环内部移动到循环外部。这可以显着改善相关工作性能。
此外,我们改进了long
计数循环中的安全点消除机制。编译器现在可以消除具有long
归纳变量的循环中的安全点,它可以静态证明归纳变量的范围是Integer.MAX_VALUE - Integer.MIN_VALUE
。思考这个 for 循环示例:
for (long i = Integer.MIN_VALUE;i< Integer.MAX_VALUE;i++) {
// body
}
编译器现在可以静态证明i
只在整数范围内迭代,即使它是一个long
整数。因此,不需要考虑整数溢出的情况,循环的优化效果更好。
除此之外,此版本中还有一些可用的优化在默认情况下尚未启用,并且被认为是实验性的。一项名为Write Sinking的实验性优化尝试将写入移出循环。你可以使用-Dgraal.OptWriteMotion=true
启用它。GraalVM Enteprise 中提供的另一个优化(默认情况下尚未启用)是针对顺序代码的新颖 SIMD(单指令多数据)矢量化优化。你可以使用-Dgraal.VectorizeSIMD=true
选项进行实验。如果你还没有准备好自己进行实验,请继续关注 在即将发布的独立文章中,我们将详细探讨它给你带来了什么,以及你的代码如何从中受益。
四、多语言和Truffle框架
Truffle 受到了一个新的编译队列启发,默认情况下启用。这种新的启发式改进了多语言运行时在工作性能上的预热时间。
下面简要解释一下它的作用:假设以下合成的 JavaScript 代码是你的应用程序。循环运行2个函数,都达到了JIT编译的阈值,需要编译:
function lowUsage() {
for (i = 0; i < COMPILATION_THRESHOLD; i++) {
// Do something
}
}
function highUsage() {
for (i = 0; i < 100 * COMPILATION_THRESHOLD; i++) {
// Do something
}
}
while(true) {
lowUsage();
highUsage();
}
在以前的 GraalVM 版本中,优先编译的算法是按先到先得的顺序工作的。这将导致首先编译lowUsage
函数,而不是先编译highUsage
的更有利的顺序。
现在在 GraalVM 21.2 中,该策略比 hotness 更为复杂,并考虑了多层设置,更快地编译第一层,编译历史记录和取消优化对以前确定为重要的代码进行优先级排序,以及代码 hotness 和同时使用活跃度的加权组合。总而言之,这应该会为所有 GraalVM 语言带来更好的预热。
有额外的配置选项来调整启发式或完全禁用它的使用。在文档[5]中了解更多信息。
另一个变化是嵌入多语言上下文时的某些 API 调用需要新版本的JVMCI,即用于将编译器插入 Java HotSpot VM 的编译器接口。所有 JDK 版本的 GraalVM 发行版都包含新的 JVMCI,但如果你使用具有不同 JDK 的 GraalVM 语言并在模块路径上启用 GraalVM 编译器,请确保使用以下包含JDK-8264016的 JDK 版本,保证完全的兼容性。
否则使用强制上下文取消 (Context.close(true)
) 或中断 (Context.interrupt(Duration)
) 将抛出错误。为了获得最佳体验,请考虑使用 Graa lVM 发行版来运行你的应用程序或避免使用这些 API。注释中[6]描述了其他可能的解决方法。
既然我们正在讨论的是与 Truffle 相关的更改,因此 GraalVM 21.2 中有一些令人兴奋的更新,用于在 GraalVM 上实现语言和工具的项目。Truffle 库现在可以在无需事先执行的情况下为提前编译做好准备。有关更多详细信息,请参阅 ExportLibrary.useForAOT[7] 和 AOT 教程[8]。
五、JavaScript
JavaScript 实现继续添加和更新最新提案的实现,如新的 Set 方法[9]、实验运算符重载支持[10]或 RegExp 匹配索引提案。你可以在发行说明中找到有关如何启用这些功能的详细信息。
最重要的是,Graal.js 通过在多语言Context
跟踪未处理的 promise rejections 选项来增强开发体验。默认情况下,该选项设置为none
,并且不会跟踪未处理的 promise rejections,但是你可以使用js.unhandled-rejections
选项进行配置,并使你的开发更轻松。
六、Ruby
Ruby 也一如既往地经历了一系列持续不断的兼容性和性能改进。一个非常有影响力的附加功能是通过使用每个名称和每个类的假设来精确地使 Ruby 方法和常量失效。在加载 Ruby 代码时,由于 Ruby 的语义,会逐个添加方法,在加载文件时会执行大量的代码。根据每个类的假设(这是它在此更改之前在其他 Ruby 实现和 TruffleRuby 中的行为方式),它将使所有调用该类方法的调用站点无效。通过精确失效,它只会使下一次调用实际上需要调用另一种方法的调用站点失效。这改善了现实世界代码的预热。
随着这些更改,我们更新到了 Ruby 2.7.3,除了 resolvstdlib 没有更新(2.7.3 中的 resolv 有bug[11])。我们还添加了一个新的TruffleRuby::ConcurrentMap
数据结构,用于 ruby 并发。
有关许多其他更改和修复,请查看发行说明[12]。
七、Python
在这个版本中,我们实现了一个比纯 Python 版本更快的_pickle
,使序列化更快。
改进了对与其他语言的互操作性的支持,使dict
类型按照你期望的方式工作,现在使用 Truffle 哈希实现。
与往常一样,很多工作都集中在性能改进上,特别是在预热和共享引擎配置期间!
还有兼容性改进。查看发行说明或更多详细信息。
八、LLVM bitcode运行时
在21.2 中有一些与 C++ 相关的改进。我们修复了 LLVM 工具链在 MacOS 11.3上C工作不正常的问题。我们还通过跨语言互操作性添加了对 C 虚拟调用的支持。
GraalVM Enterprise 中的管理模式也得到了改善。我们将musl libc
更新到 1.2.2 版,并通过在管理模式[13]下添加对pthreads
和pthread
同步原语的支持,提高了与现有 native 代码库的兼容性。
九、FastR
FastR 继续致力于兼容性,在 2021-02-01 CRAN 快照中改进其对软件包的支持:
-
测试对 3.0.1 的部分支持。 -
对tibble 3.0.6、vctrs 0.3.6和data.table 1.13.6的主要支持。 -
对dplyr 1.0.3、ggplot 3.3.3和knitr 1.31的支持正在进行中。
如果你对特定软件包的支持感兴趣,请联系我们!
十、WebAssembly
你可以看到我们对 WebAssembly 运行时许多兼容性改进和一些错误修复,以通过对尽可能多的使用 WebAssembly 的 NPM 模块的测试。
十一、Java on Truffle
GraalVM 21.2 for Java on Truffle 中的一项重要内容是新的Hotswap 插件 API,它允许重新加载代码而无需重启正在运行的应用程序。该 API 旨在允许框架开发人员更好地控制反馈应用程序中的更改以响应 IDE 中的源代码编辑。它是通过设置适当的钩子来实现的,其中主要设计原则是你可以注册将在指定的 HotSwap 事件上触发的各种 HotSwap 侦听器。例如,当某个服务提供者的实现发生变化时,它能够重新运行静态初始值设定项,运行通用后热交换回调或钩子。
最令人惊奇的是这些API由普通的 Java 调用组成——比任何基于字节码操作的解决方案更容易集成和维护。
有关更多详细信息,请查看插件 API 文档[14]。
对 21.2 进行的另一个非常受欢迎的改进是更好的字节码调度,它可以将 Java 在 Truffle 上使用的解释器速度提高约15-30%。解释器速度是影响应用程序预热速度的一个非常重要的因素。
21.2 还改进了 Java 在 Truffle 对象上实现Map
,Map.Entry
,List
,Iterator
或Iterable
与其他语言(包 括 Java 主机代 码)的互操作性。
十二、工具
VisualVM 获得了许多出色的改进,包括对JDK 17 的支持以及允许从命令行或其他外部工具控制功能。后者可以在安装了 GraalVM 扩展的情况下实现VisualVM 和 VSCode 之间的无缝集成。你可以添加 IDE 配置集成 VisualVM 来分析你的应用程序。
VisualVM 现在还可以保存来自实时 Java 进程的 JFR 记录,并且在 Profiler 选项卡中有一个新的 Lock Contention 视图。因此,如果你现在使用 VisualVM 来满足日常分析需求,它比以往任何时候都更加强大。
十三、文档
最后但并非最不重要的一点是,我们重构了 GraalVM 官网上的文档。现在可以在 GitHub 上的主项目[15] 中找到它。
这意味着为 GraalVM 做出贡献并改善生态系统中许多其他开发人员的生活比以往任何时候都要容易。如果你注意到可能使用澄清或不完整描述你最喜欢的功能或调整选项的文档,请考虑分享你的专业知识并通 pull request 帮助项目!如果有疑问,这里有一个关于如何做到这一点的简短指南:参与文档贡献[16]。
请注意,GraalVM Enterprise 文档可在docs.oracle.com[17]上找到。
与每个版本一样,我们要感谢 GraalVM 社区的所有协作、反馈和讨论,以及分享你如何使用 GraalVM。
请不要犹豫,让我们知道任何和所有反馈!有多种渠道可以联系团队,选择最适合你的渠道:Slack、GitHub 或 Twitter,或者向我们发送邮件。
译者说:
大家好,我是 如梦技术春哥(mica 开源作者)感谢深夜还一起参与翻译和校对的张亚东(JustAuth 开源作者)、吴天狗、老叶等同学。我们已经输出和翻译了多篇 GraalVM 和 Spring Native 的文章:
文章列表:
翻译不易,请帮忙分享给更多的同学,谢谢!!!
参考资料
参阅文档: https://www.graalvm.org/release-notes/21_2/
[2]这两个测试去验证在 Native Image中的运行情况: https://medium.com/graalvm/gradle-and-maven-plugins-for-native-image-with-initial-junit-testing-support-dde00a8caf0b
[3]文档: https://www.graalvm.org/reference-manual/native-image/JCASecurityServices/
[4]Java 编写 JFR 事件: https://docs.oracle.com/en/java/javase/11/docs/api/jdk.jfr/jdk/jfr/Event.html
[5]文档: https://github.com/oracle/graal/blob/master/truffle/docs/TraversingCompilationQueue.md
[6]注释中: https://www.graalvm.org/release-notes/21_2/
[7]ExportLibrary.useForAOT: https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/library/ExportLibrary.html#useForAOT--
[8]AOT 教程: https://github.com/oracle/graal/blob/master/docs/graalvm-as-a-platform/truffle-framework/AOT.md#truffle-aot-tutorial
[9]新的 Set 方法: https://github.com/tc39/proposal-set-methods
[10]实验运算符重载支持: https://github.com/oracle/graaljs/blob/master/docs/user/OperatorOverloading.md
[11]bug: https://bugs.ruby-lang.org/issues/17748
[12]发行说明: https://github.com/oracle/truffleruby/releases/tag/vm-21.2.0
[13]管理模式: https://www.graalvm.org/reference-manual/llvm/NativeExecution/#limitations-and-differences-to-managed-execution-on-top-of-graalvm-enterprise
[14]插件 API 文档: https://www.graalvm.org/reference-manual/java-on-truffle/hotswap-plugin/
[15]GitHub 上的主项目: https://github.com/oracle/graal/tree/master/docs
[16]参与文档贡献: https://github.com/oracle/graal/tree/master/docs#readme
[17]docs.oracle.com: https://docs.oracle.com/en/graalvm/index.html