- JDK 23用 GraalVM (最新)
- JDK 24用 GraalVM (早期アクセス)
- JDK 21用 GraalVM
- JDK 17用 GraalVM
- アーカイブ
- 開発ビルド
- Truffle 言語実装フレームワーク
- Truffle ブランチ計測
- 動的オブジェクトモデル
- 静的オブジェクトモデル
- インタプリタコードのホスト最適化
- Truffleの関数インライン化のアプローチ
- Truffleインタプリタのプロファイリング
- Truffle Interop 2.0
- 言語実装
- Truffleを使った新しい言語の実装
- Truffle言語とInstrumentsのJavaモジュールへの移行
- Truffleネイティブ関数インターフェース
- Truffleインタプリタの最適化
- オプション
- On-Stack Replacement
- Truffle文字列ガイド
- 特殊化ヒストグラム
- DSL特殊化のテスト
- Polyglot APIベースのTCK
- Truffleのコンパイルキューへのアプローチ
- Truffleライブラリガイド
- Truffle AOT概要
- Truffle AOTコンパイル
- 補助エンジンキャッシング
- Truffle言語セーフポイントチュートリアル
- 単態化
- 分割アルゴリズム
- 単態化のユースケース
- 多態特殊化のランタイムへの報告
Truffle ブランチ計測
Truffle上に実装された言語では、AST実装に、通常はプロファイルなどの条件に基づいて、高速と低速の実行パスが含まれていることが一般的です。 これらの実行パスは、異なる条件分岐に編成されます。 このような場合、プログラムの実行中にこれらの実行パスのそれぞれでコードが実際に実行されたかどうかを知ることが役立つことがよくあります。
ブランチ計測機能は、ターゲットメソッドの`if`文を計測して、実行中にどのブランチが実行されたかを追跡します。 ブランチ計測は、グローバルテーブルに書き込むコードでブランチを計測することでこれを実現します。 各ブランチはこのテーブルにエントリを持ちます。 プログラムが終了すると、テーブルの内容がデコードされ、読みやすい形式で標準出力にダンプされます。
ブランチ計測の動作を制御するフラグがいくつかあります。 これらのフラグはシステムプロパティとして指定されます。
- `--compiler.InstrumentBranches` - 計測をオンにするかどうかを制御します (`true` または `false`、デフォルトは `false`)。
- `--compiler.InstrumentFilter` - 計測を行うメソッドをフィルタリングします (メソッドフィルタ構文、基本的に `<package>.<class>.<method>[.<signature>]`)。
- `--compiler.InstrumentationTableSize` - 計測される場所の最大数を制御します。
- `--compiler.InstrumentBranchesPerInlineSite` - 計測がゲスト言語関数/コンパイル単位ごとに個別のブランチプロファイルを 제공するかどうかを制御します (デフォルトは `false`)。
使用例 #
プログラムでブランチ計測を有効にする方法の例を次に示します。
計測を使用してTruffle言語実装でホットまたは使用頻度の低いブランチを検出する場合、通常は問題のあるメソッドを持つ言語ノードを見つけることから始めます。 次のコマンドは、SimpleLanguageのユニットテストを実行し、すべての`if`文を計測します。
mx --jdk jvmci sl --engine.BackgroundCompilation=false \
--compiler.InstrumentBranches \
'--compiler.InstrumentFilter=*.*.*' \
../truffle/truffle/com.oracle.truffle.sl.test/src/tests/LoopObjectDyn.sl
次の出力が得られます。
Execution profile (sorted by hotness)
=====================================
0: *****************************************************
1: **************************
com.oracle.truffle.sl.nodes.access.SLPropertyCacheNode.namesEqual(SLPropertyCacheNode.java:109) [bci: 120]
[0] state = IF(if=36054#, else=0#)
com.oracle.truffle.sl.nodes.controlflow.SLWhileRepeatingNode.executeRepeating(SLWhileRepeatingNode.java:102) [bci: 5]
[1] state = BOTH(if=18000#, else=18#)
この出力は、`SLWhileRepeatingNode.java`ファイルの102行目の`if`文では両方のブランチが訪問され、`SLPropertyCacheNode.java`ファイルの109行目の`if`文では`true`ブランチのみが訪問されたことを示しています。 ただし、たとえば、この特定の`SLPropertyCacheNode`ノードがどこから使用されたかはわかりません。同じ`execute`メソッドは、多くの異なるSimpleLanguageノードから呼び出すことができ、これらの出現を区別したい場合があります。 したがって、inlineサイトごとのフラグを`true`に設定し、フィルタを変更して`SLPropertyCacheNode`のみに焦点を当てます。
mx --jdk jvmci sl -Djdk.graal.TruffleBackgroundCompilation=false \
--compiler.InstrumentBranchesPerInlineSite \
--compiler.InstrumentBranches \
'--compiler.InstrumentFilter=*.SLPropertyCacheNode.*' \
../truffle/truffle/com.oracle.truffle.sl.test/src/tests/LoopObjectDyn.sl
今回は、メソッド`namesEqual`が複数のサイトでインライン化されたため、より多くの出力が得られます(各サイトはインライン化チェーンで表されます)。 次の出力フラグメントは、最初に`if`文IDとその出現回数を含むヒストグラムを示しています。 その後、ブランチの正確なコールスタックと実行回数を示します。 たとえば、`[1]`の場合、`namesEqual`が`executeRead`から呼び出されると、`true`ブランチは`18018`回実行されます。 `namesEqual`が`executeWrite`(`[0]`)から呼び出されると、`true`ブランチは`18`回だけ実行されます。
Execution profile (sorted by hotness)
=====================================
1: ***************************************
2: ***************************************
0:
3:
com.oracle.truffle.sl.nodes.access.SLPropertyCacheNode.namesEqual(SLPropertyCacheNode.java:109) [bci: 120]
com.oracle.truffle.sl.nodes.access.SLReadPropertyCacheNodeGen.executeRead(SLReadPropertyCacheNodeGen.java:76) [bci: 88]
com.oracle.truffle.sl.nodes.access.SLReadPropertyNode.read(SLReadPropertyNode.java:71) [bci: 7]
com.oracle.truffle.sl.nodes.access.SLReadPropertyNodeGen.executeGeneric(SLReadPropertyNodeGen.java:30) [bci: 35]
com.oracle.truffle.sl.nodes.SLExpressionNode.executeLong(SLExpressionNode.java:81) [bci: 2]
com.oracle.truffle.sl.nodes.expression.SLLessThanNodeGen.executeBoolean_long_long0(SLLessThanNodeGen.java:42) [bci: 5]
com.oracle.truffle.sl.nodes.expression.SLLessThanNodeGen.executeBoolean(SLLessThanNodeGen.java:33) [bci: 14]
com.oracle.truffle.sl.nodes.controlflow.SLWhileRepeatingNode.evaluateCondition(SLWhileRepeatingNode.java:133) [bci: 5]
com.oracle.truffle.sl.nodes.controlflow.SLWhileRepeatingNode.executeRepeating(SLWhileRepeatingNode.java:102) [bci: 2]
com.oracle.truffle.runtime.OptimizedOSRLoopNode.executeLoop(OptimizedOSRLoopNode.java:113) [bci: 61]
com.oracle.truffle.sl.nodes.controlflow.SLWhileNode.executeVoid(SLWhileNode.java:69) [bci: 5]
com.oracle.truffle.sl.nodes.controlflow.SLBlockNode.executeVoid(SLBlockNode.java:84) [bci: 37]
com.oracle.truffle.sl.nodes.controlflow.SLFunctionBodyNode.executeGeneric(SLFunctionBodyNode.java:81) [bci: 5]
com.oracle.truffle.sl.nodes.SLRootNode.execute(SLRootNode.java:78) [bci: 28]
[1] state = IF(if=18018#, else=0#)
...
com.oracle.truffle.sl.nodes.access.SLPropertyCacheNode.namesEqual(SLPropertyCacheNode.java:109) [bci: 120]
com.oracle.truffle.sl.nodes.access.SLWritePropertyCacheNodeGen.executeWrite(SLWritePropertyCacheNodeGen.java:111) [bci: 244]
com.oracle.truffle.sl.nodes.access.SLWritePropertyNode.write(SLWritePropertyNode.java:73) [bci: 9]
com.oracle.truffle.sl.nodes.access.SLWritePropertyNodeGen.executeGeneric(SLWritePropertyNodeGen.java:33) [bci: 47]
com.oracle.truffle.sl.nodes.access.SLWritePropertyNodeGen.executeVoid(SLWritePropertyNodeGen.java:41) [bci: 2]
com.oracle.truffle.sl.nodes.controlflow.SLBlockNode.executeVoid(SLBlockNode.java:84) [bci: 37]
com.oracle.truffle.sl.nodes.controlflow.SLFunctionBodyNode.executeGeneric(SLFunctionBodyNode.java:81) [bci: 5]
com.oracle.truffle.sl.nodes.SLRootNode.execute(SLRootNode.java:78) [bci: 28]
[0] state = IF(if=18#, else=0#)
...
Truffle 呼び出し境界計測 #
Truffle 呼び出し境界計測ツールは、`TruffleCallBoundary`アノテーションを持つメソッドへの呼び出しサイトを計測し、それらのメソッドへの呼び出しをカウントします。 これは、次のフラグセットによって制御されます。
- `--compiler.InstrumentBoundaries` - 計測をオンにするかどうかを制御します (`true` または `false`、デフォルトは `false`)。
- `--compiler.InstrumentFilter` - 計測を行うメソッドをフィルタリングします (メソッドフィルタ構文、基本的に `<package>.<class>.<method>[.<signature>]`)。
- `--compiler.InstrumentationTableSize` - 計測される場所の最大数を制御します。
- `--compiler.InstrumentBoundariesPerInlineSite` - Truffle境界呼び出しの宣言ごとに計測を行うか(`false`)、またはその呼び出しサイトがインライン化されたすべてのコールスタックごとに計測を行うか(`true`)を制御します。
このツールは、ブランチ計測ツールと組み合わせて使用できます。
たとえば、インライン化されていない頻繁に発生するメソッドを見つける必要があるとします。 Truffle呼び出し境界を特定する通常の手順は、最初に`InstrumentBoundariesPerInlineSite`フラグを`false`に設定してプログラムを実行し、問題のあるメソッドを特定した後、そのフラグを`true`に設定し、`InstrumentFilter`を設定してそれらのメソッドの特定のコールスタックを特定することです。