Skip to main content

OpenVML RVV

OpenVML 简介

OpenVML 是一套面向向量数据的数学函数库,提供浮点向量初等函数、二元运算、归约运算以及整数向量运算等能力。它的接口以批量数组为输入输出,例如 vsSqrtvsAddvsSumvdExpvs8Dot 等,可用于一次处理大量连续元素。

在机器人、感知算法、信号处理、机器学习前后处理等场景中,常见的数据流会包含大量逐元素运算,例如绝对值、平方根、指数、对数、三角函数、向量加减乘以及整型量化数据处理。相比在业务代码中手写标量循环,使用向量数学库可以把这些基础算子交给经过平台优化的实现,从而获得更稳定的吞吐表现。

RVV 加速

K3 平台搭载的 SpacemiT X100 处理器支持 RISC-V Vector Extension(RVV 1.0)。openvml-spacemit 安装在 /opt/openvml-spacemit,面向 K3 平台提供 OpenVML 算子实现。

项目openvml-spacemit
安装方式sudo apt install openvml-spacemit
头文件路径/opt/openvml-spacemit/include
库文件路径/opt/openvml-spacemit/lib
主要头文件openvml.h
主要库文件libopenvml.so
典型用途向量数学函数、浮点逐元素运算、整数向量运算与归约运算

使用示例

软硬件环境

  • SpacemiT RISCV64 X100 CPU(2.4 GHz)
  • Bianbu 4.0.1 操作系统
  • 内存:32 GB

安装必要依赖

sudo apt update
sudo apt install openvml-spacemit

测试代码

目录结构:

openvml_check/
├── CMakeLists.txt
└── openvml_check.cpp

CMakeLists.txt:

cmake_minimum_required(VERSION 3.16)
project(openvml_simple_check LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(OPENVML_PREFIX "/opt/openvml-spacemit" CACHE PATH "OpenVML install prefix")

find_path(OPENVML_INCLUDE_DIR
NAMES openvml.h
PATHS "${OPENVML_PREFIX}/include"
NO_DEFAULT_PATH)

find_library(OPENVML_LIBRARY
NAMES openvml
PATHS "${OPENVML_PREFIX}/lib"
NO_DEFAULT_PATH)

if(NOT OPENVML_INCLUDE_DIR)
message(FATAL_ERROR "openvml.h not found under ${OPENVML_PREFIX}/include")
endif()

if(NOT OPENVML_LIBRARY)
message(FATAL_ERROR "libopenvml not found under ${OPENVML_PREFIX}/lib")
endif()

get_filename_component(OPENVML_LIBRARY_DIR "${OPENVML_LIBRARY}" DIRECTORY)

add_executable(openvml_check openvml_check.cpp)
target_include_directories(openvml_check PRIVATE "${OPENVML_INCLUDE_DIR}")
target_compile_options(openvml_check PRIVATE -O3 -DNDEBUG)
target_link_libraries(openvml_check PRIVATE "${OPENVML_LIBRARY}")

if(OPENVML_LIBRARY_DIR)
set_target_properties(openvml_check PROPERTIES
BUILD_RPATH "${OPENVML_LIBRARY_DIR}"
INSTALL_RPATH "${OPENVML_LIBRARY_DIR}")
endif()

message(STATUS "OPENVML_PREFIX: ${OPENVML_PREFIX}")
message(STATUS "OPENVML_INCLUDE_DIR: ${OPENVML_INCLUDE_DIR}")
message(STATUS "OPENVML_LIBRARY: ${OPENVML_LIBRARY}")

openvml_check.cpp:

#include <openvml.h>

#include <algorithm>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <vector>

double max_abs_error(const std::vector<float>& x, const std::vector<float>& y) {
double err = 0.0;
for (size_t i = 0; i < x.size(); ++i) {
err = std::max(err, std::abs(static_cast<double>(x[i]) - static_cast<double>(y[i])));
}
return err;
}

int main() {
const VML_INT n = 8;
std::vector<float> x{0.25f, 1.0f, 2.25f, 4.0f, 6.25f, 9.0f, 12.25f, 16.0f};
std::vector<float> y{1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f};
std::vector<float> sqrt_out(n);
std::vector<float> add_out(n);
std::vector<float> sqrt_ref(n);
std::vector<float> add_ref(n);
float sum_out = 0.0f;

vsSqrt(n, x.data(), sqrt_out.data());
vsAdd(n, x.data(), y.data(), add_out.data());
vsSum(n, x.data(), &sum_out);

for (VML_INT i = 0; i < n; ++i) {
sqrt_ref[static_cast<size_t>(i)] = std::sqrt(x[static_cast<size_t>(i)]);
add_ref[static_cast<size_t>(i)] = x[static_cast<size_t>(i)] + y[static_cast<size_t>(i)];
}
const float sum_ref = std::accumulate(x.begin(), x.end(), 0.0f);

const double sqrt_err = max_abs_error(sqrt_out, sqrt_ref);
const double add_err = max_abs_error(add_out, add_ref);
const double sum_err = std::abs(static_cast<double>(sum_out) - static_cast<double>(sum_ref));

std::cout << std::fixed << std::setprecision(6);
std::cout << "OpenVML config: " << openvml_get_config() << '\n';
std::cout << "vsSqrt max_abs_error=" << sqrt_err << '\n';
std::cout << "vsAdd max_abs_error=" << add_err << '\n';
std::cout << "vsSum abs_error=" << sum_err << '\n';

if (sqrt_err > 1e-5 || add_err > 1e-5 || sum_err > 1e-5) {
std::cerr << "OpenVML result: FAIL - result mismatch\n";
return 1;
}

std::cout << "OpenVML result: PASS - linked OpenVML and computed correctly.\n";
return 0;
}

编译与运行

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
./build/openvml_check

终端输出:

也可以显式指定安装路径:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DOPENVML_PREFIX=/opt/openvml-spacemit
cmake --build build
LD_LIBRARY_PATH=/opt/openvml-spacemit/lib:$LD_LIBRARY_PATH ./build/openvml_check

更多性能测试数据

  • 使用 taskset -c 绑定 CPU,脚本同时设置 OMP_NUM_THREADS
  • vector_size 为 1048576,iterations 为 50,warmup 为 10。
  • avg_ms 表示单次算子调用的平均耗时,mevals_per_second 表示每秒百万次元素计算吞吐,checksum 用于确认输出参与计算且结果稳定。

测试结果简要说明

本次测试覆盖 81 个 OpenVML 算子,包括 float32float64int8 / int16 / int32 数据类型。

单核绑定测试结果

modulefunctionapiexpressiondtypeinput_sizeoutput_sizeeval_counttotal_msavg_msmevals_per_secondchecksum
unaryabsvsAbsy = abs(x)float321048576x11048576x11048576.000028.03530.56071870.09621572878.0765
unaryrecipvsRecipy = 1 / xfloat321048576x11048576x11048576.000098.78291.9757530.7475634956.4420
unarysqrtvsSqrty = sqrt(x)float321048576x11048576x11048576.000099.40841.9882527.40832796397.7431
unarysqrvsSqry = x * xfloat321048576x11048576x11048576.000026.66770.53341966.00323145759.1388
unarypow2o3vsPow2o3y = x^(2/3)float321048576x11048576x11048576.0000280.86585.6173186.66853995106.2436
unarypow3o2vsPow3o2y = x^(3/2)float321048576x11048576x11048576.0000276.82675.5365189.392226845443.5468
unaryexpvsExpy = exp(x)float321048576x11048576x11048576.000082.00621.6401639.32761149691.5115
unaryexpm1vsExpm1y = exp(x) - 1float321048576x11048576x11048576.0000613.641512.272885.4388101107.1236
unarylog10vsLog10y = log10(x)float321048576x11048576x11048576.0000589.452011.789088.9450807500.8625
unarylnvsLny = ln(x)float321048576x11048576x11048576.0000138.15102.7630379.50351859339.4487
unarylog1pvsLog1py = log(1 + x)float321048576x11048576x11048576.0000719.053414.381172.91362108088.9563
unarytanhvsTanhy = tanh(x)float321048576x11048576x11048576.00001031.756020.635150.8151-0.0011
unaryroundvsRoundy = round(x)float321048576x11048576x11048576.000026.17610.52352002.92560.0001
unaryceilvsCeily = ceil(x)float321048576x11048576x11048576.000026.24730.52491997.4920524292.1944
unaryfloorvsFloory = floor(x)float321048576x11048576x11048576.000028.13380.56271863.5518-524290.1941
unarysinvsSiny = sin(x)float321048576x11048576x11048576.0000113.46902.2694462.0541-0.0021
unarycosvsCosy = cos(x)float321048576x11048576x11048576.0000113.91442.2783460.2474953007.0083
unarytanvsTany = tan(x)float321048576x11048576x11048576.00001184.423423.688544.2653-0.0026
unaryasinvsAsiny = asin(x)float321048576x11048576x11048576.0000303.84966.0770172.54850.0006
unaryacosvsAcosy = acos(x)float321048576x11048576x11048576.0000396.69067.9338132.16551647112.5339
unaryatanvsAtany = atan(x)float321048576x11048576x11048576.0000289.22325.7845181.2745-0.0013
reductionsumvsSumy = sum(x)float321048576x11x11048576.000025.77940.51562033.74950.0344
binaryaddvsAddy = a + bfloat321048576x11048576x11048576.000062.58701.2517837.69481310730.4626
binarysubvsSuby = a - bfloat321048576x11048576x11048576.000061.20431.2241856.6199-1310730.4890
binarymulvsMuly = a * bfloat321048576x11048576x11048576.000061.19371.2239856.7680786439.7730
binarypowvsPowy = a ^ bfloat321048576x11048576x11048576.00001217.961624.359243.0463136978240.0024
binarypowxvsPowxy = a ^ bfloat321048576x11048576x11048576.0000276.72535.5345189.46152796397.7404
binaryatan2vsAtan2y = atan2(a, b)float321048576x11048576x11048576.0000245.46154.9092213.5927-122453.9317
reductiondotvsDoty = dot(a, b)float321048576x11x11048576.000056.92161.1384921.0701786433.1875
binaryoffsetvsOffsety = offset(a, b)float321048576x11048576x11048576.000027.98060.55961873.7580524292.1943
binaryscalevsScaley = scale(a, b)float321048576x11048576x11048576.000028.51320.57031838.7553-0.0032
pair_outputsincosvsSinCossin(x), cos(x)float321048576x11048576x21048576.0000624.671012.493483.9303953007.5056
unaryabsvdAbsy = abs(x)float641048576x11048576x11048576.000088.52671.7705592.23681572878.0829
unaryrecipvdRecipy = 1 / xfloat641048576x11048576x11048576.0000365.29987.3060143.5227634956.4308
unarysqrtvdSqrty = sqrt(x)float641048576x11048576x11048576.00001075.443121.508948.75092796397.7786
unarysqrvdSqry = x * xfloat641048576x11048576x11048576.000066.49731.3299788.43553145759.1658
unarypow2o3vdPow2o3y = x^(2/3)float641048576x11048576x11048576.00001987.495939.749926.37933995106.1786
unarypow3o2vdPow3o2y = x^(3/2)float641048576x11048576x11048576.00001986.163639.723326.397026845444.5869
unaryexpvdExpy = exp(x)float641048576x11048576x11048576.0000594.158411.883288.24041149691.5151
unaryexpm1vdExpm1y = exp(x) - 1float641048576x11048576x11048576.00001420.279328.405636.9144101107.1265
unarylog10vdLog10y = log10(x)float641048576x11048576x11048576.00001146.862222.937245.7150807500.8742
unarylnvdLny = ln(x)float641048576x11048576x11048576.0000606.584512.131786.43281859339.4754
unarylog1pvdLog1py = log(1 + x)float641048576x11048576x11048576.00001842.772236.855428.45102108088.9783
unarytanhvdTanhy = tanh(x)float641048576x11048576x11048576.00002713.289154.265819.32300.0000
unaryroundvdRoundy = round(x)float641048576x11048576x11048576.00001229.297724.586042.64940.0001
unaryceilvdCeily = ceil(x)float641048576x11048576x11048576.00001228.679424.573642.6709524290.1944
unaryfloorvdFloory = floor(x)float641048576x11048576x11048576.00001229.595924.591942.6391-524291.1942
unarysinvdSiny = sin(x)float641048576x11048576x11048576.00001655.067633.101431.67770.0000
unarycosvdCosy = cos(x)float641048576x11048576x11048576.00001612.783332.255732.5083953007.5061
unarytanvdTany = tan(x)float641048576x11048576x11048576.00002048.618540.972425.59230.0000
unaryasinvdAsiny = asin(x)float641048576x11048576x11048576.0000900.658518.013258.21160.0001
unaryacosvdAcosy = acos(x)float641048576x11048576x11048576.0000843.223316.864562.17661647112.5059
unaryatanvdAtany = atan(x)float641048576x11048576x11048576.00001833.677636.673628.59220.0000
reductionsumvdSumy = sum(x)float641048576x11x11048576.000067.16821.3434780.55950.0000
binaryaddvdAddy = a + bfloat641048576x11048576x11048576.0000119.39942.3880439.10451310730.4859
binarysubvdSuby = a - bfloat641048576x11048576x11048576.0000125.32012.5064418.3592-1310730.4857
binarypowvdPowy = a ^ bfloat641048576x11048576x11048576.00002072.601941.452025.2961136978237.8088
binarypowxvdPowxy = a ^ bfloat641048576x11048576x11048576.00001988.893939.777926.36082796397.7786
binaryatan2vdAtan2y = atan2(a, b)float641048576x11048576x11048576.00002898.757557.975218.0866-122453.8617
pair_outputsincosvdSinCossin(x), cos(x)float641048576x11048576x21048576.00003265.380565.307616.0560953007.5061
integeraddvs8Addy = int8(a) + int8(b)int81048576x11048576x11048576.000011.87770.23764414.0540-10.9028
integersubvs8Suby = int8(a) - int8(b)int81048576x11048576x11048576.00004.39320.087911934.2022-13.0971
integermulvs8Muly = int8(a) * int8(b)int81048576x11048576x11048576.000011.52180.23044550.3839144.0038
integershiftvs8Shifty = int8(a) << bint81048576x11048576x11048576.00002.97810.059617604.5274-24.0000
integerscalevs8Scaley = int8(a) * scalarint81048576x11048576x11048576.00002.58360.051720292.7597-35.9999
integeroffsetvs8Offsety = int8(a) + scalarint81048576x11048576x11048576.00002.76260.055318978.27022097156.7772
integerdotvs8Doty = dot(int8(a), int8(b))int81048576x11x11048576.00006.23160.12468413.3135144.0000
integeraddvs16Addy = int16(a) + int16(b)int161048576x11048576x11048576.000026.12170.52242007.0979-110.9992
integersubvs16Suby = int16(a) - int16(b)int161048576x11048576x11048576.000023.07460.46152272.1397-107.0000
integermulvs16Muly = int16(a) * int16(b)int161048576x11048576x11048576.000026.32940.52661991.2651-31661.6291
integershiftvs16Shifty = int16(a) << bint161048576x11048576x11048576.00006.05670.12118656.2753-217.9993
integerscalevs16Scaley = int16(a) * scalarint161048576x11048576x11048576.00005.22660.104510031.1215-326.9989
integeroffsetvs16Offsety = int16(a) + scalarint161048576x11048576x11048576.00005.97440.11958775.56112097059.7776
integerdotvs16Doty = dot(int16(a), int16(b))int161048576x11x11048576.000021.40460.42812449.4130-31661.0000
integeraddvs32Addy = int32(a) + int32(b)int321048576x11048576x11048576.000061.17721.2235856.99851797469.3806
integersubvs32Suby = int32(a) - int32(b)int321048576x11048576x11048576.000067.64261.3529775.0853-1797681.3804
integermulvs32Muly = int32(a) * int32(b)int321048576x11048576x11048576.000062.02211.2404845.3244-250.9997
integershiftvs32Shifty = int32(a) << bint321048576x11048576x11048576.000028.49340.56991840.0325-211.9998
integerscalevs32Scaley = int32(a) * scalarint321048576x11048576x11048576.000027.76040.55521888.6208-317.9997
integeroffsetvs32Offsety = int32(a) + scalarint321048576x11048576x11048576.000028.34000.56681849.99292097062.7773
integerdotvs32Doty = dot(int32(a), int32(b))int321048576x11x11048576.000053.67761.0736976.7358-251.0000