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,可用于以文本形式输出性能分析信息,以便进一步分析。

有关完整详细信息,请参阅 性能分析器 文档。

联系我们