- 适用于 JDK 23 的 GraalVM(最新)
- 适用于 JDK 24 的 GraalVM(抢先体验)
- 适用于 JDK 21 的 GraalVM
- 适用于 JDK 17 的 GraalVM
- 存档
- 开发版本
- Truffle 语言实现框架
- Truffle 分支仪器
- 动态对象模型
- 静态对象模型
- 解释器代码的主机优化
- Truffle 的函数内联方法
- 分析 Truffle 解释器
- Truffle 交互 2.0
- 语言实现
- 使用 Truffle 实现新语言
- Truffle 语言和仪器迁移到 Java 模块
- Truffle 本机函数接口
- 优化 Truffle 解释器
- 选项
- 栈上替换
- Truffle 字符串指南
- 特化直方图
- 测试 DSL 特化
- 基于 Polyglot 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
的节点结束的节点链(通过子连接以及直接调用节点到被调用者根节点的链接连接)。