- 適用於 JDK 23 的 GraalVM (最新)
- 適用於 JDK 24 的 GraalVM (搶先體驗)
- 適用於 JDK 21 的 GraalVM
- 適用於 JDK 17 的 GraalVM
- 封存
- 開發版本
- Truffle 語言實作框架
- Truffle 分支檢測
- 動態物件模型
- 靜態物件模型
- 針對直譯器程式碼的主機最佳化
- Truffle 函式內聯方法
- 分析 Truffle 直譯器
- Truffle Interop 2.0
- 語言實作
- 使用 Truffle 實作新語言
- Truffle 語言與工具移轉至 Java 模組
- Truffle 原生函式介面
- 最佳化 Truffle 直譯器
- 選項
- 堆疊上取代
- Truffle 字串指南
- 特化直方圖
- 測試 DSL 特化
- 基於多語 API 的 TCK
- Truffle 編譯佇列方法
- Truffle 函式庫指南
- Truffle AOT 概觀
- Truffle AOT 編譯
- 輔助引擎快取
- Truffle 語言安全點教學
- 單態化
- 分割演算法
- 單態化使用案例
- 向執行期回報多型特化
向執行期回報多型特化
本指南概述了語言實作者為了利用單態化(分割)策略所需的事項。關於其運作方式的更多資訊,請參閱分割指南。
簡單來說,單態化啟發法依賴於語言回報每個可能透過分割回到單態狀態的節點的多型特化。在此情境中,多型特化是指任何節點重寫導致節點改變其「多型程度」的狀況。這包括但不限於:啟用另一個特化、增加作用中特化的實例數量、排除特化等。
手動回報多型特化 #
為了方便回報多型特化,在 Node
類別中引入了一個新的 API:Node#reportPolymorphicSpecialize。此方法可用於手動回報多型特化,但僅限於無法透過使用 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
。由於此呼叫目標只有一個已知的呼叫者,您可以分析其在呼叫樹中的父系 (即呼叫者)。在此範例中,它是 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
。由於它只有一個呼叫者,因此您會查看該呼叫者 (Scheduler.queue
),由於已符合所有必要的條件,因此您會將其標記為「需要分割」。
將多型特化傾印至 IGV
請先閱讀分割指南,再閱讀本節,因為傾印的資料與分割的運作方式直接相關。
在執行您的客體語言程式碼時,將 --engine.SplittingDumpDecisions
引數新增至命令列,每次呼叫目標被標記為「需要分割」時,都會傾印一個圖表,顯示節點鏈 (透過子系連線以及直接呼叫節點至被呼叫者根節點連線連結),最終連結到呼叫 Node#reportPolymorphicSpecialize
的節點。