Truffleブランチ・インストゥルメンテーション
Truffle上に実装されている言語では、AST実装に、通常、プロファイルなどの条件に基づいて高速および低速の実行パスが含まれていることが一般的です。これらの実行パスは、様々な条件付きブランチに編成されます。このようなケースでは、多くの場合、プログラムの実行によってこれらの各実行パスで実際にコードが実行されたかどうかを把握しておくと役立ちます。
ブランチ・インストゥルメンテーション機能は、ターゲット・メソッドにif
文をインストゥルメントして、実行中に取得されたブランチを追跡します。ブランチ・インストゥルメンテーションでこれを行うには、グローバル表に書き込むコードを使用してブランチをインストゥルメントします。この表には、ブランチごとに1つのエントリが含まれます。プログラムが終了すると、表の内容がデコードされ、読取り可能な形式で標準出力にダンプされます。
ブランチ・インストゥルメンテーションの動作を制御するフラグがいくつかあります。これらのフラグは、システム・プロパティとして指定されます:
--engine.InstrumentBranches
- インストゥルメンテーションがオンかどうかを制御します(true
またはfalse
。デフォルトはfalse
です)--engine.InstrumentFilter
- インストゥルメンテーションを実行する必要があるメソッドをフィルタ処理します(メソッド・フィルタ構文は、基本的に<package>.<class>.<method>[.<signature>]
です)--engine.InstrumentationTableSize
- インストゥルメントされる場所の最大数を制御します--engine.InstrumentBranchesPerInlineSite
- インストゥルメンテーションがゲスト言語関数/コンパイル・ユニットごとに個別のブランチ・プロファイルを提供するかどうかを制御します(デフォルトはfalse
です)。
使用例
プログラムでブランチ・インストゥルメンテーションを有効にする方法の例を次に示します。
Truffle言語実装でインストゥルメンテーションを使用してホット・ブランチまたは使用頻度の低いブランチを検出する場合は、通常、まず問題のあるメソッドを含む言語ノードを見つけます。次のコマンドは、SimpleLanguageのユニット・テストを実行し、すべてのif
文をインストゥルメントします:
mx --jdk jvmci sl --engine.BackgroundCompilation=false \
--engine.InstrumentBranches \
'--engine.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#)
この出力は、102行目のSLWhileRepeatingNode.java
ファイルのif
文で両方のブランチがアクセスされ、109行目のSLPropertyCacheNode.java
ファイルのif
文でtrue
ブランチのみがアクセスされたことを示しています。ただし、たとえば、この特定のSLPropertyCacheNode
ノードがどこから使用されたかは示されていません。同じexecute
メソッドを多数の異なるSimpleLanguageノードからコールできるため、これらの出現を区別する必要がある場合があります。したがって、per-inline-siteフラグをtrue
に設定し、SLPropertyCacheNode
にのみフォーカスするようにフィルタを変更します:
mx --jdk jvmci sl -Dgraal.TruffleBackgroundCompilation=false \
--engine.InstrumentBranchesPerInlineSite \
--engine.InstrumentBranches \
'--engine.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]
org.graalvm.compiler.truffle.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
注釈を持つメソッドへのコールサイトをインストゥルメントし、それらのメソッドへのコールをカウントします。これは、次のフラグ・セットによって制御されます:
--engine.InstrumentBoundaries
- インストゥルメンテーションがオンかどうかを制御します(true
またはfalse
。デフォルトはfalse
です)--engine.InstrumentFilter
- インストゥルメンテーションを実行する必要があるメソッドをフィルタ処理します(メソッド・フィルタ構文は、基本的に<package>.<class>.<method>[.<signature>]
です)--engine.InstrumentationTableSize
- インストゥルメントされる場所の最大数を制御します--engine.InstrumentBoundariesPerInlineSite
- インストゥルメンテーションがTruffle境界コールの宣言ごとに実行されるか(false
)、そのコールサイトがインライン化されたすべてのコール・スタックごとに実行されるか(true
)を制御します
このツールは、ブランチ・インストゥルメンテーション・ツールと一緒に使用できます。
たとえば、インライン化されていない、頻繁に出現するメソッドを見つける必要があるとします。Truffleコール境界を識別する通常のステップでは、まずInstrumentBoundariesPerInlineSite
フラグをfalse
に設定してプログラムを実行し、次に問題のあるメソッドを識別した後で、そのフラグをtrue
に設定し、それらのメソッドの特定のコール・スタックを識別するようにInstrumentFilter
を設定します。