- 适用于 JDK 23 的 GraalVM(最新版)
- 适用于 JDK 24 的 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 语言安全点教程
- 单态化
- 分割算法
- 单态化用例
- 向运行时报告多态专业化
Truffle 解释器性能分析
用于分析使用 Truffle 编写的解释器的工具并不少。在 JVM 模式下运行时,您可以使用标准的 JVM 工具,例如 VisualVM、Java Flight Recorder 和 Oracle Developer Studio。在 Native Image 模式下运行时,您可以使用 Valgrind 工具套件中的 callgrind
以及其他系统工具,例如 strace
。作为在 GraalVM 上运行的语言,可以使用其他 GraalVM 工具。对于足够广泛的性能分析定义,您还可以使用 理想图可视化工具 (IGV) 和 C1 可视化工具来检查编译器输出。
本指南并非介绍如何使用每种工具,而是提供一些建议,假设您具备基本的工具使用知识,帮助您从这些工具中提取最有用的信息。
使用 CPU 采样器进行性能分析 #
对应用程序级别进行性能分析的最简单方法是使用 CPU 采样器,它是 /tools
套件的一部分,也是 GraalVM 的一部分。只需将 --cpusampler
传递给您的语言启动器即可。
language-launcher --cpusampler --cpusampler.Delay=MILLISECONDS -e 'p :hello'
您可能希望使用带有 --cpusampler.Delay=MILLISECONDS
的采样延迟,以便仅在预热后才开始性能分析。这样一来,您可以轻松识别哪些函数已编译,哪些函数未编译,但执行仍需要很长时间。
有关更多 --cpusampler
选项,请参阅 language-launcher --help:tools
。
从 CPU 采样器获取编译数据 #
CPU 采样器不会显示有关已编译代码中花费时间的任何信息。这至少部分是由于引入了多层编译,而“已编译代码”的描述不够精确。使用 --cpusampler.ShowTiers
选项,用户可以控制是否要查看编译数据,以及要指定报告中应考虑哪些确切的编译层。例如,添加 --cpusampler.ShowTiers=true
将显示执行期间遇到的所有编译层,如下所示。
-----------------------------------------------------------------------------------------------------------------------------------------------------------
Sampling Histogram. Recorded 553 samples with period 10ms.
Self Time: Time spent on the top of the stack.
Total Time: Time spent somewhere on the stack.
T0: Percent of time spent in interpreter.
T1: Percent of time spent in code compiled by tier 1 compiler.
T2: Percent of time spent in code compiled by tier 2 compiler.
-----------------------------------------------------------------------------------------------------------------------------------------------------------
Thread[main,5,main]
Name || Total Time | T0 | T1 | T2 || Self Time | T0 | T1 | T2 || Location
-----------------------------------------------------------------------------------------------------------------------------------------------------------
accept || 4860ms 87.9% | 31.1% | 18.3% | 50.6% || 4860ms 87.9% | 31.1% | 18.3% | 50.6% || ../primes.js~13-22:191-419
:program || 5530ms 100.0% | 100.0% | 0.0% | 0.0% || 360ms 6.5% | 100.0% | 0.0% | 0.0% || ../primes.js~1-46:0-982
next || 5150ms 93.1% | 41.7% | 39.4% | 18.8% || 190ms 3.4% | 100.0% | 0.0% | 0.0% || ../primes.js~31-37:537-737
DivisibleByFilter || 190ms 3.4% | 89.5% | 10.5% | 0.0% || 100ms 1.8% | 80.0% | 20.0% | 0.0% || ../primes.js~7-23:66-421
AcceptFilter || 30ms 0.5% | 100.0% | 0.0% | 0.0% || 20ms 0.4% | 100.0% | 0.0% | 0.0% || ../primes.js~1-5:0-63
Primes || 40ms 0.7% | 100.0% | 0.0% | 0.0% || 0ms 0.0% | 0.0% | 0.0% | 0.0% || ../primes.js~25-38:424-739
-----------------------------------------------------------------------------------------------------------------------------------------------------------
或者,--cpusampler.ShowTiers=0,2
将仅显示解释时间和在第二层已编译代码中花费的时间,如下所示。
-----------------------------------------------------------------------------------------------------------------------------------------
Sampling Histogram. Recorded 620 samples with period 10ms.
Self Time: Time spent on the top of the stack.
Total Time: Time spent somewhere on the stack.
T0: Percent of time spent in interpreter.
T2: Percent of time spent in code compiled by tier 2 compiler.
-----------------------------------------------------------------------------------------------------------------------------------------
Thread[main,5,main]
Name || Total Time | T0 | T2 || Self Time | T0 | T2 || Location
-----------------------------------------------------------------------------------------------------------------------------------------
accept || 5510ms 88.9% | 30.9% | 52.3% || 5510ms 88.9% | 30.9% | 52.3% || ../primes.js~13-22:191-419
:program || 6200ms 100.0% | 100.0% | 0.0% || 320ms 5.2% | 100.0% | 0.0% || ../primes.js~1-46:0-982
next || 5870ms 94.7% | 37.3% | 20.6% || 190ms 3.1% | 89.5% | 10.5% || ../primes.js~31-37:537-737
DivisibleByFilter || 330ms 5.3% | 100.0% | 0.0% || 170ms 2.7% | 100.0% | 0.0% || ../primes.js~7-23:66-421
AcceptFilter || 20ms 0.3% | 100.0% | 0.0% || 10ms 0.2% | 100.0% | 0.0% || ../primes.js~1-5:0-63
Primes || 20ms 0.3% | 100.0% | 0.0% || 0ms 0.0% | 0.0% | 0.0% || ../primes.js~25-38:424-739
-----------------------------------------------------------------------------------------------------------------------------------------
从 CPU 采样器创建火焰图 #
CPUSampler 生成的直方图输出可能非常大,难以分析。此外,由于其格式为平面格式,因此无法分析调用图,因为这些信息根本没有在输出中编码。火焰图显示了整个调用图。其结构使得查看应用程序时间花费在何处变得更加简单。
创建火焰图是一个多步骤过程。首先,我们需要使用 JSON 格式化程序对应用程序进行性能分析。
language-launcher --cpusampler --cpusampler.SampleInternal --cpusampler.Output=json -e 'p :hello' > simple-app.json
如果您要对内部源代码进行性能分析,例如标准库函数,请使用 --cpusampler.SampleInternal=true
选项。
JSON 格式化程序对调用图信息进行编码,而这些信息在直方图格式中是不可用的。但是,要从此输出创建火焰图,我们需要将其转换为一种格式,该格式将调用栈样本折叠为单行。可以使用 Benoit Daloze 的 FlameGraph 分支中的 stackcollapse-graalvm.rb 来完成此操作。
如果您尚未这样做,请将此 FlameGraph 分支 克隆到父目录中。现在,您可以运行脚本转换输出,并将其管道传输到将生成 SVG 数据的脚本中。
../FlameGraph/stackcollapse-graalvm.rb simple-app.json | ../FlameGraph/flamegraph.pl > simple-app.svg
此时,您应该在基于 Chromium 的网络浏览器中打开 SVG 文件。您的系统可能已将不同的图像处理应用程序配置为 SVG 文件的默认应用程序。虽然在这样的应用程序中加载文件可能会呈现一个图形,但它可能无法处理火焰图的交互式组件。Firefox 也可能有效,但基于 Chromium 的浏览器目前似乎对火焰图文件有更好的支持和性能。
使用 Oracle Developer Studio 进行性能分析 #
Oracle Developer Studio 包含一个 性能分析器,可与 GraalVM 一起使用。Developer Studio 可以从 OTN 下载,目前版本(12.6)为生产使用和商业应用程序开发提供了永久性的免费许可证。
使用 Developer Studio 性能分析器非常简单。将 Developer Studio 二进制文件的路径包含在 PATH
中,然后在正常的命令行前面添加 collect
。例如
collect js mybenchmark.js
完成后,将创建一个名为“experiment”(.er)的目录,其中包含该命令执行的性能分析数据,默认情况下为 test.1.er
。要查看性能分析结果,请使用 analyzer
工具。
analyzer test.1.er
analyzer
GUI 允许您以多种不同的方式查看捕获的性能分析信息,例如应用程序的时间线、平面函数列表、调用树、火焰图等。还有一个命令行工具 er_print
,可用于以文本形式输出性能分析信息,以便进一步分析。
有关完整详细信息,请参阅 性能分析器 文档。