Truffleブランチ・インストゥルメンテーション

Truffle上に実装されている言語では、AST実装に、通常、プロファイルなどの条件に基づいて高速および低速の実行パスが含まれていることが一般的です。これらの実行パスは、様々な条件付きブランチに編成されます。このようなケースでは、多くの場合、プログラムの実行によってこれらの各実行パスで実際にコードが実行されたかどうかを把握しておくと役立ちます。

ブランチ・インストゥルメンテーション機能は、ターゲット・メソッドにif文をインストゥルメントして、実行中に取得されたブランチを追跡します。ブランチ・インストゥルメンテーションでこれを行うには、グローバル表に書き込むコードを使用してブランチをインストゥルメントします。この表には、ブランチごとに1つのエントリが含まれます。プログラムが終了すると、表の内容がデコードされ、読取り可能な形式で標準出力にダンプされます。

ブランチ・インストゥルメンテーションの動作を制御するフラグがいくつかあります。これらのフラグは、システム・プロパティとして指定されます:

使用例

プログラムでブランチ・インストゥルメンテーションを有効にする方法の例を次に示します。

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#)

この出力は、102行目のSLWhileRepeatingNode.javaファイルのif文で両方のブランチがアクセスされ、109行目のSLPropertyCacheNode.javaファイルのif文でtrueブランチのみがアクセスされたことを示しています。ただし、たとえば、この特定のSLPropertyCacheNodeノードがどこから使用されたかは示されていません。同じexecuteメソッドを多数の異なるSimpleLanguageノードからコールできるため、これらの出現を区別する必要がある場合があります。したがって、per-inline-siteフラグをtrueに設定し、SLPropertyCacheNodeにのみフォーカスするようにフィルタを変更します:

mx --jdk jvmci sl -Dgraal.TruffleBackgroundCompilation=false \
  --compiler.InstrumentBranchesPerInlineSite \
  --compiler.InstrumentBranches \
  '--compiler.InstrumentFilter=*.SLPropertyCacheNode.*' \
  ../truffle/truffle/com.oracle.truffle.sl.test/src/tests/LoopObjectDyn.sl

今回は、namesEqualメソッドが複数のサイトでインライン化されているため、より多くの出力が表示されます(各サイトはインライン化チェーンで表されます)。次の出力フラグメントは、最初にif文のIDとその出現数を含むヒストグラムを示しています。次に、ブランチの正確なコール・スタックおよび実行数を示しています。たとえば、[1]では、namesEqualexecuteReadからコールされた場合、trueブランチが18018回取得されます。namesEqualexecuteWriteからコールされた場合([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注釈を持つメソッドへのコールサイトをインストゥルメントし、それらのメソッドへのコールをカウントします。これは、次のフラグ・セットによって制御されます:

このツールは、ブランチ・インストゥルメンテーション・ツールと一緒に使用できます。

たとえば、インライン化されていない、頻繁に出現するメソッドを見つける必要があるとします。Truffleコール境界を識別する通常のステップでは、まずInstrumentBoundariesPerInlineSiteフラグをfalseに設定してプログラムを実行し、次に問題のあるメソッドを識別した後で、そのフラグをtrueに設定し、それらのメソッドの特定のコール・スタックを識別するようにInstrumentFilterを設定します。