在本研究中,我们开发了一个名为向量-矩阵分析器(VMP)的 QEMU TCG 插件,以研究向量寄存器和矩阵寄存器之间数据移动的潜在影响。
我们将 POWER10 的 MMA 作为 RISC-V IME 实现应有的样子的代理,使用该插件对八个经过优化以使用 POWER10 矩阵运算的卷积神经网络(CNN)的执行过程进行插桩,并分析标量、矩阵和向量指令之间的交互。
结果表明,这类工作负载在向量寄存器和矩阵寄存器之间的交互非常少,这意味着所测试的两种卷积算法目前并未利用 IME 方法的这一特性。
收集到的数据还能让我们深入了解与矩阵数据交互的向量运算类型,这将有助于 AME 的实现,从而避免将数据发送回内存。
Implementation
POWER 是一种类 RISC 架构,拥有 32 个通用寄存器(GPR)、32 个浮点寄存器(FPR)和 64 个向量/标量寄存器(VSR)。
VSR 的宽度固定为 128 位,前 32 个寄存器与 FPR 共享高 64 位,如图所示。
Power ISA 3.1 版本引入了矩阵乘法辅助(MMA)功能,添加了一组类似 IME 的指令来执行秩为 k 的外积(k 最大为 8,具体取决于基础元素类型)。
这些指令将 128 位 VSR 作为输入,输出则放入 8 个 512 位累加器寄存器中的一个。
每个累加器都与一组 4 个 VSR 相关联,如下图所示。有关 POWER10 MMA 的更详细描述,请参见 Power ISA v3.1。
八个 CNN 模型通过基于 ONNX 的工具链进行编译,该工具链为 MLIR/LLVM 编译器输出 POWER10 MMA 代码。编译器使用两种卷积算法生成 POWER10 MMA 二进制文件:
b)一种基线算法,即 Im2Col+BLAS 实现,我们简称为 BLAS。关于该工具链的更多细节可在 SConv 论文中找到。
QEMU 是一款通用的机器模拟器和虚拟化工具。作为虚拟化工具时,QEMU 利用基于虚拟机监控程序的加速器(如 KVM 和 Xen ),让客户机代码直接在主机 CPU 上运行。
在模拟客户机 CPU 时,微型代码生成器(TCG)加速器会将客户机指令动态转换为主机可执行的代码。
QEMU TCG 还支持插件,这些插件提供了一个应用程序编程接口(API),可用于订阅客户机指令翻译和执行过程中的事件,从而实现对客户机代码的运行时插桩。
下图展示了 VMP 工具的整体流程。VMP 将编译工具链生成的 .so 文件作为输入,并加载 Python 脚本对每个模型进行推理。
使用 QEMU 的用户模式仿真来执行 Python 解释器,因此分析不包括模型编译、上下文切换或任何特权代码,但包括 ELF 加载器(ld*.so)、Python 解释器以及可能在运行时加载的其他库/模块(例如 libc、libz、numpy 等)。
每条翻译后的指令都会被分析,以确定其类别和访问的寄存器。为指令执行注册了一个回调,其中“用户数据”指针存储类别以及输入/输出寄存器。大致流程如下:
一个 64 元素的数组用于存储生成每个 VSR 值的指令类别。两个为每个类别各含一个元素的数组用于跟踪每个类别的指令数量和 VSR 写入次数。
一个二维数组统计 VSR 读取次数,其中一个维度对应源类别(即生成该值的指令类别),另一个维度对应目标类别(即读取该值的指令类别)。
类、输入和输出寄存器从“用户数据”指针中恢复,并且每个输入寄存器对应的源类和目标类对的计数器都会递增。然后,输出寄存器用于更新 VSR 数组。
该插件会记录对 VSR 的每次访问。对 VSR 的写入操作会更新插件状态,以指示是哪类指令生成了每个寄存器的值。
当另一条指令读取该寄存器的值时,插件会为源类和目标类这一对类别增加一个计数器。
通用用途:在通用寄存器(GPRs)和向量寄存器(VSRs)之间移动数据的指令。例如,使用 GPR 指定的右索引从 GPR 插入字(vinswrx);
浮点:关于浮点功能的说明,包括针对浮点寄存器(FPRs)的加载和存储操作。例如,加载双精度浮点数(lfd)、浮点乘加(fmadd)等;
立即数:加载立即数的指令,例如 VSX 向量扩展立即数字(xxspltiw);或根据立即数字段加载常量值的指令, 例如,加载 VSX 向量特殊值四字(lxvkq)等;
加载/存储:读取/写入向量标量寄存器(VSRs)的加载和存储指令。例如,存储向量索引(stvx)、加载 VSX 向量对(lxvp)等;
标量:属于向量-标量扩展(VSX)功能的指令,仅对向量标量寄存器(VSRs)的第一个元素进行操作。例如,VSX 标量乘加 A 型双精度(xsmaddadp)、VSX 标量除法单精度(xsdivsp)等;
转换:用于转换 VSR 中元素类型的指令。例如,VSX 向量转换 bfloat16 至单精度格式(非信号)(xvcvbf16spn)、VSX 标量转换双精度四舍五入为零至有符号字格式(xscvdpsxws);
矩阵:矩阵乘法辅助(MMA)功能的指令。例如,VSX 向量 16 位浮点 GER(xvf16ger2)、VSX 向量 64 位浮点 GER 正乘法、正累加(xvf64gerpp);
向量:向量功能或 VSX 功能的指令,但不包括归类在即时、加载/存储、转换、标量和矩阵类下的指令。例如,向量乘加浮点(vmaddfp)、向量每隔第 N 位收集(vgnb)、VSX 向量乘加 A 型双精度(xvmaddadp)等。
退出时,统计指令、读取和写入次数的数组会被写入 QEMU 日志。
Result
VMP 收集的数据以 Excel 文件形式提供。下图以热图形式展示了每个测试模型和算法的 VSR 读数的二维数组。从这些数据中可以得出以下观察结果:
矩阵指令读取的内容中,三分之一使用来自加载指令的值,三分之二访问由其他矩阵指令生成的值。
没有矩阵指令对向量指令的结果进行操作。除了“mnist-8”模型(该模型非常小)之外,矩阵指令占所有 VSR 读取的 80%~90%,其中读取其他矩阵指令结果的矩阵指令占总数的 50%~60%。
正如预期的那样,更大的模型对应的比例更高。与 BLAS 基准实现相比,SConv 对矩阵指令的读取次数始终更多,且矩阵指令的占比也更高;
矩阵运算结果的大多数读取(约 98%)是由其他矩阵指令完成的,其余读取(约 1%)由向量指令完成。
随着插件的进一步专业化,我们可以确定向量乘加指令(如 xvmaddasp)是唯一一种消耗矩阵指令生成的数据的向量指令。没有直接存储矩阵运算结果。
向量乘加指令在将向量状态寄存器(VSR)的值返回内存之前,总会对其进行处理,这在指令总数中所占比例极小(小于 5%)。
一些存储和浮点指令会从未初始化的寄存器(或不明来源)读取数据。
这可能与 ELFv2 ABI 的非易失性寄存器(即“被调用者保存的寄存器”)有关。少数对矩阵结果的浮点读取也可能是由非易失性浮点寄存器(f14 至 f31)引起的。
这些结果表明,与仅使用矩阵指令完成的计算量相比,所分析的工作负载中混合向量和矩阵的操作数量相对较少,这意味着为类 IME 架构优化的现有 AI/ML 软件的性能并不依赖于 IME 方法使用单一类型寄存器来处理矩阵和向量数据这一事实。
此外,观察到的唯一消耗矩阵数据的向量操作是向量乘加指令。
如果未来的 AME 扩展提供仅使用矩阵寄存器来执行此类操作的指令,那么这些算法的实现将不需要在不同类型的寄存器之间进行数据移动。
Conclusion
这些结果表明,与仅使用矩阵指令完成的计算量相比,所分析的工作负载中混合使用向量和矩阵的操作数量相对较少。具体而言,观察到:
矩阵指令读取的内容中,三分之一使用来自加载指令的值,三分之二访问由其他矩阵指令生成的值。没有矩阵指令对向量指令的结果进行操作。
除了非常小的模型外,矩阵指令占所有 VSR 读取的 80%~90%,其中读取其他矩阵指令结果的矩阵指令占总数的 50%~60%。
不出所料,对于更大的模型,这些百分比更高。
矩阵结果的大多数读取(约 98%)是由其他矩阵指令完成的,其余的读取(约 1%)是由向量乘加指令(例如 xvmaddasp)完成的;
没有直接存储矩阵运算结果。向量乘加指令在其值返回内存之前总是会处理向量状态寄存器,这在总指令数中所占比例很小(<5%)。
作者:Matheus Ferst / Guido Araujo
首图:主核(Kernyr)
审校:泽文(Zevorn)
图片、资料来源:
[1] Towards an Integrated Matrix Extension: Workload Analysis of CNN Inference with QEMU TCG Plugings
-
关注公众号,免费使用社区提供的 ima 知识库
现已推出:QEMU/GEM5/编译器/Linux