1 前言
在正式阅读前,希望您已经对DSP的软硬件特点、编程思路和板端运行方法有基本的了解,关于这方面的内容可以查看社区文章《DSP开发快速上手》。
2 功能说明
J5的DSP能替代ARM计算量化、反量化,且同样支持以两种方式进行。
方式1
关于如何删除模型内部的量化、反量化算子,以及将相关操作融合进前后处理的大致思路,可以查看社区文章《反量化节点的融合实现》。
注意:由于删去量化、反量化算子后,会让模型的BPU算子直接暴露在模型最外侧,此时板端推理库便不会在模型推理时自动做对齐和删除对齐的操作(模型最外侧是CPU算子时,有方法可以让板端自动做对齐和去除对齐),因此当用户使用方式1时,需要编写代码为输入数据做对齐,并且在使用输出数据前需要编写代码跳用于过对齐的无效数据。关于数据对齐的更多介绍,可以查看社区文章《数据排布与跨距对齐》《模型输入输出对齐规则解析》《在部署时为输入数据做padding》。
方式2
可使用如下环境变量控制ARM是否将计算调度到DSP上执行:
需要注意的是,如果模型输入、输出数据的尺寸过小,那么ARM的量化、反量化性能是会高于DSP的,具体规则如下:
quantize输入数据尺寸需要大于等于1x2^18,否则DSP性能会低于ARM
dequantize输入数据尺寸需要大于等于1x2^20,否则DSP性能会低于ARM
当板端推理库判断量化、反量化算子的DSP计算性能低于ARM时,会将相关计算还原到ARM侧进行。但如果您十分确定要将所有尺寸的量化、反量化全部交给DSP计算,以节约CPU资源,那么就可以使用下方环境变量:
3 示例文件介绍
OE包的ddk/samples/vdsp_rpc_sample目录提供了量化、反量化示例,文件结构如下:
arm:arm侧示例,封装了常用api,主要负责发起RPC调用,接收dsp处理结果。
cv:cv示例,包含了图片处理的cv算子示例。
nn:nn示例,包含quantize和dequantize api,自定义算子softmax以及pointpillar前处理。
dsp:dsp侧示例,实现了dsp算子功能,主要负责接收arm侧发来的任务,完成softmax等算子的计算,将结果发送给arm。
src:包含quantize和dequantize api,以及自定义算子softmax以及pointpillar前处理的dsp侧实现。
script:示例的生成文件及脚本目录。
cv:包含cv示例的可执行文件、输入数据及执行脚本。
nn:包含nn示例的可执行文件、输入数据、模型及执行脚本。
image: DSP镜像目录。
lib: 可执行程序的依赖库目录。
deps:所有示例的依赖文件目录。
aarch64:arm侧的依赖目录。
vdsp:dsp侧的依赖目录。
test_quantize.cc和test_dequantize.cc是以方式1计算量化、反量化的示例代码,代码中设定了一组输入数据,以RPC的方式调用DSP做计算,同时将DSP计算结果和ARM的计算结果做了对比,以表明DSP和ARM在计算量化、反量化的精度一致性。
test_nn_plugin.cc是以方式2计算量化、反量化的示例代码,该示例推理了一个实际的模型,并将模型内部的量化和反量化算子交由DSP运行。
common.cc和common.h包含了示例运行的必备组件,其余代码属于Softmax示例,在此不做赘述。
nn文件夹的main.cc集成了调用量化反量化算子的完整功能,CMakeLists.txt是编译必备的配置文件,执行build_arm.sh后,即可编译出可上板运行的可执行文件即相关依赖,这些生成的文件会自动存放进script目录中。我们已提供了编译好的上述文件,无需用户重复编译。
量化与反量化计算的DSP实现源码是开源的,位于dsp/src路径,用户可参考学习,或者基于此代码做二次开发。main.cc主要用于注册编写的DSP算子,量化与反量化算子已经注册,CMakeLists.txt是编译必备的配置文件,执行build_dsp.sh后,即可编译出可以在板端配置的vdsp0和vdsp1镜像,这两个镜像文件还会自动存放进script/image目录中。我们提供了已经编译好的镜像文件,无需用户重复编译。
4 ARM调用代码解读
方式1
删去模型内部的量化、反量化算子,将量化、反量化移到前后处理中由DSP计算。
示例代码为vdsp_rpc_sample/arm/nn/src路径下的test_quantize.cc和test_dequantize.cc,此处以量化调用代码为例进行介绍,反量化调用的代码编写思路相近。
该部分代码是为了让ARM做量化计算编写的,为了验证DSP的计算结果是否和ARM一致。
在test_quantize函数中,这里手动设置了一组输入数据信息。
src_mem:存放所有待量化计算的输入数据
scale_mem:存放所有scale值
zero_point_mem:存放所有zero_point值
dst_arm_mem:存放ARM的量化计算结果
dst_dsp_mem:存放DSP的量化计算结果
data_generate函数的具体实现在common.cc,基于shape生成待量化计算的输入数据
quantize_ref函数用于让ARM做量化计算
定义结构体src和dst,用于RPC调用时传递给DSP。
hbDSPQuantize函数封装了hbDSPRpc接口,通过RPC的方式向DSP发送量化计算指令。
验证ARM计算结果和DSP计算结果的一致性,验证通过则打印check result right。
方式2
保留模型内部的量化、反量化算子,将原本派发到ARM上的计算调度到DSP执行。
5 示例运行说明
此时我们可以编写一个deploy.sh脚本并执行,用于在J5开发板上部署DSP镜像:
之后执行以下命令,给予dsp_relay_server和test_cv文件可执行权限:
最后进入script/nn文件夹,执行以下命令即可运行NN侧包括量化、反量化的全部示例:
用户也可以通过追加参数的形式指定需要执行的算子,所有可执行算子可以在运行脚本后添加help查看:
打印信息中,如果出现Run Quantize and Dequantize on DSP,就表明正在以方式2让DSP计算量化反量化算子。
