向 TVM 中添加虚拟机:Relay 虚拟 机
Relay 是一种新的程序表示形式,实现了很多机器学习程序的表示和优化。然而,在支持了一组更具表现力的程序的同时,也引入了几个新的执行挑战。
Relay 解释器可以执行完整的语言,但有明显的局限性——它不适合生产部署。它被构造为一个执行 AST 遍历(用来运行程序)的低效解释器。这种方法在概念上很简单,但效率很低,因为 AST 遍历严重依赖于间接层。
编译动态代码还存在其他挑战,例如动态调度和分配、完全动态的张量 shape 和控制流。解释器为它们提供了简单的解决方案,但没有一个是完美的。
第二种执行机制是已有的图执行器。为将 Relay 程序定位到这里,将它们中的一小部分编译为旧的计算图格式,并在 runtime 上执行。图执行器仅在非常有限的 Relay 程序子集上提供了快速的执行体验。
一种非标准的替代方法是 Relay 的 ahead-of-time 编译器,它将 Relay 程序编译为包含提前实现的共享库。ahead-of-time 编译器提供了较好的性能,但难以扩展和检测,只能通过修改代码生成和优化机制来实现。
Relay 虚拟机旨在成为一个平衡了这些竞争方法的框架,提供一个动态执行环境——它通过灵活的扩展机制与其他方法(如提前编译)进行扩展、检测和集成。
虚拟机是为了取得部署和执行 Relay 程序时性能和灵活性之间的平衡,同时不损失 TVM 的优势。
虚拟机(VM)设计是编程语言和系统中一个经过充分研究的领域,成熟的嵌入式编程语言都有多种虚拟机设计。以前的语言虚拟机设计针对传统程序的执行配置文件进行了大量适配。传统程序处理小的标量值,并由大量底层指令组成。
由于指令的数量很多,因此指令的执行和调度必须非常高效。机器学习的上下文中,主要用(相对)较少的高级指令来处理张量值。机器学习(ML)程序的 cost 中心是对大量输入的耗时算子的调用,比如 GEMM 或卷积。由于 ML 程序呈现的执行配置文件,标量虚拟机中的微优化显得没那么重要了。
TVM 很好地支持了视觉模型,但也希望能够支持更广泛的模型。图执行器能够利用输入图的完全静态特性,来执行积极的优化,比如完全静态分配,以及最佳内存重用。若引入的模型要利用控制流、递归、动态 shapes 和动态分配,就必须改变执行的工作方式。选择 Relay 虚拟机合情合理。
本文档的其余部分提供了 Relay 虚拟机设计及其指令集的高级概述。
设计
虚拟机的设计侧重于简单性,同时不牺牲性能。要实现这点,必须专注于设计张量虚拟机,而非标量虚拟机。
在张量虚拟机的设置中,进行了以下优化:对象的低成本「分配」(通过避免实际分配)、静态片段的重用,以及进行动态 shape 的能力(即锯齿张量)。
指令集
指令集和指令表示的选择是虚拟机最关键的设计决策。当前的指令表示是包含操作码和数据载荷的标记联合体。重要的设计决策是指令的抽象级别(RISC 与 CISC),以及获取数据的方式(固定宽度指令编码 vs. 可变长度编码)。当前版本更接近于 CISC,具有像 AllocTensor 这样的复杂指令,并且由于包含 shape 作为指令的一部分,因此其长度可变。当前的指令集的级别较高,基本和 Relay 中的高级操作对应。
Ret
参数:
RegName dst
RegName result
将 result
寄存器中的对象返回到调用者的 dst
寄存器。
InvokePacked
参数:
Index packed_index
Index arity
Index output_size
RegName* packed_args
调用 packed_index
表示的打包函数。arity
和 output_size
用于通知虚拟机预期有多少输入和输出。packed_args
存储了参数寄存器的列表。注意:Index
是 int64_t
的别名,在其他指令中也会用到。
AllocTensor
参数:
RegName dst
RegName storage
uint32_t ndim
int64_t* shape
DLDataType dtype
从给定的存储块 storage
分配一个张量值,这个张量值使用常量 shape(存储在 shape
中)和 dtype
。结果保存到 dst
寄存器中。
AllocTensorReg
参数:
RegName dst
RegName storage
RegName shape_register
DLDataType dtype
从给定的存储块(存储在 storage
中)分配适当的 shape 的张量值(存储在 shape_register
中)和 dtype
。结果保存到 dst
寄存器中。
AllocStorage
参数:
RegName dst
RegName size
RegName alignment
DLDataType dtype_hint