- 适用于 JDK 24 的 GraalVM(最新)
- 适用于 JDK 25 的 GraalVM(早期访问)
- 适用于 JDK 21 的 GraalVM
- 适用于 JDK 17 的 GraalVM
- 存档
- 开发构建
- Truffle 语言实现框架
- Truffle 分支插桩
- 动态对象模型
- 静态对象模型
- 解释器代码的主机优化
- Truffle 函数内联方法
- 分析 Truffle 解释器
- Truffle 互操作 2.0
- 语言实现
- 使用 Truffle 实现新语言
- Truffle 语言和工具迁移到 Java 模块
- Truffle 原生函数接口
- 优化 Truffle 解释器
- 选项
- 栈上替换
- Truffle 字符串指南
- 特化直方图
- 测试 DSL 特化
- 基于多语言 API 的 TCK
- Truffle 编译队列方法
- Truffle 库指南
- Truffle AOT 概述
- Truffle AOT 编译
- 辅助引擎缓存
- Truffle 语言安全点教程
- 单态化
- 拆分算法
- 单态化用例
- 向运行时报告多态特化
辅助引擎缓存
以下文档介绍了 GraalVM 的辅助引擎缓存的工作原理。
此功能仅在 Oracle GraalVM 中可用。在 GraalVM Community Edition 中,这些选项不可用。
简介 #
Truffle 客户语言程序的预热可能需要大量时间。预热包括每次程序执行时重复进行的工作,直到达到峰值性能。这包括:
- 将客户应用程序加载并解析到 Truffle AST 数据结构中。
- 在解释器中执行和分析客户应用程序。
- 将 AST 编译为机器码。
在单个操作系统进程中,可以通过指定显式引擎来共享预热期间执行的工作。这要求语言实现禁用上下文相关的优化,以避免共享代码的上下文之间发生去优化。辅助引擎缓存基于禁用上下文相关优化的机制,并增加了将引擎及其 AST 和优化后的机器码持久化到磁盘的能力。这样,在新进程的第一个应用程序上下文中,预热期间执行的工作可以显著减少。
我们使用 SVM 辅助镜像功能来持久化和加载必要的数据结构到磁盘。持久化镜像可能需要相当长的时间,因为需要执行编译。然而,加载旨在尽可能快,通常几乎是即时的。这显著减少了应用程序的预热时间。
入门 #
从 Oracle GraalVM 安装开始,您首先需要(重新)构建一个具有辅助引擎缓存功能的镜像。例如,可以通过添加辅助引擎缓存功能来重新构建 JavaScript 镜像:
graalvm/bin/native-image --macro:js-launcher -H:+AuxiliaryEngineCache -H:ReservedAuxiliaryImageBytes=1073741824
--macro 参数值取决于客户语言。默认情况下,辅助镜像最大可达 1GB。最大大小可以根据需要增加或减少。保留的字节数实际上不会影响应用程序消耗的内存。在未来的版本中,当使用 --macro:js-launcher 宏时,辅助引擎缓存将默认启用。
重新构建 JavaScript 启动器后,该功能的使用方式如下:
创建一个新文件 fib.js:
function fib(n) {
   if (n == 1 || n == 2) {
       return 1;
   }
   return fib(n - 1) + fib(n - 2);
}
console.log(fib(32))
为了将分析运行的引擎持久化到磁盘,请使用以下命令行:
graalvm/bin/js --experimental-options --engine.TraceCache=true --engine.CacheStore=fib.image fib.js
--engine.TraceCache=true 选项是可选的,它允许您查看正在发生的事情。
输出如下:
[engine] [cache] No load engine cache configured.
2178309
[engine] [cache] Preparing engine for store (compile policy hot)...
[engine] [cache] Force compile targets mode: hot
[engine] [cache] Prepared engine in 1 ms.
[engine] [cache] Persisting engine for store ...
[engine] [cache] Persisted engine in 20 ms.
[engine] [cache] Detecting changes (update policy always)...
[engine] [cache]     New image contains         1 sources and  82 function roots.
[engine] [cache]     Always persist policy.
[engine] [cache] Writing image to fib.image...
[engine] [cache] Finished writing 1,871,872 bytes in 4 ms.
现在可以使用以下命令从磁盘加载引擎:
graalvm/bin/js --experimental-options --engine.TraceCache --engine.CacheLoad=fib.image fib.js
它会打印:
[engine] [cache] Try loading image './fib.image'...
[engine] [cache] Loaded image in 0 ms. 1,871,872 bytes   1 sources  82 roots
[engine] [cache] Engine from image successfully patched with new options.
2178309
[engine] [cache] No store engine cache configured.
由于无需预热应用程序,应用程序的执行时间应显著缩短。
用法 #
缓存存储和加载操作可以使用以下选项进行控制:
- --engine.Cache=<path>从/向- path加载和存储缓存的引擎。
- --engine.CacheStore=<path>将缓存的引擎存储到- path。
- --engine.CacheLoad=<path>从- path加载缓存的引擎。
- --engine.CachePreinitializeContext=<boolean>在镜像中预初始化一个新的上下文(默认- true)。
- --engine.TraceCache=<boolean>启用调试输出。
- --engine.TraceCompilation=<boolean>打印强制编译。
当使用 --engine.CacheCompile=<policy> 选项存储镜像时,可以强制编译根。支持的策略有:
- none: 不会持久化任何编译,并且现有编译将失效。
- compiled: 不会强制编译,但已完成的编译将被持久化。
- hot: 所有已启动的编译都将完成,然后持久化。(默认)
- aot: 所有已启动和 AOT 可编译的根都将被强制编译并持久化。
- executed: 所有已执行和所有 AOT 可编译的根都将被强制编译。
默认情况下,编译队列中所有已启动的编译都将完成,然后持久化。函数根是否可 AOT 编译由语言决定。语言通过实现 RootNode.prepareForAOT() 来支持 AOT。
如果同时设置了加载和存储操作,则可以使用 --engine.UpdatePolicy=<policy> 选项指定更新策略。可用策略有:
- always始终持久化。
- newsource如果加载了先前加载的镜像中不包含的新源,则存储。
- newroot如果加载了先前加载的镜像中不包含的新根,则存储。
- never从不持久化。
已知限制 #
- 
    对可持久化的应用程序类型通常没有限制。如果语言支持共享上下文策略,辅助引擎缓存应该可以工作。如果语言不支持,则不会持久化任何数据。 
- 
    持久化的辅助引擎镜像只能与创建它的 SVM 原生镜像一起使用。将引擎镜像与任何其他原生镜像一起使用将失败。 
- 
    每个原生镜像隔离区只能有一个活动的辅助镜像。同时尝试加载多个辅助镜像将失败。目前,辅助镜像也无法卸载,但计划在未来解除此限制。 
安全考虑 #
所有持久化到磁盘的数据仅表示代码,不包含应用程序上下文特定的数据,例如全局变量。但是,分析过的 AST 和代码可能包含 Truffle AST 中执行的优化产生的工件。例如,运行时字符串可能用于优化并因此持久化到引擎镜像中。
NativeImage 上的开发和调试 #
在 NativeImage 上运行辅助引擎缓存时,有几个选项对调试很有用:
- -XX:+TraceAuxiliaryImageClassHistogram持久化时打印镜像中所有对象的类直方图。
- -XX:+TraceAuxiliaryImageReferenceTree持久化时打印镜像中所有对象的类引用树。
HotSpot 上的开发和调试 #
调试 HotSpot 上与辅助镜像相关的语言实现问题可能很有用。在 JVM 模式下的 Oracle GraalVM 中,我们有额外的选项可用于帮助调试此功能的问题:由于 HotSpot 不支持存储部分堆,因此这些调试功能在 HotSpot 上不起作用。
- --engine.DebugCacheStore=<boolean>准备引擎进行缓存并将其存储到静态字段,而不是写入磁盘。
- --engine.DebugCacheLoad=<boolean>准备引擎使用存储在静态字段中的引擎,而不是从磁盘读取。
- --engine.DebugCacheCompile=<boolean>用于在持久化引擎之前强制编译已执行调用目标的策略。这支持与- --engine.CacheCompile相同的值。
- --engine.DebugCacheTrace=<boolean>启用引擎缓存调试功能的跟踪。
例如
js --experimental-options --engine.TraceCompilation --engine.DebugCacheTrace --engine.DebugCacheStore --engine.DebugCacheCompile=executed fib.js
打印以下输出:
[engine] opt done         fib                                                         |ASTSize            32 |Time   231( 147+84  )ms |Tier             Last |DirectCallNodes I    6/D    8 |GraalNodes   980/ 1857 |CodeSize         7611 |CodeAddress 0x10e20e650 |Source       fib.js:2
2178309
[engine] [cache] Preparing debug engine for storage...
[engine] [cache] Force compile targets mode: executed
[engine] [cache] Force compiling 4 roots for engine caching.
[engine] opt done         @72fa3b00                                                   |ASTSize             3 |Time   211( 166+45  )ms |Tier             Last |DirectCallNodes I    2/D    1 |GraalNodes   500/ 1435 |CodeSize         4658 |CodeAddress 0x10e26c8d0 |Source            n/a
[engine] opt done         :program                                                    |ASTSize            25 |Time   162( 123+39  )ms |Tier             Last |DirectCallNodes I    1/D    1 |GraalNodes   396/ 1344 |CodeSize         4407 |CodeAddress 0x10e27fd50 |Source       fib.js:1
[engine] opt done         Console.log                                                 |ASTSize             3 |Time    26(  11+15  )ms |Tier             Last |DirectCallNodes I    0/D    0 |GraalNodes    98/  766 |CodeSize         2438 |CodeAddress 0x10e285710 |Source    <builtin>:1
[engine] [cache] Stored debug engine in memory.
这允许快速迭代与编译相关的问题,并附加 Java 调试器。可以使用 --vm.Xdebug --vm.Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 附加 Java 调试器。
调试持久化引擎的加载更困难,因为 HotSpot 不支持将引擎写入磁盘。但是,可以使用多语言嵌入 API 在单元测试中模拟此用例。请参阅 com.oracle.truffle.enterprise.test.DebugEngineCacheTest 类作为示例。