
关于作者
张银于 2018 年获得中山大学计算机科学与技术学士学位。他现在是中国科学院软件研究所智能软件研究中心的硕士研究生。他的主要研究方向是编译技术,特别是 RISC-V ISA 的支持和应用。
这篇文章是谷歌暑期编程训练营 2020 项目的一部分。
OpenCV 在许多硬件平台上运行,并利用支持 SIMD(单指令多数据)加速的平台。今天我们将介绍如何将 OpenCV 移植到 RISC-V 并加速其性能。
什么是 RISC-V 以及为什么选择 RISC-V
来自 维基百科
RISC-V 是一种开放标准指令集架构 (ISA),基于已建立的精简指令集计算机 (RISC) 原则。与大多数其他 ISA 设计不同,RISC-V 在开源许可下提供,无需使用费。许多公司正在提供或已经宣布推出 RISC-V 硬件,支持 RISC-V 的开源操作系统也已可用,并且多个流行的软件工具链也支持该指令集。
换句话说,与其他一些流行的架构相比,RISC-V 具有免费/开源许可证,并且设计更加简洁。这些关键特性使其有可能在未来得到广泛应用。由于 OpenCV 的根源在于开放式软件和硬件,我们非常高兴将对 RISC-V 的支持引入该平台,并将继续改进它。
优化方法
OpenCV 提供了一种便捷的方法,可以将许多优化的内核一次性移植到新的 CPU 上,只要该 CPU 支持 SIMD/向量指令即可。我们为此使用了所谓的宽通用内联函数。到目前为止,宽通用内联函数支持多种 SIMD 指令集,例如 x86 和 x64 架构上的 SSE、AVX、AVX2、AVX512,ARM 架构上的 NEON,IBM Power 架构上的 VSX,以及 MIPS 架构上的 MSA。
作者完成的谷歌暑期编程训练营 2020 项目的目标是添加基于 RISC-V 向量扩展的宽通用内联函数实现,以便在 RISC-V 架构上实现向量加速。
RISC-V 的“V”(向量)扩展 (RVV) 是 RISC-V ISA 的标准扩展之一(实际上,RVV 扩展仍处于草案阶段)。它在基本 RISC-V ISA 中引入了向量寄存器和相应的向量指令,以便程序代码可以使用向量架构进行优化和加速。
在我们的代码中,我们使用 RISC-V 向量扩展的本机内联函数来访问其向量数据类型和向量操作。我们将它们封装到 OpenCV 的宽通用内联函数中。当 OpenCV 在 RISC-V 平台上编译并运行时,OpenCV 算法使用的宽通用内联函数将被转换为 RISC-V 向量指令。
OpenCV 中 RISC-V 加速的现状
目前,我们已经完成了 宽通用内联函数 RISC-V 版本的首次实现。第一个版本已成功由 RISC-V gnu 工具链 和 PLCT 提供的 rvv-llvm 版本编译。当使用 QEMU 在我们的模拟器上进行测试时,此版本已通过所有 HAL(硬件加速层)准确性测试和 11000 多个核心测试(约 99.8% 的核心测试)。
如何在 RISC-V 上编译和运行 OpenCV
要使用启用的 RISC-V RVV 优化构建 OpenCV,可以使用以下命令在运行在 X64 平台上的 Ubuntu(在 Ubuntu 18.04 上测试)上交叉编译 OpenCV。
1. 收集先决条件
apt-get update apt-get install gcc g++ git make cmake python python3 gcc-multilib vim autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev pkg-config libglib2.0-dev
2. 构建 RISC-V GNU 编译器工具链和 QEMU 模拟器
git clone [email protected]:riscv/riscv-gnu-toolchain.git -b rvv-intrinsic cd riscv-gnu-toolchain git submodule update --init --recursive ./configure --prefix=/opt/RISCV --with-arch=rv64gcv_zfh --with-abi=lp64d make linux -j$(nproc) make build-qemu -j$(nproc)
3. 为 RISC-V 构建 OpenCV
git clone [email protected]:opencv/opencv.git cd opencv mkdir build && cd build cmake -DCMAKE_TOOLCHAIN_FILE=../platforms/linux/riscv64-gcc.toolchain.cmake ../ make -j$(nproc)
完成上述命令后,您已成功为 RISC-V 编译了 OpenCV 库,并使用了 RVV。现在可以在 RISC-V 平台上运行代码。由于在撰写本文时没有合适的硬件,因此可以在刚刚构建的 QEMU 模拟器上运行它。使用以下命令在 QEMU 模拟器上运行准确性测试。
4. 在 QEMU 模拟器上运行准确性测试
/opt/RISCV/bin/qemu-riscv64 -cpu rv64,x-v=true opencv/build/bin/opencv_test_core
未来的工作
内存中的向量类型
宽通用内联函数框架最初是基于固定的向量长度设计的。我们今天基于 RISC-V 向量扩展的实现是使用 128 位固定向量长度创建的,但 RISC-V 向量扩展本身是可扩展的。因此,当前版本支持的通用内联函数的向量类型存储在内存中。这对性能有负面影响。
我们看到两种解决这个问题的方法:
- 设计一个新的宽通用内联函数框架,以适应与向量长度无关的架构。
- 在编译器端添加对 RVV 的不可扩展支持。
性能测试和优化
今天展示的实现已经通过了我们的准确性测试。但是,如上所述,此实现以及我们的通用内联函数可能不是最高效的。随着时间的推移,我们一定会进行更多性能测试,并引入进一步的优化。
当然,如果没有兼容的硬件来运行,即使是高度优化的代码也是无用的。RISC-V 社区正在积极努力完成 RISC-V RVV 规范,我们希望很快看到一些硬件支持。
参考资料和相关链接
- RISC-V 维基百科: https://en.wikipedia.org/wiki/RISC-V
- 实现的拉取请求: https://github.com/opencv/opencv/pull/18228
- 宽通用内联函数: https://docs.opencv.ac.cn/master/df/d91/group__core__hal__intrin.html
- OpenCV 官方仓库: https://github.com/opencv/opencv
- RISC-V ISA 规范: https://github.com/riscv/riscv-isa-manual
- RISC-V “V” 扩展规范: https://github.com/riscv/riscv-v-spec
- RVV 内联函数规范: https://github.com/riscv/rvv-intrinsic-doc
- RISC-V GNU 工具链: https://github.com/riscv/riscv-gnu-toolchain
- PLCT 集团的 Rvv-llvm: https://github.com/isrc-cas/rvv-llvm
- 内存中问题详细信息: https://github.com/riscv/riscv-gnu-toolchain/issues/701