Truffleインタプリタのプロファイリング

Truffleを使用して記述されたインタプリタをプロファイルするツールは多数あります。JVMモードで実行する場合は、VisualVM、Javaフライト・レコーダ、Oracle Developer Studioなどの標準のJVMツールを使用できます。ネイティブ・イメージで実行する場合は、Valgrindツール・スイートのcallgrind、およびstraceなどの他のシステム・ツールを使用できます。GraalVMで実行する言語として、他のGraalVMツールを使用できます。プロファイリングを幅広く十分に定義するために、Ideal Graph Visualizer (IGV)およびC1ビジュアライザを使用してコンパイラ出力を調べることもできます。

このガイドでは、各ツールの使用方法に関する基本的な知識があることを前提として、ツールの使用方法ではなく、ツールから最も有用な情報を抽出するための提案を示します。

CPUサンプラでのプロファイリング

アプリケーション・レベルをプロファイルする最も簡単な方法は、たとえば、ほとんどの時間が費やされているゲスト言語関数を見つけるには、/toolsスイートおよびGraalVMの一部であるCPUサンプラを使用することです。単に--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では、解釈時間と階層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

ここで、SVGファイルをChromiumベースのWebブラウザで開く必要があります。システムによっては、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

完了すると、コマンド実行のプロファイリング・データを含む実験(.er)ディレクトリが作成されます(デフォルトではtest.1.er)。プロファイリング結果を表示するには、analyzerツールを使用します:

analyzer test.1.er

analyzer GUIを使用すると、アプリケーションのタイムライン、フラット関数リスト、コール・ツリー、フレーム・グラフなど、取得したプロファイリング情報を様々な方法で表示できます。さらに分析するためのコマンドライン・ツールer_printも用意されています。これを使用してプロファイリング情報をテキスト形式で出力できます。

詳細は、パフォーマンス・アナライザのドキュメントを参照してください。