性能分析命令行工具

GraalVM **性能分析命令行工具** 可以帮助您通过分析 CPU 和内存使用情况来优化代码。

大多数应用程序在 20% 的代码中花费了 80% 的运行时间。因此,为了优化代码,必须了解应用程序在哪里花费了时间。在本节中,我们将使用一个示例应用程序来演示 GraalVM 提供的三种主要性能分析功能:CPU 跟踪器、CPU 采样器和内存跟踪器。

此示例应用程序使用基于 埃拉托斯特尼筛法 的基本素数计算器。

  1. 将以下代码复制到名为 primes.js 的新文件中

     class AcceptFilter {
         accept(n) {
             return true
         }
     }
    
     class DivisibleByFilter {
         constructor(number, next) {
             this.number = number;
             this.next = next;
         }
    
         accept(n) {
             var filter = this;
             while (filter != null) {
                 if (n % filter.number === 0) {
                     return false;
                 }
                 filter = filter.next;
             }
             return true;
         }
     }
    
     class Primes {
         constructor() {
             this.number = 2;
             this.filter = new AcceptFilter();
         }
    
         next() {
             while (!this.filter.accept(this.number)) {
                 this.number++;
             }
             this.filter = new DivisibleByFilter(this.number, this.filter);
             return this.number;
         }
     }
    
     var primes = new Primes();
     var primesArray = [];
     for (let i = 0; i < 5000; i++) {
         primesArray.push(primes.next());
     }
     console.log(`Computed ${primesArray.length} prime numbers. ` +
                 `The last 5 are ${primesArray.slice(-5)}.`);
    
  2. 运行 js primes.js。示例应用程序应按如下方式打印输出
     js primes.js
     Computed 5000 prime numbers. The last 5 are 48563,48571,48589,48593,48611.
    

    该程序需要一些时间来计算。接下来,您将检查时间花在哪里。

  3. 运行 js --cpusampler primes.js 以启用 CPU 采样。对于示例应用程序,CPU 采样器应按如下方式打印输出
     js --cpusampler primes.js
    
     Computed 5000 prime numbers. The last 5 are 48563,48571,48589,48593,48611.
     ----------------------------------------------------------------------------------------------
     Sampling Histogram. Recorded 250 samples with period 10ms.
       Self Time: Time spent on the top of the stack.
       Total Time: Time spent somewhere on the stack.
     ----------------------------------------------------------------------------------------------
     Thread[main,5,main]
      Name       ||             Total Time    ||              Self Time    || Location
     ----------------------------------------------------------------------------------------------
      accept     ||             2150ms  86.0% ||             2150ms  86.0% || primes.js~13-22:191-419
      next       ||             2470ms  98.8% ||              320ms  12.8% || primes.js~31-37:537-737
      :program   ||             2500ms 100.0% ||               30ms   1.2% || primes.js~1-46:0-982
     ----------------------------------------------------------------------------------------------
    

    默认情况下,采样器会为每个 JavaScript 函数打印一个执行时间直方图。

    您可以通过使用 --cpusampler=flamegraph 选项请求,生成 SVG 格式的火焰图

     js --cpusampler=flamegraph primes.js
    

    它应该生成一个名为 flamegraph.svg 的文件,其中包含以下内容

    Profiler Flamegraph

    您可以通过单击元素来放大图形。

    默认情况下,CPU 采样每 10 毫秒采样一次。从结果中,我们可以看到大约 89% 的时间花费在 DivisibleByFilter.accept 函数中。

     accept(n) {
         var filter = this;
         while (filter != null) {
             if (n % filter.number === 0) {
                 return false;
             }
             filter = filter.next;
         }
         return true;
     }
    

    有关更多详细信息,请参阅 这篇博客文章

    现在使用 CPU 跟踪器来收集每个语句的执行次数

  4. 运行 js primes.js --cputracer --cputracer.TraceStatements --cputracer.FilterRootName=*accept 以收集以 accept 结尾的方法中所有语句的执行次数
     js primes.js --cputracer --cputracer.TraceStatements --cputracer.FilterRootName=accept
     Computed 5000 prime numbers. The last 5 are 48563,48571,48589,48593,48611.
     -----------------------------------------------------------------------------------------
     Tracing Histogram. Counted a total of 468336895 element executions.
       Total Count: Number of times the element was executed and percentage of total executions.
       Interpreted Count: Number of times the element was interpreted and percentage of total executions of this element.
       Compiled Count: Number of times the compiled element was executed and percentage of total executions of this element.
     -----------------------------------------------------------------------------------------
      Name     |          Total Count |    Interpreted Count |       Compiled Count | Location
     -----------------------------------------------------------------------------------------
      accept   |     234117338  50.0% |        365660   0.2% |     233751678  99.8% | primes.js~15:245-258
      accept   |     117053670  25.0% |        182582   0.2% |     116871088  99.8% | primes.js~16-18:275-348
      accept   |     117005061  25.0% |        181001   0.2% |     116824060  99.8% | primes.js~19:362-381
      accept   |         53608   0.0% |          1829   3.4% |         51779  96.6% | primes.js~14:211-227
      accept   |         53608   0.0% |          1829   3.4% |         51779  96.6% | primes.js~13-22:191-419
      accept   |         48609   0.0% |          1581   3.3% |         47028  96.7% | primes.js~17:322-334
      accept   |          4999   0.0% |           248   5.0% |          4751  95.0% | primes.js~21:402-413
      accept   |             1   0.0% |             1 100.0% |             0   0.0% | primes.js~2-4:25-61
      accept   |             1   0.0% |             1 100.0% |             0   0.0% | primes.js~3:45-55
     -----------------------------------------------------------------------------------------
    

    输出显示了每个语句的执行计数器,而不是计时信息。跟踪直方图通常提供了对需要优化的算法行为的见解。

  5. 运行 js primes.js --experimental-options --memtracer 以显示源代码位置和报告分配的计数。请注意,用于捕获分配的内存跟踪器工具目前是 GraalVM 中的实验性功能。因此,--memtracer 必须在 --experimental-options 命令行选项之前。
     js primes.js --experimental-options --memtracer
     Computed 5000 prime numbers. The last 5 are 48563,48571,48589,48593,48611.
     ------------------------------------------------------------
     Location Histogram with Allocation Counts. Recorded a total of 5007 allocations.
       Total Count: Number of allocations during the execution of this element.
       Self Count: Number of allocations in this element alone (excluding sub calls).
    --------------------------------------------------------
     Name     |      Self Count |     Total Count | Location
    --------------------------------------------------------
     next     |     5000  99.9% |     5000  99.9% | primes.js~31-37:537-737
     :program |        6   0.1% |     5007 100.0% | primes.js~1-46:0-982
     Primes   |        1   0.0% |        1   0.0% | primes.js~25-38:424-739
    --------------------------------------------------------
    

    此输出显示了每个函数记录的分配数量。对于计算的每个素数,程序在 DivisibleByFilternextconstructor 中分配一个对象。分配记录是独立于它们是否可能被编译器消除的。

    GraalVM 编译器在优化分配方面特别强大,并且可以将分配推送到不常发生的分支中以提高执行性能。GraalVM 团队计划将来在内存跟踪器中添加有关内存优化的信息。

工具选项 #

在所有访客语言启动器中使用 --help:tools 选项以显示有关 CPU 采样器、CPU 跟踪器和内存跟踪器的参考信息。当前可用的选项集如下所示。

CPU 采样器选项 #

  • --cpusampler=true|false|<Output> : 启用/禁用 CPU 采样器,或使用特定输出启用 - 如输出选项所指定(默认值:false)。使用此选项选择输出默认为将输出打印到标准输出,除了火焰图以外,火焰图被打印到 flamegraph.svg 文件中。
  • --cpusampler.Delay=<ms> : 将采样延迟这么多毫秒(默认值:0)。
  • --cpusampler.FilterFile=<filter> : 源文件路径的通配符过滤器。(例如,program.sl)(默认值:无过滤器)。
  • --cpusampler.FilterLanguage=<languageId> : 仅分析具有给定 ID 的语言。(例如,js)(默认值:分析所有语言)。
  • --cpusampler.FilterMimeType=<mime-type> : 仅分析具有给定 MIME 类型的语言。(例如,application/javascript)(默认值:分析所有语言)
  • --cpusampler.FilterRootName=<filter> : 程序根的通配符过滤器。(例如,Math.*)(默认值:无过滤器)。
  • --cpusampler.GatherAsyncStackTrace=true|false : 尝试为每个样本收集异步堆栈跟踪元素(默认值:true)。禁用此选项可能会降低采样开销。
  • --cpusampler.GatherHitTimes : 为每个采样保存一个时间戳。
  • --cpusampler.MinSamples=[0, inf) : 如果元素的样本数少于此值,则将其从输出中删除(默认值:0)
  • --cpusampler.Output=histogram|calltree|json|flamegraph : 将输出格式指定为以下之一:直方图、调用树、JSON 或火焰图(默认值:直方图)。
  • --cpusampler.OutputFile=<path> : 将输出保存到给定文件。默认情况下,输出打印到输出流。
  • --cpusampler.Period=<ms> : 采样堆栈的毫秒周期(默认值:10)
  • --cpusampler.SampleContextInitialization : 启用对在上下文初始化期间执行的代码的采样
  • --cpusampler.ShowTiers=true|false|0,1,2 : 指定是否显示条目的编译信息。您可以指定 ‘true’ 以显示所有编译信息,‘false’ 以不显示,或指定以逗号分隔的编译层列表。注意:解释器被认为是第 0 层。(默认值:false)
  • --cpusampler.StackLimit=[1, inf) : 最大堆栈元素的最大数量(默认值:10000)。
  • --cpusampler.SummariseThreads : 将输出打印为所有 ‘每线程’ 配置文件的摘要。

CPU 跟踪器选项 #

  • --cputracer : 启用 CPU 跟踪器(默认值:false)。
  • --cputracer.FilterFile=<filter> : 源文件路径的通配符过滤器。(例如,program.sl)(默认值:无过滤器)。
  • --cputracer.FilterLanguage=<languageId> : 仅分析具有给定 ID 的语言。(例如,js)(默认值:无过滤器)。
  • --cputracer.FilterMimeType=<mime-type> : 仅分析具有 MIME 类型的语言。(例如,application/javascript)(默认值:无过滤器)。
  • --cputracer.FilterRootName=<filter> : 程序根的通配符过滤器。(例如,Math.*)(默认值:无过滤器)。
  • --cputracer.Output=histogram|json : 打印 ‘直方图’ 或 ‘JSON’ 作为输出(默认值:直方图)。
  • --cputracer.OutputFile=<path> : 将输出保存到给定文件。默认情况下,输出打印到标准输出流。
  • --cputracer.TraceCalls : 在跟踪时捕获调用(默认值:false)。
  • --cputracer.TraceRoots=true|false : 在跟踪时捕获根(默认值:true)。
  • --cputracer.TraceStatements : 在跟踪时捕获语句(默认值:false)。

内存跟踪器选项 #

内存跟踪器工具目前是实验性工具。确保在 --experimental-options 标志之前添加前缀以启用 --memtracer

  • --memtracer : 启用内存跟踪器(默认值:false)。
  • --memtracer.FilterFile=<filter> : 源文件路径的通配符过滤器。(例如,program.sl)(默认值:无过滤器)。
  • --memtracer.FilterLanguage=<languageId> : 仅分析具有给定 ID 的语言。(例如 js)(默认值:无过滤器)。
  • --memtracer.FilterMimeType=<mime-type> : 仅分析具有 MIME 类型的语言。(例如,application/javascript)。(默认值:无过滤器)
  • --memtracer.FilterRootName=<filter> : 程序根的通配符过滤器。(例如,Math.*)(默认值:无过滤器)。
  • --memtracer.Output=typehistogram|histogram|calltree : 打印 ‘typehistogram’、‘直方图’ 或 ‘调用树’ 作为输出。(默认值:直方图)
  • --memtracer.StackLimit=[1, inf) : 最大堆栈元素的最大数量。(默认值:10000)
  • --memtracer.TraceCalls : 在跟踪时捕获调用。(默认值:false)
  • --memtracer.TraceRoots=true|false : 在跟踪时捕获根。(默认值:true)
  • --memtracer.TraceStatements : 在跟踪时捕获语句(默认值:false)。

联系我们