ランタイムへの多相特殊化のレポート
このガイドでは、単相化(分割)戦略を活用するために言語インプリメンタに必要なものの概要を示します。この仕組みの詳細は、分割ガイドを参照してください。
簡単に言えば、単相化ヒューリスティックは、分割によって単相状態に戻される可能性がある各ノードの多相特殊化をレポートする言語に依存します。このコンテキストでの多相特殊化とは、ノードの多相度を変更するノードのリライトです。これには、別の特殊化のアクティブ化、アクティブな特殊化のインスタンス数の増加、特殊化の除外などが含まれますが、これらに限定されません。
多相特殊化の手動レポート
多相特殊化のレポートを容易にするために、Node#reportPolymorphicSpecializeという新しいAPIがNode
クラスに導入されました。このメソッドを使用して多相特殊化を手動でレポートできますが、DSLを使用してこれを自動化できない場合にかぎります。
多相特殊化の自動レポート
Truffle DSLでは特殊化間の遷移の多くが自動化されるため、多相特殊化の自動レポートのための@ReportPolymorphism
注釈が追加されました。この注釈は、DSLに対して、特殊化後の多相性のチェックを含め、必要に応じてNode#reportPolymorphicSpecialize
をコールするように指示します。
この注釈の使用方法の例では、com.oracle.truffle.sl.nodes.SLStatementNode
について考えてみます。これはすべてのSimpleLanguageノードのベース・クラスであり、ReportPolymorphism
注釈が継承されるため、単にこのクラスに注釈を付けることで、すべてのSimpleLanguageノードの多相特殊化のレポートが可能になります。この注釈をSLStatementNode
に追加する変更の差異を次に示します:
diff --git
a/truffle/src/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java
b/truffle/src/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java
index 788cc20..89448b2 100644
---
a/truffle/src/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java
+++
b/truffle/src/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java
@@ -43,6 +43,7 @@ package com.oracle.truffle.sl.nodes;
import java.io.File;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.ReportPolymorphism;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.GenerateWrapper;
import com.oracle.truffle.api.instrumentation.InstrumentableNode;
@@ -62,6 +63,7 @@ import com.oracle.truffle.api.source.SourceSection;
*/
@NodeInfo(language = "SL", description = "The abstract base node for all SL
statements")
@GenerateWrapper
+@ReportPolymorphism
public abstract class SLStatementNode extends Node implements
InstrumentableNode {
private static final int NO_SOURCE = -1;
多相特殊化の自動レポートの制御
特定のノードおよび特殊化の除外
言語のすべてのノードにReportPolymorphism
注釈を適用することは、単相化を容易にする最も簡単な方法ですが、これが必ずしも意味をなすとはかぎらない場合は、多相特殊化のレポートが発生する可能性があります。言語開発者が多相性のレポート対象としてどのノードおよびどの特殊化を考慮するかをより詳細に制御できるように、クラス(クラス全体の自動レポートを無効にする)または個々の特殊化(多相性のチェック時にこれらの特殊化を考慮から除外する)に適用可能な@ReportPolymorphism.Exclude
注釈が導入されました。
メガモフィック・ケースのみのレポート
バージョン20.3.0では、ReportPolymorphism.Megamorphicという新しい注釈が追加されました。この注釈は、単相化によって修正する必要があるコストの高い汎用特殊化で使用することを目的としているため、特殊化にのみ適用でき、その特殊化をメガモフィックとしてマークできます。この注釈を追加すると、注釈付きの特殊化がアクティブになった場合、ノードは他の特殊化の状態に関係なく、ランタイムに多相性をレポートします。
この注釈は@ReportPolymorphism
とは別に使用できます。つまり、メガモフィック注釈を機能させるために、ノードに@ReportPolymorphism
注釈を付ける必要はありません。両方の注釈が使用されている場合、多相とメガモフィックの両方のアクティブ化が多相性としてレポートされます。
ツールのサポート
多相特殊化をレポートする必要があるノードとレポートしないノードは、言語開発者が判断します。その基準は、ドメインの知識(多相の場合に言語のどのノードのコストが高いか)または実験(特定のノード/特殊化を含める/除外する効果の測定)のいずれかになります。言語開発者が多相特殊化のレポートの影響に関する理解を深めることを支援する、いくつかのツール・サポートが提供されています。
個々の分割のトレース
ゲスト言語コードの実行時に--engine.TraceSplitting
引数をコマンドラインに追加すると、ランタイムによって作成された各分割に関する情報がリアルタイムで出力されます。
フラグを有効にしてJavaScriptベンチマークのいずれかを実行した場合の出力のごく一部を次に示します。
...
[engine] split 0-37d4349f-1 multiplyScalar |ASTSize 40/ 40 |Calls/Thres 2/ 3 |CallsAndLoop/Thres 2/ 1000 |Inval# 0 |SourceSection octane-raytrace.js~441-444:12764-12993
[engine] split 1-2ea41516-1 :anonymous |ASTSize 8/ 8 |Calls/Thres 3/ 3 |CallsAndLoop/Thres 3/ 1000 |Inval# 0 |SourceSection octane-raytrace.js~269:7395-7446
[engine] split 2-3a44431a-1 :anonymous |ASTSize 28/ 28 |Calls/Thres 4/ 5 |CallsAndLoop/Thres 4/ 1000 |Inval# 0 |SourceSection octane-raytrace.js~35-37:1163-1226
[engine] split 3-3c7f66c4-1 Function.prototype.apply |ASTSize 18/ 18 |Calls/Thres 7/ 8 |CallsAndLoop/Thres 7/ 1000 |Inval# 0 |SourceSection octane-raytrace.js~36:1182-1219
...
分割サマリーのトレース
ゲスト言語コードの実行時に--engine.TraceSplittingSummary
引数をコマンドラインに追加すると、実行が完了した後に、分割に関して収集されたデータのサマリーが出力されます。これには、分割の数、分割予算の大きさと使用量、強制された分割の数、分割ターゲット名とその分割回数のリスト、および多相特殊化をレポートしたノードとその回数のリストが含まれます。
フラグを有効にしてJavaScriptベンチマークのいずれかを実行した場合の少し簡略化された出力を次に示します。
[engine] Splitting Statistics
Split count : 9783
Split limit : 15342
Split count : 0
Split limit : 574
Splits : 591
Forced splits : 0
Nodes created through splitting : 9979
Nodes created without splitting : 10700
Increase in nodes : 93.26%
Split nodes wasted : 390
Percent of split nodes wasted : 3.91%
Targets wasted due to splitting : 27
Total nodes executed : 7399
--- SPLIT TARGETS
initialize : 60
Function.prototype.apply : 117
Array.prototype.push : 7
initialize : 2
magnitude : 17
:anonymous : 117
add : 5
...
--- NODES
class ANode : 42
class AnotherNode : 198
class YetAnotherNode : 1
...
多相特殊化のトレース
ダンプされたデータは分割の仕組みに直接関連するため、この項の前に分割ガイドをご覧ください。
多相性のレポートが、分割対象として考慮されるコール・ターゲットにどのように影響するかについての理解を深めるには、--engine.SplittingTraceEvents
オプションを使用できます。このオプションにより、どのノードが多相性をレポートしているか、およびそれがコール・ターゲットにどのように影響しているかを詳細に示すログがリアルタイムで出力されます。次の例を参照してください。
例1
[engine] [poly-event] Polymorphic event! Source: JSObjectWriteElementTypeCacheNode@e3c0e40 WorkerTask.run
[engine] [poly-event] Early return: false callCount: 1, numberOfKnownCallNodes: 1 WorkerTask.run
このログ・セクションは、WorkerTask.run
メソッドのJSObjectWriteElementTypeCacheNode
が多相になり、これをレポートしたことを示しています。また、WorkerTask.run
が実行されるのはこれが初めて(callCount: 1
)であることも示しているため、「分割が必要」とマークしません(Early return: false
)
例2
[engine] [poly-event] Polymorphic event! Source: WritePropertyNode@50313382 Packet.addTo
[engine] [poly-event] One caller! Analysing parent. Packet.addTo
[engine] [poly-event] One caller! Analysing parent. HandlerTask.run
[engine] [poly-event] One caller! Analysing parent. TaskControlBlock.run
[engine] [poly-event] Early return: false callCount: 1, numberOfKnownCallNodes: 1 Scheduler.schedule
[engine] [poly-event] Return: false TaskControlBlock.run
[engine] [poly-event] Return: false HandlerTask.run
[engine] [poly-event] Return: false Packet.addTo
この例では、多相特殊化のソースはPacket.addTo
のWritePropertyNode
です。このコール・ターゲットの既知のコール元は1つのみであるため、コール・ツリーでその親(コール元)を分析できます。これは、この例ではHandlerTask.run
であり、これにも同じことが適用され、TaskControlBlock.run
につながり、同じトークンによってScheduler.schedule
につながります。Scheduler.schedule
のcallCount
は1です。つまり、これがその最初の実行であるため、「分割が必要」とマークしません(Early return: false
)。
例3
[engine] [poly-event] Polymorphic event! Source: JSObjectWriteElementTypeCacheNode@3e44f2a5 Scheduler.addTask
[engine] [poly-event] Set needs split to true Scheduler.addTask
[engine] [poly-event] Return: true Scheduler.addTask
この例では、多相特殊化のソースはScheduler.addTask
のJSObjectWriteElementTypeCacheNode
です。それを行うためのすべての基準が満たされているため、このコール・ターゲットはただちに「分割が必要」とマークされます。
例3
[engine] [poly-event] Polymorphic event! Source: WritePropertyNode@479cbee5 TaskControlBlock.checkPriorityAdd
[engine] [poly-event] One caller! Analysing parent. TaskControlBlock.checkPriorityAdd
[engine] [poly-event] Set needs split to true Scheduler.queue
[engine] [poly-event] Return: true Scheduler.queue
[engine] [poly-event] Set needs split to true via parent TaskControlBlock.checkPriorityAdd
[engine] [poly-event] Return: true TaskControlBlock.checkPriorityAdd
この例では、多相特殊化のソースはTaskControlBlock.checkPriorityAdd
のWritePropertyNode
です。コール元が1つのみであるため、そのコール元(Scheduler.queue
)を確認し、必要な基準がすべて満たされていると思われるため、「分割が必要」とマークします。
IGVへの多相特殊化のダンプ
ダンプされたデータは分割の仕組みに直接関連するため、この項の前に分割ガイドをご覧ください。
ゲスト言語コードの実行時に--engine.SplittingDumpDecisions
引数をコマンドラインに追加すると、コール・ターゲットが「分割が必要」とマークされるたびに、Node#reportPolymorphicSpecialize
というノードで終了するノードのチェーン(子接続および直接コール・ノードからコール先ルート・ノードへのリンクによってリンクされる)を示すグラフがダンプされます。