Page 83 - 《软件学报》2025年第9期
P. 83

3994                                                       软件学报  2025  年第  36  卷第  9  期



                                                                                   硬件抽象层
                                                       通用内建函数
                                             向量类型                    向量操作
                                             v_float32               v_fma(…)



                                                                                 目标平台检查
                                   cpu_avx.cpp           cpu_neon.cpp         cpu_rvv.cpp



                              x86 AVX 实现             ARM Neon 实现          RISC-V Vector 实现
                                    CV_AVX                CV_NEON              CV_RVV
                                  CV_SIMD256             CV_SIMD128           CV_SIMD128
                                    _mm256                float32x4_t         vfloat32m1_t

                               _mm256_fmadd_ps(…)        vfmaq_f32(…)       __riscv_vfmacc(…)

                                     图 3 通用内建函数到平台特定内建函数的映射过程示意图

                    更为棘手的是, RISC-V    向量扩展的向量寄存器长度在编译时是未知的, 存储其向量类型所需的空间也就无法
                 确定, 这种类型被称为无大小         (sizeless) 类型. 此种类型在实际使用中通常受到一定限制: 由于实例化对象或确定
                 栈帧布局过程中, 编译器需要准确计算出类的大小和类成员的偏移量, 而                       RISC-V  向量扩展的内建类型大小无法
                 在编译时确定. 因此, 类或结构体内不能声明这些类型的成员变量, 这与                    OpenCV  硬件抽象层的向量类型封装机制
                 不兼容. OpenCV  目前采用的替代方案是在类型封装中使用内存中的数组来存放数据, 同时增加                          RISC-V  向量类型
                 和通用内建函数向量类型之间的转换函数, 一定程度上解决了                     RISC-V  向量类型与包装类型不兼容的问题. 但两
                 种类型之间的转换不可避免地使用访存指令实现数组和寄存器的数据交换, 因而产生了冗余的访存指令: 在使用
                 通用内建函数时, 每个      RISC-V  向量类型  (数据在向量寄存器中) 到通用向量类型            (数据在内存数组中) 都需要执行
                 一次显式类型转换, 进而产生一条冗余的内存写入指令; 该对象需要再作为向量类型被使用时, 又会产生一次隐式
                 的由通用向量类型到        RISC-V  向量类型的类型转换, 进而产生一条冗余的内存读取指令; 此外, 如果在使用时将通
                 用向量类型的对象进行了赋值, 还会额外产生内存数组的拷贝操作. 由于访存指令的开销通常较大, 这些冗余操作
                 极大影响了执行性能, 甚至抵消了向量优化带来的性能提升, 以至于通用内建函数中固定长度版本的                                 RISC-V  向
                 量扩展实现的性能表现还低于未经任何向量优化的标量版本, 不具备应用价值.
                    上述问题均由      RISC-V  向量扩展中向量寄存器可变长的特性而引发, 可以预见同样具有可变寄存器长度特性
                 的  ARM SVE/SVE2  扩展也将遇到类似问题. 究其根本, 是由传统定长            SIMD  扩展发展演化而来的       OpenCV  硬件抽
                 象层设计无法适应新兴的可变长体系结构导致的.

                 3.3   面向  RISC-V  向量扩展的通用内建函数优化实现
                    将  RISC-V  向量扩展视作固定长度       SIMD  扩展将造成寄存器资源浪费, 并产生冗余访存指令, 没有产生应有
                 的向量优化效果. 因此, 本文使用所提出的面向             RISC-V  向量扩展的硬件抽象层设计方法, 优化了            OpenCV  的通用
                 内建函数的设计与实现. 新的通用内建函数可以兼容                 RISC-V  向量扩展的可变长特性, 并使用        RISC-V  向量扩展的
                 平台内建函数实现了其编程接口, 从而使得通用内建函数所编写的优化算法能够在拥有                             RISC-V  向量扩展的硬件
                 设备上获得性能提升.
                    与  OpenCV  现有定长的通用内建函数设计方案相比, 本文方法的主要差异如图                    4  所示. 首先, 在通用内建函数
   78   79   80   81   82   83   84   85   86   87   88