使用 GraalVM 提升 JUnit 性能

Boost JUnit performance using GraalVM

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

根据SO question "Does GraalVM JVM support java 11?",我在我的 eclipse(jee-2019-12,JUnit4)中的单元测试 运行 配置的 VM 参数中添加了以下内容:

-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler

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

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

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

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

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

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

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

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

  • 编译器不是最新的,在某些时候 GraalVM 编译器源被拉入 OpenJDK 项目,这就是它在您的发行版中结束的方式。
  • GraalVM 编译器是用 Java 编写的,在您的设置中它作为正常的 Java 代码执行,因此它首先可能需要自己进行 JIT 编译,这会导致更长的预热阶段运行,JIT 配置文件及其代码等受到一定程度的污染

在 GraalVM 发行版中,我为这个实验 encourage you to try -- GraalVM 编译器是最新的,默认情况下使用 GraalVM 本机图像技术预编译为共享库,所以在运行的时候不需要JIT编译,所以它的warmup更像C2的特性。

不过,2 秒可能不足以让优化编译器显示主要差异。也可能是一次测试运行很多代码,JIT编译的热代码体不够显着。