Page 80 - 《软件学报》2025年第9期
P. 80
韩柳彤 等: 面向 RISC-V 向量扩展的高性能算法库优化方法 3991
然而, 尽管 SIMD 扩展支持的标量类型具有共性, 但在各种指令集架构上向量寄存器长度的不同导致其向量
类型差异较大. 以各指令集架构在 C 语言中的向量类型中为例, 对于 32 位单精度浮点数: 在向量寄存器长度为
256 位的 AVX2 指令集扩展中, 其向量类型是“__m256”, 表示包含 8 个单精度浮点数的向量寄存器; 在寄存器长度
为 128 位的 ARM Neon 指令集扩展中, 其向量类型是“float32x4_t”, 表示包含 4 个单精度浮点数的向量寄存器;
RISC-V 向量扩展较为特殊, 在指令集架构层面没有明确向量寄存器长度, 且支持将多个寄存器视为一组, 故其向
量类型的表达形式是“vfloat32m<LMUL>_t”, 表示由“LMUL”个物理向量寄存器组成的一组包含单精度浮点数的
向量寄存器, 且包含在其中的浮点数元素数量是不确定的.
硬件抽象层需要抽象不同平台上向量类型的差异, 才能为开发者提供通用的编程接口. 因此, 当目标硬件集
合的向量寄存器长度不一致时, 硬件抽象层应当仅抽象出多种向量类型中的共性部分, 即其包含的标量元素类
型, 而舍去元素个数等有差异的信息. 在上述单精度浮点数例子中, 抽象出的通用数据类型应当类似于“v_float32”,
其仅表示一个包含单精度浮点数元素的向量, 而该向量中的元素数量, 底层映射的物理寄存器个数等信息均被
隐去.
值得注意的是, 向量类型包含的标量元素类型和元素个数是向量编程模型中的重要信息, 可以视作向量类型
的元数据. 如果无法通过向量类型本身直接得到, 则应当提供相应接口, 允许开发人员获取相关元数据. 后文图 2
以单精度浮点数乘加 (single-precision A·X plus Y, saxpy) 算法为例, 说明向量类型元数据接口的必要性和设计要
点. 不难看出, 对于通过循环展开获得向量化机会的算法而言, 向量类型包含的标量元素个数即为循环展开次数
(或每次迭代处理的元素个数). 如后文图 2 中黄色标记所示, ARM Neon 和 x86 AVX2 的向量类型分别包含 4 个
和 8 个单精度浮点数, 而 RISC-V 向量扩展中则通过相关内建函数在运行时获得元素个数, 上述信息均被用作迭
代步长. 因此, 通用内建函数的向量类型应当根据编程语言特性, 以恰当形式提供类型相关的元数据, 如图 2(e) 所
示, 元数据 (元素个数) 作为类型成员, 可以被开发人员获取并使用.
3 面向 RISC-V 向量扩展的硬件抽象层实现方法
为了解决大量算法面向不同平台加速硬件的内建函数编程时面临的重复实现问题, OpenCV 实现了名为通用
内建函数的硬件抽象层. 在 OpenCV 算法库中, 所有具备向量化机会的算法都可以使用通用内建函数的编程接口
获得性能提升. 然而, 现有的通用内建函数仅为固定长度的 SIMD 后端架构设计, 与 RISC-V 向量扩展的可变长度
向量寄存器设计并不兼容.
本节首先介绍 OpenCV 通用内建函数的设计, 然后讨论现有设计与可变长体系结构不兼容的原因, 分析将
RISC-V 向量扩展视为定长 SIMD 后端实现通用内建函数面临的问题, 最终给出面向 RISC-V 向量扩展的计算机
视觉算法库硬件抽象层实现方法.
3.1 OpenCV 的通用内建函数实现
OpenCV 的通用内建函数抽象了诸多平台的向量操作和类型, 其支持的平台包括 Intel x86 SSE/AVX 系列扩
展指令集、ARM Neon 扩展指令集、龙芯 LoongArch LSX 系列扩展指令集、MIPS MSA 扩展指令集和 Power VSX
指令集扩展等. 通用内建函数使用上述指令集扩展在高级语言层面提供的内建函数, 面向各平台分别实现统一的
向量类型及操作.
3.1.1 向量类型
OpenCV 的硬件抽象层的向量类型覆盖了几乎所有常用的基本数据类型, 并根据不同平台的向量寄存器长度
分别封装, 形成向量类型. 如后文表 2 所示, 在 OpenCV 的发展历史上, 硬件抽象层最初被设计为面向 128 位固定
长度的向量扩展, 从而支持 SSE、MSA 和 VSX 扩展指令集; 而随着计算机体系结构的发展, 具有更强数据吞吐和
处理能力的 AVX2、AVX-512 扩展指令集被提出, OpenCV 硬件抽象层也随之封装了 256 位和 512 位的向量类
型, 并通过未知长度 (size-agnostic) 向量类型提供进一步的统一抽象.
通用向量类型不仅封装了不同平台内建函数的向量类型, 还包含向量元数据, 如向量所包含的元素类型和元

