拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

rknn-toolkit2 中 rknn_matmul_api_demo 实战:在 Rockchip NPU 上执行静态与动态 shape 矩阵乘法

rknn-toolkit2 中 rknn_matmul_api_demo 实战:在 Rockchip NPU 上执行静态与动态 shape 矩阵乘法
  • 人工智能
  • 推理引擎
  • 模型量化
  • 模型优化
  • 边缘计算
  • 开发工具

【免费下载链接】rknn-toolkit2

项目地址:https://gitcode.com/gh_mirrors/rk/rknn-toolkit2
点击查看免费下载

本篇技术指南基于 rknn-toolkit2 仓库中 rknpu2/examples/rknn_matmul_api_demo 示例,讲解如何通过 Rockchip RKNN 运行时提供的 matmul C API 在 NPU 上直接执行矩阵乘法(C = A × B)。示例覆盖静态 shape 与动态 shape 两种模式、FP16/INT8/INT4 等多种量化数据类型组合,并附带 CPU 结果校验与性能测时功能。读者阅读后可以掌握 matmul API 的命令行参数含义、布局(layout)与量化参数的正确配置,以及如何在 Aarch64 Linux 与 Android 平台上编译、部署和运行此类 demo。

示例概览:两种执行模式与源码组成

rknn_matmul_api_demo是一个使用 matmul C API 在 NPU 上执行矩阵乘法的示例,包含静态 shape和动态 shape两种模式,对应两个可执行程序:

  • rknn_matmul_api_demo:静态 shape 模式,一次调用只处理一个固定的M × K × N形状;
  • rknn_matmul_api_dynshape_demo:动态 shape 模式,一次创建可依次切换多个 shape 并逐个执行,适用于推理场景中 batch(M)或维度经常变化的需求。

仓库中该示例的核心源码与构建文件如下:

  • src/rknn_matmul_api_demo.cpp:静态 shape 模式主程序,约 1300 行,包含参数解析、量化参数设置、数据生成、layout 转换、NPU 执行、CPU 结果对比;
  • src/rknn_matmul_api_dynshape_demo.cpp:动态 shape 模式主程序,通过rknn_matmul_create_dynamic_shape与rknn_matmul_set_dynamic_shape实现多 shape 支持;
  • src/matmul_utils.h 与 src/matmul_utils.cpp:提供随机数据生成、layout 转换(norm_layout_to_perf_layout、transposeB、transpose4bit)、结果比较(arraysEqual、arraysCosineSimilarity)等工具函数;
  • CMakeLists.txt:同时构建两个可执行文件,并安装到install/rknn_matmul_api_demo_<平台名>目录;
  • build-linux.sh 与 build-android.sh:分别为 Linux 与 Android 平台的交叉编译脚本。

底层 matmul C API 的完整定义位于 rknpu2/runtime/Linux/librknn_api/include/rknn_matmul_api.h,其中包含rknn_matmul_create、rknn_matmul_set_io_mem、rknn_matmul_run、rknn_matmul_set_core_mask、rknn_matmul_set_quant_params、rknn_matmul_set_dynamic_shape、rknn_matmul_destroy等接口,以及rknn_matmul_type、rknn_matmul_layout等枚举定义。

matmul_type:NPU 支持的矩阵乘法数据类型组合

无论是静态还是动态模式,第一个参数matmul_type都用于指定参与运算的 A、B、C 三个矩阵的数据类型组合。其取值在 rknn_matmul_api.h 中以枚举rknn_matmul_type定义,其中示例 README 与命令行帮助中可直接使用的前 5 种如下:

取值枚举名计算组合C 输出类型
1RKNN_FLOAT16_MM_FLOAT16_TO_FLOAT32FP16(A) × FP16(B)FP32
2RKNN_INT8_MM_INT8_TO_INT32INT8(A) × INT8(B)INT32
4RKNN_FLOAT16_MM_FLOAT16_TO_FLOAT16FP16(A) × FP16(B)FP16
7RKNN_FLOAT16_MM_INT4_TO_FLOAT32FP16(A) × INT4(B)FP32
10RKNN_INT4_MM_INT4_TO_INT16INT4(A) × INT4(B)INT16

从 rknn_matmul_api.h 可以看到,头文件中还定义了 README 未列出的其他组合:3 =RKNN_INT8_MM_INT8_TO_INT8、5 =RKNN_FLOAT16_MM_INT8_TO_FLOAT32、6 =RKNN_FLOAT16_MM_INT8_TO_FLOAT16、8 =RKNN_FLOAT16_MM_INT4_TO_FLOAT16、9 =RKNN_INT8_MM_INT8_TO_FLOAT32、11 =RKNN_INT8_MM_INT4_TO_INT32、12 =RKNN_FLOAT16_MM_INT4_TO_BFLOAT16、15 =RKNN_INT8_MM_INT4_TO_FLOAT16。两个 demo 主程序在校验时会检查取值范围:静态模式要求1 <= matmul_type <= 11(见 rknn_matmul_api_demo.cpp),动态模式同样要求1~11,超出范围会直接报错并打印用法。

对应每种类型,源码 中按类型分配了不同的缓冲区字节数:

  • INT4 参与的组合:A、B 均以 1 字节存储两个 4bit 值(A_type_bytes = B_type_bytes = 1),C 为int16_t;
  • INT8 组合:A、B 各 1 字节,C 为int32_t(INT8→INT8 组合例外,C 为int8_t);
  • FP16 组合:A、B 各 2 字节(float16),C 为float(FP16→FP16 组合 C 也为float16语义,但示例按float读取处理)。

静态 shape 模式:命令行参数与运行示例

静态 shape 模式的完整用法(来自 README_CN.md 与 print_usage 实现):

./rknn_matmul_api_demo <matmul_type> <M,K,N> <B_layout> <AC_layout> <loop_count> <core_mask> <print_result> <iommu_domain_id>

各参数含义如下:

参数默认值说明
matmul_type必填取值 1/2/4/7/10,含义见上文表格
M,K,N必填矩阵形状,逗号分隔。A 为M×K,B 为K×N,C 为M×N
B_layout0B 矩阵布局:0 = normal(普通逐行布局),1 = native(NPU 原生布局),2 = TP_NORM(转置后的普通布局N×K)
AC_layout0A 与 C 的布局:0 = normal,1 = perf(性能布局)
loop_count10重复执行rknn_matmul_run的次数,用于统计平均耗时与 FPS
core_mask0多核平台上的核掩码;单核平台调用rknn_matmul_set_core_mask会返回 -1,可忽略
print_result1非 0 时打印 A/B/C 张量内容
iommu_domain_id0IOMMU 域 ID,每个域拥有 4GB 地址空间,多域场景用于内存隔离

示例:A = [4,64]、B = [64,32] 的 INT8 矩阵乘法测试命令为:

./rknn_matmul_api_demo 2 4,64,32

即matmul_type=2(INT8×INT8→INT32),只传必填参数,其余走默认值。注意 README 中"运行"一节出现的./rknn_matmul_api_demo 2 4 64 32写法与源码实现并不一致——主程序 是用逗号切分argv[2]并严格要求 3 个数字,因此M,K,N必须使用逗号分隔,否则会报MKN splited by # must be 3 number!。

布局参数与 NPU 原生布局

B 矩阵三种布局在 rknn_matmul_api.h 中定义为RKNN_MM_LAYOUT_NORM = 0、RKNN_MM_LAYOUT_NATIVE = 1、RKNN_MM_LAYOUT_TP_NORM = 2。normal 布局下 B 直接按(K, N)逐行存放;native 布局则按 NPU 硬件分块结构[N1, K1, subN, subK]存放,示例中通过 rknn_B_normal_layout_to_native_layout 完成转换;TP_NORM 则是对[K, N]的 B 做转置成[N, K],示例通过 transposeB/transpose4bit 实现。A/C 的 perf 布局形如[K1, M, subK],由 norm_layout_to_perf_layout 完成转换。

量化参数:scale 与 zero point

对于带量化的数据类型(INT8、INT4 组合),主程序 预置了一组仅供测试使用的量化参数,真实业务需根据实际场景替换:

const float A_SCALE = 0.2f; // A 矩阵 scale const float B_SCALE = 0.1f; // B 矩阵 scale const float C_SCALE = 0.8f; // C 矩阵 scale const int A_ZP = 0; // A 矩阵 zero point const int B_ZP = 0; // B 矩阵 zero point const int C_ZP = 0; // C 矩阵 zero point

B 的量化类型由info.B_quant_type决定(见 rknn_matmul_api.h):

  • RKNN_QUANT_TYPE_PER_CHANNEL_SYM(逐通道对称量化):对RKNN_FLOAT16_MM_INT8_TO_FLOAT32、RKNN_FLOAT16_MM_INT4_TO_FLOAT32、RKNN_INT8_MM_INT4_TO_INT32等类型自动设置,scale 数组长度为 N;
  • RKNN_QUANT_TYPE_PER_GROUP_SYM(逐组对称量化):对RKNN_FLOAT16_MM_INT4_TO_FLOAT16、RKNN_INT8_MM_INT8_TO_FLOAT32等类型设置,group_size = 32,scale 数组长度为N × K / group_size;
  • A 与 C 只支持逐层(per layer)量化。

平台对齐限制

在 NPU 上执行 matmul 时,K、N 维度有硬件对齐要求。依据 rknn_matmul_api.h 与 set_io_mem 接口注释,不同平台的限制如下:

平台K 对齐要求N 对齐要求
RK3566/RK3568INT8 对齐 32 字节,FP16 对齐 16 字节INT8 对齐 16 字节,FP16 对齐 8 字节
RK3562INT8 对齐 32 字节,FP16 对齐 32 字节INT8 对齐 16 字节,FP16 对齐 8 字节
RK3588/RK3576INT8 对齐 32 字节,FP16 对齐 32 字节,INT4 对齐 32 字节INT8 对齐 32 字节,FP16 对齐 16 字节,INT4 对齐 64 字节

同时 K 的最大值限制为k <= 10240。示例默认参数4,64,32满足上述对齐要求,可直接运行。

动态 shape 模式:多 shape 切换与两种数据流

动态 shape 模式的完整用法(来自 README_CN.md 与 print_usage 实现):

./rknn_matmul_api_dynshape_demo <matmul_type> <M1K1N1#M2K2N2#...> <B_layout> <AC_layout> <loop_count> <core_mask>

各参数含义:

参数默认值说明
matmul_type必填同静态模式,取值 1/2/4/7/10
M1K1N1#M2K2N2#...必填shape 数组,多个 shape 之间用#分隔,每个 shape 内M,K,N用逗号分隔
B_layout00 = normal,1 = native,2 = TP_NORM
AC_layout00 = normal,1 = perf
loop_count10每个 shape 的执行次数
core_mask0多核平台核掩码

示例:A 依次取[1,64]、[4,64]、[8,64],B 固定为[64,32],INT8 矩阵乘法测试命令:

# feature+const:M 变化、B 作为常量权重 ./rknn_matmul_api_dynshape_demo 2 1,64,32#4,64,32#8,64,32 1 1 # two feature:A、B 均作为特征输入(B_layout 取 2) ./rknn_matmul_api_dynshape_demo 2 1,64,32#4,64,32#8,64,32 2 1

从 dynshape 主程序 的实现可以看到两点关键约束:

  1. 动态 shape 模式下info.M/K/N取所有 shape 中的最大值(maxM/maxK/maxN),用于按最大形状一次性分配 A/B/C 缓冲区(代码);
  2. 当B_layout为 0 或 1 时,要求所有 shape 的 K、N 完全相同,只有 M 可以变化(B 矩阵不参与动态切换,只需set_io_mem一次);只有B_layout = 2(TP_NORM)时 K、N 才能随 shape 变化。

动态 shape 的调用链与静态模式的区别在于:使用rknn_matmul_create_dynamic_shape一次创建多个 shape 的上下文,并在每次执行前调用rknn_matmul_set_dynamic_shape(ctx, &shapes[s])切换到目标 shape,再按该 shape 对应的io_attr[s]依次set_io_mem与run(见 源码)。源码注释特别强调:"rknn_matmul_set_io_mem must call after rknn_matmul_set_dynamic_shape",即必须先设置动态 shape 再绑定 IO 内存。

源码中的完整调用流程:从 create 到 destroy

把两个 demo 主程序综合起来,matmul C API 的标准调用流程可归纳为 8 步(对应 rknn_matmul_api_demo.cpp 主函数):

  1. 构造rknn_matmul_info:填充M/K/N、type、B_layout、AC_layout、iommu_domain_id,并按类型设置B_quant_type与group_size(代码);
  2. 创建上下文:rknn_matmul_create(&ctx, &info, &io_attr),返回后io_attr携带 A/B/C 的实际张量属性(名称、维度、size、类型)(代码);
  3. (可选)设置核掩码:rknn_matmul_set_core_mask(ctx, mask),单核平台会返回 -1,按平台决定是否调用;
  4. 设置量化参数:rknn_matmul_set_quant_params,仅量化类型需要,且与B_quant_type匹配(逐通道 / 逐组 / 逐层三种路径,见 代码);
  5. 生成输入数据并按布局写入内存:随机生成 A/B 数据(FP16 类型取值范围[-1,1],INT8 类型[-128,127],INT4 类型[-8,7]),根据B_layout/AC_layout选择 normal 拷贝、native 转换或转置;INT4 数据通过 set_mem_from_int8_to_int4 打包为每字节两个 4bit 值;
  6. 绑定 IO 内存:rknn_create_mem按io_attr中的 size 分配 A/B/C,再用rknn_matmul_set_io_mem绑定(代码);
  7. 循环执行与测时:rknn_matmul_run(ctx)循环loop_count次,用gettimeofday计算单次耗时,输出Elapse Time(ms)与FPS,最后给出平均值(代码);
  8. 结果校验与释放:将 NPU 输出与 CPU 侧朴素三重循环矩阵乘法结果对比(arraysEqual精确相等或arraysCosineSimilarity余弦相似度),然后依次rknn_destroy_mem、rknn_matmul_destroy并释放宿主缓冲区(代码)。

其中"结果校验"是示例最实用的部分:当程序打印xxx matmul result is correct M x K x N is ...时,说明 NPU 计算结果与 CPU 参考实现一致,可用于验证布局转换与量化参数配置是否正确。

Aarch64 Linux 编译、安装与运行

编译

Linux 平台使用 build-linux.sh 交叉编译。首先导入交叉编译器路径:

export GCC_COMPILER=~/opt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu

然后执行:

./build-linux.sh -t <target> -a <arch> -b <build_type> # 例如: ./build-linux.sh -t rk3588 -a aarch64 -b Release

各选项说明(依据 build-linux.sh):

  • -t:目标芯片,支持rk3562、rk3566/rk3568(可用rk356x、rk3566、rk3568写法)、rk3576、rk3588、rv1126b;
  • -a:架构,aarch64或armhf;
  • -b:构建类型,Debug或Release,默认Release。

脚本内部会依据架构从 rknpu2/runtime/Linux/librknn_api 选择对应的librknnrt.so(aarch64 或 armhf),构建产物通过 CMakeLists.txt 安装到install/rknn_matmul_api_demo_Linux目录(含两个可执行文件与lib/librknnrt.so)。

安装

将install/rknn_matmul_api_demo_Linux整个目录拷贝到目标设备上:

  • 若使用 Rockchip 的 EVB 板,可用 adb 将程序传输到/userdata:
adb push install/rknn_matmul_api_demo_Linux /userdata/
  • 若板子有 sshd 服务,也可使用scp或其他方式将程序传输到板子上。

运行

在板子上进入目录并设置动态库路径:

adb shell cd /userdata/rknn_matmul_api_demo_Linux/ export LD_LIBRARY_PATH=./lib

执行静态 shape 测试(INT8 类型,A=[4,64],B=[64,32]):

./rknn_matmul_api_demo 2 4,64,32

或执行动态 shape 测试:

export LD_LIBRARY_PATH=./lib ./rknn_matmul_api_dynshape_demo 2 1,64,32#4,64,32#8,64,32 1 1

LD_LIBRARY_PATH=./lib用于让程序加载同目录下的librknnrt.so运行时库。

Android 编译、安装与运行

编译

Android 平台使用 build-android.sh 交叉编译。首先导入 NDK 路径:

export ANDROID_NDK_PATH=~/opts/ndk/android-ndk-r18b

然后执行:

./build-android.sh -t <target> -a <arch> [-b <build_type>] # 例如: ./build-android.sh -t rk3568 -a arm64-v8a -b Release

-t目标芯片选项与 Linux 版一致,-a填 Android ABI(如arm64-v8a)。构建产物安装到install/rknn_matmul_api_demo_Android目录。

安装

连接设备并将程序传输到/data:

adb push install/rknn_matmul_api_demo_Android /data/

运行

adb shell cd /data/rknn_matmul_api_demo_Android/ export LD_LIBRARY_PATH=./lib

静态 shape 测试:

./rknn_matmul_api_demo 2 4,64,32

动态 shape 测试:

./rknn_matmul_api_dynshape_demo 2 1,64,32#4,64,32#8,64,32 1 1

性能测试与结果验证要点

两个 demo 都内置了性能测时逻辑:rknn_matmul_run每执行一次,就打印Elapse Time = xx.xxms, FPS = xx.xx,loop_count次后输出平均耗时与平均 FPS(见 静态模式 与 动态模式)。做性能评估时建议:

  • 适当增大loop_count(如 100~1000),以平均耗时为准,避免单次执行受缓存与调度抖动影响;
  • 多核平台(如 RK3588/RK3576)可通过core_mask指定使用哪些 NPU 核心;单核平台调用rknn_matmul_set_core_mask返回 -1 属正常现象;
  • 结果校验依赖示例内置的 CPU 参考实现(matrixMultiply系列函数),它假设量化反量化公式为sum * input_scale * weight_scale / output_scale,因此示例中的 scale/zp 仅供测试,接入真实模型时必须替换为模型实际的量化参数,否则校验可能不通过。

总体而言,rknn_matmul_api_demo是一个把 NPU 底层矩阵乘法能力完整暴露出来的参考实现:既适合作为性能基准测试工具,也可以作为将 FP16/INT8/INT4 矩阵运算卸载到 NPU 的移植起点。建议结合 rknn_matmul_api.h 的接口注释(布局格式、对齐限制、量化类型说明均在该头文件中)与本文的调用流程对照阅读。

  • 人工智能
  • 推理引擎
  • 模型量化
  • 模型优化
  • 边缘计算
  • 开发工具

【免费下载链接】rknn-toolkit2

项目地址:https://gitcode.com/gh_mirrors/rk/rknn-toolkit2
点击查看免费下载

相关推荐

上一篇:LikeC4 图表初识视口:用 initialZoom 让嵌入式架构图以调用方定义的缩放级别居中显示
下一篇:Node.js v10.3.0(Current)版本发布深度解析:核心变更、下载校验与 nodejs.org 发布博文生产管线

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表