首页 > 解决方案 > 使用 GraalVM 提升 JUnit 性能

问题描述

阅读了一些关于新 GraalVM 的高级文章,并认为使用它来增强 JUnit 测试性能是一个好主意,特别是对于在分叉模式下运行的大型测试套件。

根据SO 问题“GraalVM JVM 是否支持 java 11?” 我在我的 Eclipse(jee-2019-12,JUnit4)中的单元测试运行配置的 VM 参数中添加了以下内容:

-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler

效果:单元测试比没有这些开关需要更长的时间(2800 毫秒,2200 毫秒,可重现)。

我错过了什么?还是我误解了 GraalVM 中增强启动时间的承诺?

标签: javajunitgraalvm

解决方案


是的,不幸的是,感觉这里有一些误解。我将在这里尝试详细说明有关性能和 GraalVM 的一些问题。

GraalVM是一个多语言运行时,它可以运行 JVM 应用程序,通常它通过运行 Java HotSpot VM(例如,与 OpenJDK JDK 相同)来运行 Java HotSpot VM(例如,与 OpenJDK JDK 相同),并将顶层优化即时 (JIT) 编译器替换为它自己的 GraalVM编译器。稍微简化一下,在运行期间,JVM 加载类文件,验证它们,开始解释它们,然后用一系列编译器编译它们,这些编译器往往从最快编译到最优化——所以你的应用程序运行的时间越长还有更多你使用相同的方法——它们被逐步编译成越来越好的机器代码。

GraalVM 编译器非常擅长优化代码,因此当您的应用程序运行足够的时间并开始工作时,结果通常比其他编译器显示的要好。这会带来更好的峰值性能,这对于您的中/长时间运行的工作负载非常有用。

您的单元测试运行需要 2 秒,这对于大量执行代码、收集配置文件和使用优化编译器来说确实不是那么多时间。也可能是特定的代码模式和工作负载非常适合 C2(默认 HotSpot 的顶级 JIT),因此很难变得更好。请记住,C2 是一种出色的 JIT,至少已经开发了 20 年,而且它的结果也非常非常好。

现在,GraalVM 还为您提供了另一种选择——GraalVM 原生图像,它允许您提前 (AOT) 将代码编译为不依赖 JVM 并且不会加载类、验证它们、初始化它们的原生二进制文件,所以启动这种二进制文件直到它开始做有用的“业务”工作要好得多。对于运行时间较短的工作负载或资源受限的环境,这是一个非常有趣的选择(二进制文件不需要进行 JIT 编译,因此它不需要资源,从而使运行时资源消耗更小)。但是,要使用这种方法,您需要使用 GraalVM 的本机映像实用程序编译您的应用程序,并且它可能需要比您在 2 秒内运行的工作负载更长的时间。

现在,在您描述的设置中,您没有使用 GraalVM 发行版,而是在您的 OpenJDK(我假设)发行版中启用 GraalVM 编译器。您指定的选项将 GraalVM 编译器打开为顶级 JIT 编译器。java与从 GraalVM 发行版运行的结果相比,有两个主要区别:

  • 编译器不是最新的,有时 GraalVM 编译器源会被拉入 OpenJDK 项目,这就是它在您的发行版中的结束方式。
  • GraalVM 编译器是用 Java 编写的,在您的设置中,它作为普通 Java 代码执行,因此它首先可能需要 JIT 编译自身,这会导致运行的预热阶段更长,其代码有点污染 JIT 配置文件等。

在 GraalVM 发行版中,我鼓励您尝试这个实验——GraalVM 编译器是最新的,并且默认情况下使用 GraalVM 本地映像技术预编译为共享库,因此在运行时它不会需要JIT编译,所以它的预热更类似于C2的特性。

尽管如此,2 秒可能不足以让优化编译器显示主要差异。也可能是测试一次运行大量代码,而 JIT 编译的热代码主体不够重要。


推荐阅读