赞
踩
基于昇腾CANN的推理应用开发——图片分类应用(C&C++)
CANN软件提供进程级环境变量设置脚本,供用户在进程中引用,以自动完成环境变量设置。
# 例如
/home/ma-user/Ascend/ascend-toolkit/set_env.sh
物理机场景下,一个Device上最多只能支持64个用户进程,Host最多只能支持Device个数64个进程;虚拟机场景下,一个Device上最多只能支持32个用户进程,Host最多只能支持Device个数32个进程。不支持使用fork函数创建多个进程,且在进程中调用AscendCL接口的场景,否则进程运行时会报错或者卡死 。
达芬奇架构,华为自研的新型芯片架构。
Fusion Engine融合引擎,负责对接GE和TBE算子,具备算子信息库的加载与管理、融合规则管理等能力。提供图优化,图编译实现接口; 实现算子接入管理;实现算子融合优化。
Graph Engine,MindSpore计算图执行引擎,主要负责根据前端的计算图完成硬件相关的优化(算子融合、内存复用等等)、device侧任务启动。提供了Graph/Operator IR作为安全易用的构图接口集合,用户可以调用这些接口构建网络模型,设置模型所包含的图、图内的算子、以及模型和算子的属性。
Tensor Boost Engine,华为自研的NPU算子开发工具,在TVM( Tensor Virtual Machine )框架基础上扩展,提供了一套Python API来实施开发活动,进行自定义算子开发。
环境变量
# TBE operator implementation tool path
export TBE_IMPL_PATH=/home/ma-user/Ascend/ascend-toolkit/latest/opp/op_impl/built-in/ai_core/tbe
# TBE operator compilation tool path
export PATH=/home/ma-user/Ascend/ascend-toolkit/latest/fwkacllib/ccec_compiler/bin/:${PATH}
# Python library that TBE implementation depends on
export PYTHONPATH=${TBE_IMPL_PATH}:${PYTHONPATH}
环境变量
# OPP path
export ASCEND_OPP_PATH=/home/ma-user/Ascend/ascend-toolkit/latest/opp
关键概念
极智AI | 谈谈为什么卷积加速更喜欢 NHWC Layout
Format为数据的物理排布格式,定义了解读数据的维度,比如1D,2D,3D,4D,5D等。
在深度学习框架中,多维数据通过多维数组存储,比如卷积神经网络的特征图(Feature Map)通常用四维数组保存,即4D,4D格式解释如下:
由于数据只能线性存储,因此这四个维度有对应的顺序。不同深度学习框架会按照不同的顺序存储特征图数据,比如:
如上图所示,以一张格式为RGB的图片为例,NCHW中,C排列在外层,每个通道内,像素紧挨在一起,实际存储的是“RRRRRRGGGGGGBBBBBB”,即同一通道的所有像素值顺序存储在一起;而NHWC中C排列在最内层,每个通道内,像素间隔挨在一起,实际存储的则是“RGBRGBRGBRGBRGBRGB”,即多个通道的同一位置的像素值顺序存储在一起。
昇腾AI处理器中,为了提高通用矩阵乘法(GEMM)运算数据块的访问效率,所有张量数据统一采用NC1HWC0的 五维数据格式,如下图所示:
其中C0与微架构强相关,等于AI Core中矩阵计算单元的大小,对于FP16类型为16,对于INT8类型则为32,这部分数据需要连续存储;C1是将C维度按照C0进行拆分后的数目,即C1=C/C0。如果结果不整除,最后一份数据需要补零以对齐C0。
NHWC -> NC1HWC0的转换过程
NHWC->NC1HWC0的转换场景示例
昇腾CANN系列教程-AscendCL特性之运行资源管理(C++)
资源申请顺序:SetDevice->CreateContext->CreateStream->CreateEvent
;
资源释放顺序:DestroyEvent->DestroyStream->DestroyContext->ResetDevice
;
同步、异步是站在调用者和执行者的角度,在当前场景下,若在Host调用接口后不等待Device执行完成再返回,则表示Host的调度是异步的;若在Host调用接口后需等待Device执行完成再返回,则表示Host的调度是同步的。
Context作为一个容器,管理了所有对象(包括Stream、Event、设备内存等)的生命周期。不同Context的Stream、不同Context的Event是完全隔离的,无法建立同步等待关系。多线程编程场景下,每切换一个线程,都要为该线程指定当前Context,否则无法获取任何其他运行资源。
Context分为两种
Context接口调用流程
与 NVIDIA GPU的Stream类似,可参考资料:CUDA随笔之Stream的使用
Stream用于维护一些异步操作的执行顺序,确保按照应用程序中的代码调用顺序在Device上执行。可以将Stream简单理解为一个异步任务的队列,主线程在调用异步接口时,指定了某个stream,本质上就是把异步任务送进了这个任务队列中。队列中任务的执行是保序的,即运行时环境会根据任务下发的顺序来依次执行,不会出现乱序执行的情况。在AscendCL的后台,运行时环境会自动从stream中依次取出一个个任务来执行。kernel执行和数据传输都显示或者隐式地运行在Stream中。
异步且基于stream的kernel执行和数据传输能够实现以下几种类型的并行:
说明:基于Stream的kernel执行和数据传输,能够实现Host运算操作、Host与Device间的数据传输、Device内的运算并行。在许多情况下,花费在执行kernel上的时间要比传输数据多得多,所以很容易想到将Host与Devide之间的交互时间隐藏在其他kernel执行过程中,我们可以将数据传输和kernel执行放在不同的stream中来实现此功能。
Stream分两种:
Stream加速
问题引入
上图中“Stream1->任务4”的执行依赖“Stream2->任务6”执行完毕,而如果还按照之前的方式,任务4执行前等待整个Stream2全部执行完毕,其实是多等了“任务7”、“任务8”的执行时间的。为了对Stream间任务依赖进行精细化管理,我们需要一个新的运行资源:Event。
Event通常用于在Stream之间执行事件同步操作,在两个Stream之间存在任务级别的依赖时尤其有用,如下图所示:
“Stream1->任务4”的确是依赖“Stream2->任务6”的完成,但这两个任务之间是无法直接产生依赖关系的,要使用Event机制来同步:
两个无法产生直接依赖关系的任务,通过Event实现了依赖机制。
// 在stream上声明event已经发生
aclError aclrtRecordEvent(aclrtEvent event, aclrtStream stream);
// 让stream等待event的发生
aclError aclrtStreamWaitEvent(aclrtStream stream, aclrtEvent event);
相关概念
支持调用AscendCL接口同步Stream之间的任务,包括同步Host与Device之间的任务、Device与Device间的任务。
Events标记了stream执行过程中的一个点,我们就可以检查正在执行的stream中的操作是否到达该点,我们可以把event当成一个操作插入到stream中的众多操作中,当执行到该操作时,所做工作就是设置CPU的一个flag来标记表示完成。
例如,若stream2的任务依赖stream1的任务,想保证stream1中的任务先完成,这时可创建一个Event,并将Event插入到stream1,在执行stream2的任务前,先同步等待Event完成。
默认Context、默认Stream一般适用于简单应用,用户仅仅需要一个Device的计算场景下。多线程应用程序建议全部使用显式创建的Context和Stream。
线程中创建的多个Context,线程缺省使用最后一次创建的Context。
一个用户线程一定会绑定一个Context,所有Device的资源使用或调度,都必须基于Context。
一个进程中可以创建多个Context,但一个线程同一时刻只能使用一个Contex,Context中已经关联了本线程要使用的Device。
一个线程中可以创建多个Stream,不同的Stream上计算任务是可以并行执行;多线程场景下,也可以每个线程分别创建一个Stream,且线程之间的Stream在Device上相互独立,每个Stream内部的任务是按照Stream下发的顺序执行。
多线程的调度依赖于运行应用的
操作系统调度
\textcolor{Red}{操作系统调度}
操作系统调度,多Stream在Device侧的调度,由Device的
调度组件
\textcolor{Red}{调度组件}
调度组件 进行调度。
昇腾CANN系列教程-AscendCL特性之内存管理(C++)
如果进程运行在Host上,就申请Host内存,如果是运行在Device上,申请的就是Device内存。
aclrtMallocHost->aclrtMalloc->aclrtMemcpy(host-device)
模型输入
一个模型有且只有1个“输入DataSet”(数据集对象),里边包含所有的输入;而如果有多个输入的话,每个输入用一个“DataBuffer”来承载。
如上图所示,一个模型有2个输入,其中第一个输入是若干张图片,第二个输入是每张图片的元数据等信息,那么在编程中我们需要这样做:
核心代码
// 创建DataBuffer
aclDataBuffer *aclCreateDataBuffer(void *data, size_t size);
// 创建DataSet
aclmdlDataset *aclmdlCreateDataset();
// 向DataSet中添加DataBuffer
aclError aclmdlAddDatasetBuffer(aclmdlDataset *dataset, aclDataBuffer *dataBuffer);
模型输出
模型输出数据结构,也是一个DataSet,1-N个DataBuffer,但是问题来了,我还没推理,没有数据呢,怎么会有DataBuffer? 其实在模型确定下来之后,基本上输出的个数和占用内存大小就已经完全确定了。比如一个有1000个类别的分类网络的输出,结果就是1000组数据,每组包含一个标签和一个置信度,共2000个数值。那么这个输出所占用的内存大小就很容易计算出来,并在推理之前先申请好内存。没错,AscendCL不支持推理过程中自动申请输出内存,一定要在调用推理接口之前先把输出内存、DataBuffer、DataSet准备好。
核心代码
// 创建一个“模型描述信息”对象,用于收集模型的描述信息,也就是模型的元数据
aclError aclmdlGetDesc(aclmdlDesc *modelDesc, uint32_t modelId);
// 获取模型描述信息
// 可以根据模型的modelId来获取模型描述信息,并将描述信息填充进modelDesc对象中
aclError aclmdlGetDesc(aclmdlDesc *modelDesc, uint32_t modelId);
NPU信息
npu-smi info
如果昇腾AI处理器配套软件包没有安装在默认路径,安装好MindSpore之后,需要导出Runtime相关环境变量,下述命令中LOCAL_ASCEND=/usr/local/Ascend
的/usr/local/Ascend
表示配套软件包的安装路径,需注意将其改为配套软件包的实际安装路径。
# 安装nnae包时配置
. /usr/local/Ascend/nnae/set_env.sh
# 安装tfplugin包时配置
. /usr/local/Ascend/tfplugin/set_env.sh
# 安装toolbox包时配置
. /usr/local/Ascend/toolbox/set_env.sh
# control log level. 0-DEBUG, 1-INFO, 2-WARNING, 3-ERROR, 4-CRITICAL, default level is WARNING.
export GLOG_v=2
# Conda environmental options
export LOCAL_ASCEND=/usr/local/Ascend # the root directory of run package
# lib libraries that the run package depends on
export LD_LIBRARY_PATH=${LOCAL_ASCEND}/ascend-toolkit/latest/fwkacllib/lib64:${LOCAL_ASCEND}/driver/lib64:${LOCAL_ASCEND}/ascend-toolkit/latest/opp/op_impl/built-in/ai_core/tbe/op_tiling:${LD_LIBRARY_PATH}
/usr/local/Ascend/driver/lib64/driver
# Environment variables that must be configured
## TBE operator implementation tool path
export TBE_IMPL_PATH=${LOCAL_ASCEND}/ascend-toolkit/latest/opp/op_impl/built-in/ai_core/tbe
## OPP path
export ASCEND_OPP_PATH=${LOCAL_ASCEND}/ascend-toolkit/latest/opp
## AICPU path
export ASCEND_AICPU_PATH=${ASCEND_OPP_PATH}/..
## TBE operator compilation tool path
export PATH=${LOCAL_ASCEND}/ascend-toolkit/latest/fwkacllib/ccec_compiler/bin/:${PATH}
## Python library that TBE implementation depends on
export PYTHONPATH=${TBE_IMPL_PATH}:${PYTHONPATH}
# CANN版本信息
cat /usr/local/Ascend/version.info
输出:
version=21.0.3.1
# CANN安装路径
/usr/local/Ascend
# ascend-toolkit版本信息
/usr/local/Ascend/ascend-toolkit/latest/arm64-linux/toolkit/version.info
输出:
Version=1.75.22.0.220
# ascend-toolkit安装信息
cat /usr/local/Ascend/ascend-toolkit/latest/arm64-linux/ascend_toolkit_install.info
输出:
version=20.1.rc1
arch=arm64
os=linux
path=/usr/local/Ascend/ascend-toolkit/latest/arm64-linux
cat /usr/local/Ascend/nnae/latest/ascend_nnae_install.info
输出:
package_name=Ascend-cann-nnae
version=5.0.3
innerversion=V100R001C79B220SPC1011
arch=aarch64
os=linux
path=/usr/local/Ascend/nnae/5.0.3
# nnrt安装信息
cat /usr/local/Ascend/nnrt/latest/ascend_nnrt_install.info
输出:
version=20.0.RC1
arch=arm64
os=linux
gcc=gcc7.3.0
path=/usr/local/Ascend/nnrt/latest
fwkacllib信息
/usr/local/Ascend/nnae/5.0.3/fwkacllib/lib64
auto_tune-0.1.0-py3-none-any.whl libauto_tiling.so libge_executor.so libregister.a
hccl-0.1.0-py3-none-any.whl lib_caffe_parser.so libge_runner.so libregister.so
hccl_reduce_op_ascend710.o libcce_aicore.so libgraph.so libresource.so
hccl_reduce_op_ascend910.o libcce_aicpudev_online.so libhccl.so librs.so
libacl_cblas.so libcce.so libhcom_graph_adaptor.so libruntime.so
libacl_dvpp_mpi.so libcce_tools.so libindextransform.so libte_fusion.so
libacl_dvpp.so libcompress.so libmmpa.a libtiling.so
libacl_op_compiler.so libcompressweight.so libmmpa.so libtsdclient.so
libacl_retr.so libc_sec.so libmsprofiler.so libtvm_runtime.so
libacl_tdt_channel.so libdatatransfer.so libopskernel.so libtvm.so
libaicore_utils.so liberror_manager.a libopt_feature.so libtvm_topi.so
libaicpu_engine_common.so liberror_manager.so libparser_common.so plugin
libalog.so libfmk_onnx_parser.so libplatform.so schedule_search-0.1.0-py3-none-any.whl
libascendcl.a libfmk_parser.so libra_hdc.so stub
libascendcl.so libge_common.so libra_peer.so te-0.4.0-py3-none-any.whl
libascend_protobuf.so.3.13.0.0 libge_compiler.so libra.so topi-0.4.0-py3-none-any.whl
昇腾软件包提供商用版和社区版两种下载途径:
5.1.RC1.alpha005
版本,以及在固件与驱动链接中获取对应的固件和驱动安装包,安装包的选择与安装方式请参照上述的 商用版 安装指引文档。表1-1 昇腾软件介绍
软件类型 | 软件介绍 |
---|---|
固件 | 固件包含昇腾AI处理器自带的OS 、电源器件和功耗管理器件控制软件,分别用于后续加载到AI处理器的模型计算、芯片启动控制和功耗控制。 |
驱动 | 部署在昇腾服务器,功能类似英伟达驱动,管理查询昇腾AI处理器,同时为上层CANN软件提供芯片控制、资源分配等接口。 |
CANN | 部署在昇腾服务器,功能类似英伟达CUDA,包含Runtime、算子库、图引擎、媒体数据处理等组件,通过AscendCL(Ascend Computing Language)对外提供Device管理、Context管理、Stream管理、内存管理、模型加载与执行、算子加载与执行、媒体数据处理等API,帮助开发者实现在昇腾CANN平台上进行深度学习推理计算、图像预处理、单算子加速计算。 |
重要说明:如果是首次安装请按照“驱动 > 固件”的顺序,分别安装驱动和固件包;覆盖安装请按照“固件 > 驱动”的顺序,分别安装固件和驱动包,**{vision}**表示软件版本号。
Ascend-toolkit 开发套件包。
(ms19) root@80f9a288c6ba:/data/YOYOFile/Downloads# ./Ascend-cann-toolkit_6.0.0.alpha003_linux-aarch64.run --install
Verifying archive integrity... 100% SHA256 checksums are OK. All good.
Uncompressing ASCEND_RUN_PACKAGE 100%
[Toolkit] [20221229-09:15:54] [INFO] LogFile:/var/log/ascend_seclog/ascend_toolkit_install.log
[Toolkit] [20221229-09:15:54] [INFO] install start
[Toolkit] [20221229-09:15:54] [INFO] The installation path is /usr/local/Ascend.
[Toolkit] [20221229-09:15:54] [WARNING] driver package maybe not installed
[Toolkit] [20221229-09:15:54] [WARNING] driver package maybe not installed
[Toolkit] [20221229-09:15:54] [INFO] install package CANN-runtime-6.0.0.alpha003-linux_aarch64.run start
[Toolkit] [20221229-09:16:03] [INFO] CANN-runtime-6.0.0.alpha003-linux_aarch64.run --devel --quiet --nox11 install success
[Toolkit] [20221229-09:16:03] [INFO] install package CANN-compiler-6.0.0.alpha003-linux_aarch64.run start
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
[Toolkit] [20221229-09:16:54] [INFO] CANN-compiler-6.0.0.alpha003-linux_aarch64.run --devel --pylocal --quiet --nox11 install success
[Toolkit] [20221229-09:16:54] [INFO] install package CANN-opp-6.0.0.alpha003-linux_aarch64.run start
/root/selfgz5969520074/opp/script/opp_install.sh: 686: /root/selfgz5969520074/opp/script/opp_install.sh: __reverse_list: not found
/root/selfgz5969520074/opp/script/opp_install.sh: 686: /root/selfgz5969520074/opp/script/opp_install.sh: __reverse_list: not found
[Toolkit] [20221229-09:17:39] [INFO] CANN-opp-6.0.0.alpha003-linux_aarch64.run --devel --quiet --nox11 install success
[Toolkit] [20221229-09:17:39] [INFO] install package CANN-toolkit-6.0.0.alpha003-linux_aarch64.run start
[Toolkit] [20221229-09:18:29] [INFO] CANN-toolkit-6.0.0.alpha003-linux_aarch64.run --devel --pylocal --quiet --nox11 install success
[Toolkit] [20221229-09:18:29] [INFO] install package CANN-aoe-6.0.0.alpha003-linux_aarch64.run start
[Toolkit] [20221229-09:18:38] [INFO] CANN-aoe-6.0.0.alpha003-linux_aarch64.run --devel --quiet --nox11 install success
[Toolkit] [20221229-09:18:38] [INFO] install package Ascend-mindstudio-toolkit_6.0.0.alpha003_linux-aarch64.run start
[Toolkit] [20221229-09:18:51] [INFO] Ascend-mindstudio-toolkit_6.0.0.alpha003_linux-aarch64.run --devel --quiet --nox11 install success
[Toolkit] [20221229-09:18:51] [INFO] install package Ascend-test-ops_6.0.0.alpha003_linux.run start
[Toolkit] [20221229-09:18:51] [INFO] Ascend-test-ops_6.0.0.alpha003_linux.run --devel --quiet --nox11 install success
[Toolkit] [20221229-09:18:51] [INFO] install package Ascend-pyACL_6.0.0.alpha003_linux-aarch64.run start
[Toolkit] [20221229-09:18:52] [INFO] Ascend-pyACL_6.0.0.alpha003_linux-aarch64.run --devel --quiet --nox11 install success
[Toolkit] [20221229-09:18:52] [INFO] Please make sure that:
PATH includes :
/usr/local/Ascend/ascend-toolkit/latest/bin:
/usr/local/Ascend/ascend-toolkit/latest/compiler/ccec_compiler/bin:
LD_LIBRARY_PATH includes :
/usr/local/Ascend/ascend-toolkit/latest/lib64:
/usr/local/Ascend/ascend-toolkit/latest/lib64/plugin/opskernel:
/usr/local/Ascend/ascend-toolkit/latest/lib64/plugin/nnengine:
PYTHONPATH includes :
/usr/local/Ascend/ascend-toolkit/latest/python/site-packages:
/usr/local/Ascend/ascend-toolkit/latest/opp/built-in/op_impl/ai_core/tbe:
ASCEND_AICPU_PATH includes :
/usr/local/Ascend/ascend-toolkit/latest:
ASCEND_OPP_PATH includes :
/usr/local/Ascend/ascend-toolkit/latest/opp:
TOOLCHAIN_HOME includes :
/usr/local/Ascend/ascend-toolkit/latest/toolkit:
ASCEND_HOME_PATH includes :
/usr/local/Ascend/ascend-toolkit/latest:
[Toolkit] [20221229-09:18:52] [INFO] If your service is started using the shell script, you can call the /usr/local/Ascend/ascend-toolkit/set_env.sh script to configure environment variables. Note that this script can not be executed mannually.
[Toolkit] [20221229-09:18:52] [INFO] Ascend-cann-toolkit_6.0.0.alpha003_linux-aarch64 install success. The installation path is /usr/local/Ascend.
# 以安装用户在开发环境任意目录下执行以下命令,打开.bashrc文件。
vi ~/.bashrc
# 在文件最后一行后面添加如下内容。CPU_ARCH环境变量请根据运行环境cpu架构填写,如export CPU_ARCH=aarch64
export CPU_ARCH=[aarch64/x86_64]
# THIRDPART_PATH需要按照运行环境安装路径设置,如运行环境为arm,指定安装路径为Ascend-arm,则需要设置为export THIRDPART_PATH=${HOME}/Ascend-arm/thirdpart/${CPU_ARCH}
export THIRDPART_PATH=${HOME}/Ascend/thirdpart/${CPU_ARCH} #代码编译时链接第三方库
# CANN软件安装后文件存储路径,最后一级目录请根据运行环境设置,运行环境为arm,这里填arm64-linux;运行环境为x86,则这里填x86_64-linux,以下以arm环境为例
export INSTALL_DIR=${HOME}/Ascend/ascend-toolkit/latest/arm64-linux
# 执行命令保存文件并退出。
:wq!
# 执行命令使其立即生效。
source ~/.bashrc
# 创建第三方依赖文件夹
mkdir -p ${THIRDPART_PATH}
# 拷贝公共文件到第三方依赖文件夹
cd $HOME
git clone https://gitee.com/ascend/samples.git
cp -r ${HOME}/samples/common ${THIRDPART_PATH}
# AscendCL头文件路径
/home/ma-user/Ascend/ascend-toolkit/latest/include/acl
# AscendCL库文件路径
/home/ma-user/Ascend/ascend-toolkit/latest/lib64
# 查看device设备
ls /dev/davinci*
# 驱动安装路径
/usr/local/Ascend/driver/
# 查看 Ascend-cann-toolkit 版本
cd /home/ma-user/Ascend/ascend-toolkit/latest/arm64-linux
cat ascend_toolkit_install.info
查看NPU资源
# 查看npu资源
npu-smi info
watch -n 1 npu-smi info
npu-smi info watch -i 0 -c 0
查看 Ascend-cann-toolkit 版本
cd /home/ma-user/Ascend/ascend-toolkit/latest/arm64-linux
cat ascend_toolkit_install.info
[ma-user@notebook-87136e07-6a9a-4138-beec-742972f7b62f arm64-linux]$ cat ascend_toolkit_install.info
package_name=Ascend-cann-toolkit
version=5.0.3
innerversion=V100R001C79B220SPC1011
arch=aarch64
os=linux
path=/home/ma-user/Ascend/ascend-toolkit/5.0.3/arm64-linux
说明 | 路径 | 变量1 | 变量2 | 变量3 |
---|---|---|---|---|
Ascend安装路径 | ${HOME}/Ascend/ascend-toolkit/latest | INSTALL_DIR | DDK_PATH | INC_PATH |
Ascend库文件路径 | ${INSTALL_DIR}/acllib/lib64/stub | NPU_HOST_LIB | LIB_PATH |
设置INC_PATH、LIB_PATH路径有三种方式:
临时性设置
${INSTALL_DIR}
表示CANN软件安装目录,例如,$HOME/Ascend/ascend-toolkit/latest/{arch-os}
,arch表示操作系统架构(需根据运行环境的架构选择),{os}
表示操作系统(需根据运行环境的操作系统选择)。
旧版本(默认)
export INSTALL_DIR=${HOME}/Ascend/ascend-toolkit/latest
export DDK_PATH=${INSTALL_DIR}
export NPU_HOST_LIB=${INSTALL_DIR}/acllib/lib64/stub
新版本(未来)
export INSTALL_DIR=${HOME}/Ascend/ascend-toolkit/latest
export DDK_PATH=${INSTALL_DIR}
export NPU_HOST_LIB=${INSTALL_DIR}/{arch-os}/devlib
永久性设置
vi ~/.bashrc
# 末尾添加环境变量
export DDK_PATH=${INSTALL_DIR}
export NPU_HOST_LIB=${INSTALL_DIR}/acllib/lib64/stub
# 更新环境变量
source ~/.bashrc
在CMakeLists.txt文件中修改路径
DDK_PATH也即INC_PATH,表示AscendCL头文件路径;NPU_HOST_LIB也即LIB_PATH,表示AscendCL库文件路径。
CMakeLists.txt
文件中会指定 INC_PATH和LIB_PATH,根据真实路径修改。
CMakeLists.txt
# CMake lowest version requirement
cmake_minimum_required(VERSION 3.5.1)
# project information
project(AME)
# Compile options
add_compile_options(-std=c++11)
add_definitions(-DENABLE_DVPP_INTERFACE)
# Specify target generation path
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "../../../out")
set(CMAKE_CXX_FLAGS_DEBUG "-fPIC -O0 -g -Wall")
set(CMAKE_CXX_FLAGS_RELEASE "-fPIC -O2 -Wall")
# 设置环境变量,DDK_PATH即为INC_PATH
set(INC_PATH $ENV{DDK_PATH})
if (NOT DEFINED ENV{DDK_PATH})
# set(INC_PATH "/usr/local/Ascend")
set(INC_PATH "/home/ma-user/Ascend/ascend-toolkit/latest/arm64-linux")
message(STATUS "set default INC_PATH: ${INC_PATH}")
else ()
message(STATUS "env INC_PATH: ${INC_PATH}")
endif()
# 设置环境变量,NPU_HOST_LIB即为LIB_PATH
set(LIB_PATH $ENV{NPU_HOST_LIB})
if (NOT DEFINED ENV{NPU_HOST_LIB})
# set(LIB_PATH "/usr/local/Ascend/acllib/lib64/stub/")
set(LIB_PATH "/home/ma-user/Ascend/ascend-toolkit/latest/arm64-linux/acllib/lib64/stub/")
message(STATUS "set default LIB_PATH: ${LIB_PATH}")
else ()
message(STATUS "env LIB_PATH: ${LIB_PATH}")
endif()
# Header path
include_directories(
${INC_PATH}/acllib/include/
../inc/
)
# add host lib path
link_directories(
${LIB_PATH}
)
add_executable(main
utils.cpp
# dvpp_process.cpp
model_process.cpp
# singleOp_process.cpp
sample_process.cpp
main.cpp)
target_link_libraries(main
ascendcl acl_cblas acl_dvpp stdc++)
install(TARGETS main DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
示例:
set(INC_PATH "/usr/local/Ascend")
set(LIB_PATH "/usr/local/Ascend/acllib/lib64/stub/")
修改为
set(INC_PATH "/home/ma-user/Ascend/ascend-toolkit/latest/arm64-linux")
set(LIB_PATH "/home/ma-user/Ascend/ascend-toolkit/latest/arm64-linux/acllib/lib64/stub/")
如果不设置环境变量,很多工具无法使用,例如:ATC模型转换工具。
# 设置环境变量
cd /home/ma-user/Ascend/ascend-toolkit
source set_env.sh
set_env.sh
export LD_LIBRARY_PATH=/home/ma-user/Ascend/ascend-toolkit/latest/lib64:/home/ma-user/Ascend/ascend-toolkit/latest/compiler/lib64/plugin/opskernel:/home/ma-user/Ascend/ascend-toolkit/latest/compiler/lib64/plugin/nnengine:$LD_LIBRARY_PATH
export PYTHONPATH=/home/ma-user/Ascend/ascend-toolkit/latest/python/site-packages:/home/ma-user/Ascend/ascend-toolkit/latest/opp/op_impl/built-in/ai_core/tbe:$PYTHONPATH
export PATH=/home/ma-user/Ascend/ascend-toolkit/latest/bin:/home/ma-user/Ascend/ascend-toolkit/latest/compiler/ccec_compiler/bin:$PATH
export ASCEND_AICPU_PATH=/home/ma-user/Ascend/ascend-toolkit/latest
export ASCEND_OPP_PATH=/home/ma-user/Ascend/ascend-toolkit/latest/opp
export TOOLCHAIN_HOME=/home/ma-user/Ascend/ascend-toolkit/latest/toolkit
export ASCEND_AUTOML_PATH=/home/ma-user/Ascend/ascend-toolkit/latest/tools
# 设置环境变量
cd /home/ma-user/Ascend/ascend-toolkit
source set_env.sh
/home/ma-user/Ascend/tfplugin/set_env.sh
set_env.sh
export PYTHONPATH=/home/ma-user/Ascend/tfplugin/latest/python/site-packages:$PYTHONPATH
昇腾张量编译器(Ascend Tensor Compiler,简称ATC)是昇腾CANN架构体系下的模型转换工具。ATC工具安装在 ascend-toolkit/latest/bin
目录下。
单算子描述文件场景下
Ascend IR定义的单算子描述文件(json格式)通过ATC工具进行单算子编译后,转成适配昇腾AI处理器的单算子离线模型,然后上传到板端环境,通过AscendCL接口加载单算子模型文件用于验证单算子功能。
昇腾AI处理器中,为了提高通用矩阵乘法(GEMM)运算数据块的访问效率,所有张量数据统一采用NC1HWC0的五维数据格式,如下图所示。
/home/ma-user/Ascend/ascend-toolkit/5.0.3/arm64-linux/aarch64-linux/bin
atc --model=model/resnet50.prototxt --weight=model/resnet50.caffemodel --framework=0 --output=model/resnet50 --soc_version=Ascend310
–model:ResNet-50网络的模型文件(*.prototxt)的路径。
–weight:ResNet-50网络的预训练模型文件(*.caffemodel)的路径。
–framework:原始框架类型。0表示Caffe。
–output:resnet50.om模型文件的路径。请注意,记录保存该om模型文件的路径,后续开发应用时需要使用。
–soc_version:
昇腾AI处理器
的版本。
“Ascend-DMI”工具主要为Atlas产品的标卡、板卡及模组类产品提供带宽测试、算力测试、功耗测试等功能。工具的功能介绍如表1-1所示。本系统通过调用底层DCMI(设备控制管理接口)/DSMI(设备系统管理接口)以及ACL(Ascend Computing Language,昇腾计算语言)相关接口完成相关检测功能,对于系统级别的信息查询通过调用系统提供的通用库来实现,用户使用工具时通过配置参数来实现不同的测试功能。
表1-1 工具功能介绍
功能名称 | 功能介绍 |
---|---|
带宽测试 | 测试总线带宽、内存带宽和时延。 |
算力测试 | 测试芯片中AI Core的算力值和满算力下芯片的平均功率。 |
功耗测试 | 检测整卡或芯片的功耗信息。 |
设备实时状态查询 | 检测设备在运行过程中的状态信息。 |
故障诊断 | 分别对软件类和硬件类进行诊断,并输出诊断结果,各检查类包含的项目如下:软件类:驱动与硬件的兼容性,驱动与固件版本兼容性,CANN各层软件的兼容性,CANN与驱动的兼容性。硬件类:Device健康状态,ROCE网口健康状态,本地带宽,算力,内存和HBM。 |
软硬件版本兼容性测试 | 获取硬件信息、架构、驱动版本、固件版本以及软件版本,并检测软硬件间的兼容性。 |
设备拓扑检测 | 查询单机内多卡间的拓扑结构。 |
msame工具为模型推理工具
离线推理工具msame使用案例
yolov3
功能:对ATC模型转换工具转换后的om离线模型文件进行推理。
msame是模型推理工具,输入.om模型和模型所需要的输入bin文件,输出模型的输出数据文件,支持多次推理(指对同一输入数据进行推理)。模型必须是通过atc工具转换的om模型,输入bin文件需要符合模型的输入要求(支持模型多输入)。
export DDK_PATH=/home/ma-user/Ascend/ascend-toolkit/latest/arm64-linux
export NPU_HOST_LIB=/home/ma-user/Ascend/ascend-toolkit/latest/arm64-linux/acllib/lib64/stub
cd $HOME/AscendProjects/tools/msame/
./build.sh g++ $HOME/work/MyDocuments/tools/msame/out/
[ma-user@notebook-87136e07-6a9a-4138-beec-742972f7b62f msame]$ ./build.sh g++ $HOME/work/MyDocuments/tools/msame/out/
g++
/home/ma-user/work/MyDocuments/tools/msame/out/
-- The C compiler identification is GNU 7.3.0
-- The CXX compiler identification is GNU 7.3.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/g++
-- Check for working CXX compiler: /usr/bin/g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- env INC_PATH: /home/ma-user/Ascend/ascend-toolkit/latest/arm64-linux
-- env LIB_PATH: /home/ma-user/Ascend/ascend-toolkit/latest/arm64-linux/acllib/lib64/stub
-- Configuring done
-- Generating done
-- Build files have been written to: /home/ma-user/work/MyDocuments/tools/msame/build/intermediates/host
Scanning dependencies of target main
[ 20%] Building CXX object CMakeFiles/main.dir/utils.cpp.o
[ 40%] Building CXX object CMakeFiles/main.dir/model_process.cpp.o
[ 60%] Building CXX object CMakeFiles/main.dir/sample_process.cpp.o
[ 80%] Building CXX object CMakeFiles/main.dir/main.cpp.o
[100%] Linking CXX executable /home/ma-user/work/MyDocuments/tools/msame/out/main
[100%] Built target main
类型一 不加input参数
cd ./out
./msame --model "../../../ResNet-50/resnet50.om" --output "../out" --outfmt TXT --loop 1
[ma-user@notebook-87136e07-6a9a-4138-beec-742972f7b62f out]$ ./msame --model "../../../ResNet-50/resnet50.om" --output "../out" --outfmt TXT --loop 1
[INFO] acl init success
[INFO] open device 0 success
[INFO] create context success
[INFO] create stream success
[INFO] get run mode success
[INFO] load model ../../../ResNet-50/resnet50.om success
[INFO] create model description success
[INFO] get input dynamic gear count success
[INFO] create model output success
../out//2022627_17_16_22_844232
[INFO] model execute success
Inference time: 0.564ms
[INFO] get max dynamic batch size success
[INFO] output data success
Inference average time: 0.564000 ms
[INFO] destroy model input success
[INFO] unload model success, model Id is 1
[INFO] Execute sample success
[INFO] end to destroy stream
[INFO] end to destroy context
[INFO] end to reset device is 0
[INFO] end to finalize acl
什么场景适合使用AI CPU呢?针对IO密集型场景(程序),推荐使用AI CPU,避免数据传输耗时的问题;针对计算密集型场景(程序),推荐使用NPU(Ascend910),NPU计算性能强悍,可降低计算耗时。
AI CPU负责执行昇腾AI处理器的CPU类算子(包括控制算子、标量和向量等通用计算),其在Ascend解决方案系统架构中的位置如下所示:
AI CPU算子编译执行所涉及组件如下:
aclInit()
功能:acl初始化。
原型:
aclError aclInit(const char *configPath);
参数解释
configPath这个参数指的是在初始化时加载的配置文件的路径,可以通过这个配置文件来配置参数DUMP功能用于比对精度、配置profiling信息用于调试性能以及配置算子缓存信息老化功能,从而节约内存和平衡调用性能。
去初始化
功能:acl去初始化。
原型:
aclError aclFinalize();
不建议在析构函数中调用aclFinalize接口,否则在进程退出时可能由于单例析构顺序未知而导致进程异常退出等问题。
aclrtSetDevice()
功能:指定接下来的计算要分配哪个Device。
原型:
aclError aclrtSetDevice(int32_t deviceId);
deviceId是逻辑ID,通过以下方式获取:
aclError aclrtGetDeviceCount(uint32_t *count);
“count”指明了当前环境下一共有多少个逻辑设备可用,而上文中的“deviceId”只需要从[0, (count-1)]中任取1个即可。例如,我们的机器上只插了1个Atlas300I推理卡,上边有4个昇腾310AI处理器,但是软件栈经过检查发现只有3个芯片可用,于是在调用 aclrtGetDeviceCount()
之后,系统返回的“count”值是3,能用的deviceId就是0/1/2中任意一个。
aclrtSetDevice()
这个接口调用完毕后,除了指定了计算设备之外,还会同时创建1个默认的Context;而这个默认的Context还附赠了2个Stream,1个默认Stream和1个用于执行内部同步的Stream。这也意味着:如果是编写非常简单的单线程同步推理应用,在运行资源这里我们只需要调用aclrtSetDevice就够了。
aclrtResetDevice()
功能:复位当前运算的Device,释放Device上的资源。
原型:
aclError aclrtResetDevice(int32_t deviceId);
调用 aclrtResetDevice()
接口复位“deviceId”所指代的设备,释放其上的运行资源,包括默认Context、默认Stream以及默认Context下创建的所有Stream。若Context或Stream下还有未完成的任务,系统会等待任务完成后再对设备进行释放。建议 aclrtSetDevice()
接口和 aclrtResetDevice()
接口配对使用,在不使用Device上资源时,通过调用aclrtResetDevice接口及时释放本进程使用的Device资源。
1. 调用aclrtDestroyEvent接口释放Event
2. 调用aclrtDestroyStream接口释放显式创建的Stream
3. 调用aclrtDestroyContext释放显式创建的Context
4. 调用aclrtResetDevice接口释放Device
aclrtGetDevice()
功能:获取当前正在使用的DeviceID。
原型:
aclError aclrtGetDevice(int32_t *deviceId);
问题引入
既然可以多次调用aclrtSetDevice来为当前进程设置不同的设备,那么当我们想要知道当前时刻当前进程使用的到底是哪个设备的时候该怎么办呢?用 aclrtGetDevice()
接口来获取当前正在使用的DeviceID
aclrtSynchronizeDevice()
功能:阻塞应用程序运行,直到正在运算中的Device完成运算。
原型:
aclError aclrtSynchronizeDevice(void);
问题引入
当我们面对多线程推理应用时,直接执行DestroyEvent/Stream/Context
可能导致其上的任务被强行断开,产生未知的后果。这个时候,为了保证在执行各种销毁接口之前,该Device上的任务都已经执行完毕,故需要执行 aclrtSynchronizeDevice()
接口。
这个接口会阻塞当前线程的执行,直到对应Device上的所有任务都执行完毕。但是在接口的参数表中并没有看到指定DeviceId的参数,是因为当前线程一定有且只有一个“currentcontext”,而currentContext是会绑定一个Device的,所以调用aclrtSynchronizeDevice接口,等待的就是currentContext绑定的Device。调用完这个接口之后,就可以放心地销毁Event/Stream/Context,进而ResetDevice了。
aclrtSetCurrentContext()
功能:设置当前线程的Context。
原型:
aclError aclrtSetCurrentContext(aclrtContext context);
线程C通过切换context,间接实现了Device的切换。实际上,这种操作在AscendCL中是被鼓励的,用切换当前context的方式来切换Device甚至比直接调用“aclrtSetDevice”接口来切换Device效率更高。
aclrtSynchronizeStream()
功能:阻塞应用程序运行,直到指定Stream中的所有任务都完成。
原型:
aclError aclrtSynchronizeStream(aclrtStream stream);
确认Stream中任务全部执行完毕。
aclrtSynchronizeEvent()
功能:阻塞主线程执行,直到event发生(即某个stream跑到了aclrtRecordEvent)。
原型:
aclError aclrtSynchronizeEvent(aclrtEvent event);
Synchronize接口:
这些都是阻塞主线程等待对应的运行资源内任务全部完成,只是资源的粒度不一样。
aclrtGetRunMode()
功能:判断当前进程是跑在Host还是Device上。
原型:
aclError aclrtGetRunMode(aclrtRunMode *runMode);
ACL_DEVICE
:昇腾AI软件栈运行在Device的Control CPU或板端环境上。
ACL_HOST
:昇腾AI软件栈运行在Host CPU上。
调用 aclrtGetRunMode接口获取软件栈的运行模式,运行模式不同,则内存申请接口调用逻辑不同。
如果查询结果为ACL_HOST
,则两侧内存都要申请,并且将数据从Host拷贝到Device上;如果查询结果为 ACL_DEVICE
,则数据传输时仅需申请Device上的内存。
aclrtMallocHost()
功能:申请Host内存。
原型:
aclError aclrtMallocHost(void **hostPtr, size_t size);
使用 aclrtMallocHost 接口申请Host内存,需先调用aclrtGetRunMode 接口获取软件栈的运行模式,当查询结果为ACL_HOST
,则数据传输时涉及申请Host上的内存。
注意:对于昇腾310 AI处理器,Ascend RC场景下,不涉及Host上的内存申请、Host内的数据传输、Host与Device之间的数据传输。
// 申请Host内存
aclError aclrtMallocHost(void **hostPtr, size_t size);
// 释放Host内存
aclError aclrtFreeHost(void *hostPtr);
aclrtMalloc()
功能:申请Device内存。
原型:
aclError aclrtMalloc(void **devPtr, size_t size, aclrtMemMallocPolicy policy);
使用 aclrtMalloc 接口申请Device内存,需先调用aclrtGetRunMode接口获取软件栈的运行模式,当查询结果为ACL_DEVICE,则数据传输时不涉及申请Host上的内存,仅需申请Device上的内存。该种方式多一些代码逻辑的判断,不需要由用户处理Device上的内存对齐。在Device上运行应用的场景,该种方式少一些内存复制的步骤,性能较好。如果涉及媒体数据处理(例如,图片解码、缩放等)时,需使用 acldvppMalloc 或hi_mpi_dvpp_malloc 接口申请内存。
// 申请Device内存
aclError aclrtMalloc(void **devPtr, size_t size, aclrtMemMallocPolicy policy);
// 释放Device内存
aclError aclrtFree(void *devPtr);
申请内存的策略
aclrtMemcpy()
功能:同步内存复制。
原型:
aclError aclrtMemcpy(void *dst, size_t destMax, const void *src, size_t count, aclrtMemcpyKind kind)
调用 aclrtMemcpy 接口同步内存复制。
内存复制的类型
typedef enum aclrtMemcpyKind {
ACL_MEMCPY_HOST_TO_HOST, // Host内的内存复制
ACL_MEMCPY_HOST_TO_DEVICE, // Host到Device的内存复制
ACL_MEMCPY_DEVICE_TO_HOST, // Device到Host的内存复制
ACL_MEMCPY_DEVICE_TO_DEVICE, // Device内或Device间的内存复制
} aclrtMemcpyKind;
aclrtMemcpyAsync()
功能:异步内存复制。
原型:
aclError aclrtMemcpyAsync(void *dst, size_t destMax, const void *src, size_t count, aclrtMemcpyKind kind, aclrtStream stream)
调用 aclrtMemcpyAsync 接口成功仅表示任务下发成功,不表示任务执行成功。调用该接口后,一定要调用 aclrtSynchronizeStream 接口确保内存复制的任务已执行完成。
本接口不支持异步Host内的内存复制功能,因此调用本接口选择ACL_MEMCPY_HOST_TO_HOST
类型进行内存复制时,任务下发成功,但系统内部处理该任务时会返回失败。
aclrtGetMemInfo()
功能:获取当前设备上总内存和可用内存。
原型:
aclError aclrtGetMemInfo(aclrtMemAttr attr, size_t *free, size_t *total)
aclrtMemAttr内存类型
typedef enum aclrtMemAttr {
ACL_DDR_MEM, //DDR内存,DDR上所有大页内存+普通内存
ACL_HBM_MEM, //HBM内存,HBM上所有大页内存+普通内存
ACL_DDR_MEM_HUGE, //DDR大页内存
ACL_DDR_MEM_NORMAL, //DDR普通内存
ACL_HBM_MEM_HUGE, //HBM大页内存
ACL_HBM_MEM_NORMAL, //HBM普通内存
ACL_DDR_MEM_P2P_HUGE, //DDR中用于Device间数据复制的大页内存
ACL_DDR_MEM_P2P_NORMAL, //DDR中用于Device间数据复制的普通内存
ACL_HBM_MEM_P2P_HUGE, //HBM中用于Device间数据复制的大页内存
ACL_HBM_MEM_P2P_NORMAL, //HBM中用于Device间数据复制的普通内存
} aclrtMemAttr;
aclmdlLoadFromFileWithMem()
功能:从内存中加载模型。
原型:
aclError aclmdlLoadFromFileWithMem(const char *modelPath, uint32_t *modelId, void *workPtr, size_t workSize, void *weightPtr, size_t weightSize);
参数解释
“工作内存”,指的是模型运行过程中所占用的内存(比如计算图,不包含权值的部分);“权值内存”,专门保存模型的权值数据。
aclmdlQuerySize()
功能:查询一个磁盘上的模型文件,如果要加载进系统,需要多大的工作内存和权值内存。
原型:
aclError aclmdlQuerySize(const char *fileName, size_t *workSize, size_t *weightSize);
问题引入
查询一个磁盘上的模型文件,如果要加载进系统,究竟申请多大的内存,能够给工作内存和权值内存使用呢?
aclmdlExecute()
功能:执行推理任务。
原型:
aclError aclmdlExecute(uint32_t modelId, const aclmdlDataset *input, aclmdlDataset *output);
等待指定stream中所有任务全部执行完毕。
void test()
{
/* 1-初始化 */
const char *aclConfigPath = "";
aclError ret = aclInit(aclConfigPath);
INFO_LOG("AscendCL init success.");
/* 2-申请运行资源 */
ret = aclrtSetDevice(0);
INFO_LOG("Set device %d success.",0);
aclrtContext context;
ret = aclrtCreateContext(&context, 0);
INFO_LOG("Create context success.");
/* 3-申请stream */
aclrtStream stream;
ret = aclrtCreateStream(&stream);
INFO_LOG("Create stream success.");
/* 4-创建host内存 */
void *hostInput = nullptr;
int64_t size_input = 32;
ret = aclrtMallocHost(&hostInput, size_input);
ret = aclrtMemset(hostInput, size_input, 0, size_input);
INFO_LOG("AscendCL HostMem Malloc success .");
/* 5-创建device内存 */
void *devInput = nullptr;
ret = aclrtMalloc(&devInput, size_input, ACL_MEM_MALLOC_HUGE_FIRST);
INFO_LOG("AscendCL DeviceMem Malloc success.");
/* 6-device内存初始化*/
ret = aclrtMemset(devInput, size_input, 10, size_input);
INFO_LOG("Set All Device Memory to 10");
/* 7-显示host内存*/
char *p = (char *)hostInput;
INFO_LOG("Display: host data :");
for(int i = 0 ;i< size_input;i++)
printf("%d ", p[i]);
printf("\n");
/* 8-异步内存拷贝device->host*/
ret =aclrtMemcpyAsync(hostInput, size_input,devInput,size_input, ACL_MEMCPY_DEVICE_TO_HOST, stream);
INFO_LOG("Copy Device Data to Host Async.");
/* 9-同步等待*/
ret = aclrtSynchronizeStream(stream);
INFO_LOG("AclrtSynchronizeStream success.");
/* 10-显示host内存*/
INFO_LOG("Display: host data :");
for(int i = 0 ;i< size_input;i++)
printf("%d ", p[i]);
printf("\n");
/* 11-销毁资源 */
ret = aclrtDestroyStream(stream);
INFO_LOG("End to destroy stream.");
ret = aclrtFree(devInput);
ret = aclrtFree(hostInput);
ret = aclrtDestroyContext(context);
INFO_LOG("Context is destroyed.");
ret = aclrtResetDevice(0);
ret = aclFinalize();
INFO_LOG("End to finalize acl.");
return 0;
}
test();
把“aclrtRecordEvent”和“aclrtStreamWaitEvent”这两个接口当做两种特殊的送进Stream的任务,和其他送进Stream的任务一样,这两个接口调用完都是可以立即返回的,主线程不必跟着Stream一起等着事件发生。
void EventTest()
{
/*1-初始化*/
const char *aclConfigPath = "";
aclError ret = aclInit(aclConfigPath);
INFO_LOG("AscendCL init success.");
/*2-申请运行资源*/
ret = aclrtSetDevice(0);
INFO_LOG("Set device %d success.",0);
aclrtContext context;
ret = aclrtCreateContext(&context, 0);
INFO_LOG("Create context success.");
/* 3-创建一个Event,显示event状态 */
aclrtEvent event;
aclrtEventStatus status;
/*
typedef enum aclrtEventStatus {
ACL_EVENT_STATUS_COMPLETE = 0, //完成
ACL_EVENT_STATUS_NOT_READY = 1, //未完成
ACL_EVENT_STATUS_RESERVED = 2, //预留
} aclrtEventStatus;
*/
ret = aclrtCreateEvent(&event);
ret = aclrtQueryEvent(event, &status);
INFO_LOG("Create event success, event status is %d (0:COMPLETE, 1:NOT_READY).", status);
/* 4-创建两个Stream */
aclrtStream stream1, stream2;
ret = aclrtCreateStream(&stream1);
ret = aclrtCreateStream(&stream2);
INFO_LOG("Create stream1&stream2 success.");
/* 5-创建host内存 */
void *hostInput = nullptr;
int64_t size_input = 32;
ret = aclrtMallocHost(&hostInput, size_input);
ret = aclrtMemset(hostInput, size_input, 0, size_input);
INFO_LOG("AscendCL Host Mem Malloc success.");
/* 6-创建device内存 */
void *devInput = nullptr;
ret = aclrtMalloc(&devInput, size_input, ACL_MEM_MALLOC_HUGE_FIRST);
INFO_LOG("AscendCL Device Mem Malloc success.");
/* 7-device内存初始化*/
ret = aclrtMemset(devInput, size_input, 10, size_input);
INFO_LOG("Set all device mem 10.");
/* 8- 在Stream中记录一个Event*/
ret = aclrtRecordEvent(event, stream1);
INFO_LOG("AclrtRecordEvent stream1 success.");
/*9-阻塞应用程序运行,等待event发生,也就是stream执行完成*/
ret = aclrtStreamWaitEvent(stream2, event);
INFO_LOG("AclrtStreamWaitEvent stream2 success.");
/* 10-显示event状态、host内存内容*/
ret = aclrtQueryEvent(event, &status);
INFO_LOG("AclrtQueryEvent success, event status is %d (0:COMPLETE, 1:NOT_READY).", status);
char *p = (char *)hostInput;
INFO_LOG("Display host data:");
for(int i = 0; i< size_input; i++)
printf("%d ", p[i]);
printf("\n");
/* 11-异步内存拷贝device->host*/
ret = aclrtMemcpyAsync(hostInput, size_input,devInput,size_input, ACL_MEMCPY_DEVICE_TO_HOST, stream2);
INFO_LOG("Copy Device Data to Host Async.");
/* 12-显示event状态、host内存内容*/
ret = aclrtQueryEvent(event, &status);
INFO_LOG("AclrtQueryEvent success, event status is %d (0:COMPLETE, 1:NOT_READY).", status);
INFO_LOG("Display host data:");
for(int i = 0; i < size_input; i++)
printf("%d ", p[i]);
printf("\n");
/* 13- aclrtResetEvent */
aclrtResetEvent(stream2, event);
INFO_LOG("AclrtResetEvent stream2 success.");
ret = aclrtSynchronizeStream(stream2);
/* 14- 查看event状态、host内存内容*/
ret = aclrtQueryEvent(event, &status);
INFO_LOG("AclrtQueryEvent success, event status is %d (0:COMPLETE, 1:NOT_READY).", status);
INFO_LOG("Display host data:");
for(int i = 0; i < size_input; i++)
printf("%d ", p[i]);
printf("\n");
/* 15-销毁资源 */
ret = aclrtDestroyStream(stream1);
ret = aclrtDestroyStream(stream2);
INFO_LOG("End to destroy stream.");
ret = aclrtFree(devInput);
ret = aclrtFree(hostInput);
ret = aclrtDestroyEvent(event);
INFO_LOG("End to destroy event.");
ret = aclrtDestroyContext(context);
INFO_LOG("End to destroy context.");
ret = aclrtResetDevice(0);
ret = aclFinalize();
INFO_LOG("End to finalize acl.");
return 0;
}
EventTest();
昇腾CANN系列课程-AscendCL特性之同步异步(C++)
int32_t deviceId_ = 0;
uint32_t modelId = 0;
aclrtStream stream_;
pthread_t threadId_;
static aclrtContext context_;
size_t pictureDataSize = 0;
void *pictureHostData = nullptr;
void *pictureDeviceData = nullptr;
aclmdlDataset *inputDataSet = nullptr;
aclDataBuffer *inputDataBuffer = nullptr;
aclmdlDataset *outputDataSet = nullptr;
aclDataBuffer *outputDataBuffer = nullptr;
aclmdlDesc *modelDesc = nullptr;
size_t outputDataSize = 0;
void *outputDeviceData = nullptr;
void *outputHostData = nullptr;
static bool g_isExit = false;
void ReadPictureTotHost(const char *picturePath)
{
string fileName = picturePath;
ifstream binFile(fileName, ifstream::binary);
binFile.seekg(0, binFile.end);
pictureDataSize = binFile.tellg();
binFile.seekg(0, binFile.beg);
aclError ret = aclrtMallocHost(&pictureHostData, pictureDataSize);
binFile.read((char*)pictureHostData, pictureDataSize);
binFile.close();
INFO_LOG("ReadPictureTotHost !");
}
void PrintResult()
{
aclError ret = aclrtMallocHost(&outputHostData, outputDataSize);
ret = aclrtMemcpy(outputHostData, outputDataSize, outputDeviceData, outputDataSize, ACL_MEMCPY_DEVICE_TO_HOST);
float* outFloatData = reinterpret_cast(outputHostData);
map> resultMap;
for (unsigned int j = 0; j < outputDataSize / sizeof(float);++j)
{
resultMap[*outFloatData] = j;
outFloatData++;
}
int cnt = 0;
for (auto it = resultMap.begin();it != resultMap.end();++it)
{
if(++cnt > 5)
{
break;
}
INFO_LOG("Top %d: index[%d] value[%lf] ", cnt, it->second, it->first);
}
}
void CopyDataFromHostToDevice()
{
aclError ret = aclrtMalloc(&pictureDeviceData, pictureDataSize, ACL_MEM_MALLOC_HUGE_FIRST);
ret = aclrtMemcpy(pictureDeviceData, pictureDataSize, pictureHostData, pictureDataSize, ACL_MEMCPY_HOST_TO_DEVICE);
INFO_LOG("CopyDataFromHostToDevice!");
}
void CreateModelInput()
{
inputDataSet = aclmdlCreateDataset();
inputDataBuffer = aclCreateDataBuffer(pictureDeviceData, pictureDataSize);
aclError ret = aclmdlAddDatasetBuffer(inputDataSet, inputDataBuffer);
INFO_LOG("CreateModelInput!");
}
void CreateModelOutput()
{
modelDesc = aclmdlCreateDesc();
aclError ret = aclmdlGetDesc(modelDesc, modelId);
outputDataSet = aclmdlCreateDataset();
outputDataSize = aclmdlGetOutputSizeByIndex(modelDesc, 0);
ret = aclrtMalloc(&outputDeviceData, outputDataSize, ACL_MEM_MALLOC_HUGE_FIRST);
outputDataBuffer = aclCreateDataBuffer(outputDeviceData, outputDataSize);
ret = aclmdlAddDatasetBuffer(outputDataSet, outputDataBuffer);
INFO_LOG("CreateModelOutput!");
}
void LoadPicture(const char* picturePath)
{
ReadPictureTotHost(picturePath);
CopyDataFromHostToDevice();
CreateModelInput();
CreateModelOutput();
INFO_LOG("LoadPicture!");
}
void CallBackFunc(void *arg)
{
aclmdlDataset *output = (aclmdlDataset *)arg;
INFO_LOG("CallBackFunc!");
PrintResult();
}
void ExecuteAsync()
{
aclError ret = aclmdlExecuteAsync(modelId, inputDataSet, outputDataSet, stream_);
/* launch callback is to process all output data of model async execute */
ret = aclrtLaunchCallback(CallBackFunc, (void *)outputDataSet, ACL_CALLBACK_BLOCK, stream_);
INFO_LOG("AclmdlExecuteAsync success!");
return ;
}
void UnloadPicture()
{
aclError ret = aclrtFreeHost(pictureHostData);
pictureHostData = nullptr;
ret = aclrtFree(pictureDeviceData);
pictureDeviceData = nullptr;
aclDestroyDataBuffer(inputDataBuffer);
inputDataBuffer = nullptr;
aclmdlDestroyDataset(inputDataSet);
inputDataSet = nullptr;
ret = aclrtFreeHost(outputHostData);
outputHostData = nullptr;
ret = aclrtFree(outputDeviceData);
outputDeviceData = nullptr;
aclDestroyDataBuffer(outputDataBuffer);
outputDataBuffer = nullptr;
aclmdlDestroyDataset(outputDataSet);
outputDataSet = nullptr;
INFO_LOG("UnloadPicture success!");
}
void DestroyResource()
{
aclError ret = aclrtResetDevice(deviceId_);
aclFinalize();
INFO_LOG("DestroyResource success!");
}
void *ProcessCallback(void *arg)
{
aclrtSetCurrentContext(context_);
while (1) {
// timeout value is 100ms
(void)aclrtProcessReport(100);
if(*(static_cast(arg)) == true) {
return nullptr;
}
}
}
int testmain()
{
const char *picturePath = "./src/dog1_1024_683.bin";
const char *modelPath = "./src/resnet50.om";
/* 1-Init Resource */
aclError ret = aclInit(nullptr);
ret = aclrtSetDevice(deviceId_);
ret = aclrtCreateContext(&context_, deviceId_);
INFO_LOG("Create context success!");
ret = aclrtCreateStream(&stream_);
INFO_LOG("Create stream success!");
/* 2-Load Model */
ret = aclmdlLoadFromFile(modelPath, &modelId);
INFO_LOG("LoadModel success!");
/* 3-Load Picture */
LoadPicture(picturePath);
/* 4-aclrtSubscribeReport */
g_isExit = false;
pthread_create(&threadId_, nullptr, ProcessCallback, &g_isExit);
(void)aclrtSubscribeReport(static_cast(threadId_), stream_);
INFO_LOG("Subscribe report success!");
/* 5-ExecuteAsync */
ExecuteAsync();
ret = aclrtSynchronizeStream(stream_);
INFO_LOG("Model execute success !");
g_isExit = true;
/* 6-unsubscribe report */
aclrtUnSubscribeReport(static_cast(threadId_), stream_);
INFO_LOG("Unsubscribe report success !");
/* 7-release resource*/
aclmdlDestroyDesc(modelDesc);
aclmdlUnload(modelId);
INFO_LOG("UnloadModel success!");
UnloadPicture();
DestroyResource();
return 0;
}
testmain();
实际上如果按照功能细分的话,AscendCL在模型推理层面总共有四种动态特性:
动态Batch、动态分辨率、动态维度这三个特性统称为“动态Shape”,即动态输入形状。
静态Shapre
atc --model=./googlenet.prototxt --weight=./googlenet.caffemodel --framework=0 --output=googlenet --soc_version=Ascend310 --input_shape="data:1,3,224,224" --input_format=NCHW
如果“-input_shape”不做配置的话,atc工具会自动读取原始模型文件中记载的输入节点的形状。但是如果原始模型文件中记载了个“?,3,224,224”,代表着batch数是不固定的,是动态的。
atc --model=./googlenet.prototxt --weight=./googlenet.caffemodel --framework=0 --output=googlenet --soc_version=Ascend310 --input_shape="data:?,3,224,224" --input_format=NCHW --dynamic_batch_size="1,2,4,8"
参数解释
动态特性是会一定程度上牺牲性能的,并且也可能会导致模型文件体积急剧增长。
void testdynamicbatch()
{
uint32_t modelId_ = 0;
aclmdlDesc *modelDesc_ = nullptr;
aclmdlDataset *input_ = nullptr;
aclmdlDataset *output_ = nullptr;
aclDataBuffer* inputBuffer = nullptr;
aclDataBuffer* batchBuffer = nullptr;
aclDataBuffer* outputBuffer = nullptr;
int32_t deviceId_ = 0;
void* pictureData = nullptr;
void* pictureDeviceData = nullptr;
uint32_t pictureDataSize = 0;
uint32_t totalDataSize = 0;
void* outputHostData = nullptr;
void* outputDeviceData = nullptr;
size_t outputDataSize = 0;
uint64_t batchSize = 2;
char *modelPath = (char *)"./src/googlenet_multibatch.om";
/*1- init */
aclError ret = aclInit(nullptr);
INFO_LOG("Call aclInit success.");
ret = aclrtSetDevice(deviceId_);
INFO_LOG("Call aclrtSetDevice success.");
/*2- load model */
ret = aclmdlLoadFromFile(modelPath, &modelId_);
INFO_LOG("Call aclmdlLoadFromFile %s success.", modelPath);
/*3- load picture data */
uint32_t pos = 0;
for(int i = 0;i < batchSize;i++)
{
string fileName = "./src/dog" + (to_string(i+1)) + "_1024_683.bin";
ifstream binFile(fileName, ifstream::binary);
binFile.seekg(0, binFile.end);
pictureDataSize = binFile.tellg();
binFile.seekg(0, binFile.beg);
if(nullptr == pictureData)
ret = aclrtMallocHost(&pictureData, pictureDataSize);
binFile.read(static_cast(pictureData), pictureDataSize);
binFile.close();
if(nullptr == pictureDeviceData)
ret = aclrtMalloc(&pictureDeviceData, pictureDataSize * batchSize, ACL_MEM_MALLOC_HUGE_FIRST);
ret = aclrtMemcpy((char *)pictureDeviceData + pos, pictureDataSize,
pictureData, pictureDataSize, ACL_MEMCPY_HOST_TO_DEVICE);
pos += pictureDataSize;
INFO_LOG("Load Picture data %s success.", fileName.c_str());
}
/*4- set input data buffer */
totalDataSize = pictureDataSize * batchSize;
modelDesc_ = aclmdlCreateDesc();
ret = aclmdlGetDesc(modelDesc_, modelId_);
input_ = aclmdlCreateDataset();
for(size_t index = 0; index < aclmdlGetNumInputs(modelDesc_); ++index)
{
const char* name = aclmdlGetInputNameByIndex(modelDesc_, index);
size_t inputLen = aclmdlGetInputSizeByIndex(modelDesc_, index);
if(strcmp(name, ACL_DYNAMIC_TENSOR_NAME) == 0)
{
void *data = nullptr;
ret = aclrtMalloc(&data, inputLen, ACL_MEM_MALLOC_HUGE_FIRST);
batchBuffer = aclCreateDataBuffer(data, inputLen);
ret = aclmdlAddDatasetBuffer(input_, batchBuffer);
/*4.1- set idynamic batch */
ret = aclmdlSetDynamicBatchSize(modelId_, input_, index, batchSize);
}
else
{
inputBuffer = aclCreateDataBuffer(pictureDeviceData, totalDataSize);
ret = aclmdlAddDatasetBuffer(input_, inputBuffer);
}
}
INFO_LOG("Call aclCreateDataBuffer input success.");
/*5- set output dataset */
output_ = aclmdlCreateDataset();
outputDataSize = aclmdlGetOutputSizeByIndex(modelDesc_, 0);
ret = aclrtMalloc(&outputDeviceData, outputDataSize, ACL_MEM_MALLOC_NORMAL_ONLY);
outputBuffer = aclCreateDataBuffer(outputDeviceData, outputDataSize);
ret = aclmdlAddDatasetBuffer(output_, outputBuffer);
INFO_LOG("Call aclCreateDataBuffer output success.");
/*6-model inference execute*/
ret = aclmdlExecute(modelId_, input_, output_);
INFO_LOG("Call aclmdlExecute success.");
/*7-print output data*/
float* outFloatData;
ret = aclrtMallocHost(&outputHostData, outputDataSize);
ret = aclrtMemcpy(outputHostData, outputDataSize, outputDeviceData, outputDataSize, ACL_MEMCPY_DEVICE_TO_HOST);
outFloatData = reinterpret_cast < float * > (outputHostData);
for(int i = 0;i < batchSize;i++)
{
map> resultMap;
for (unsigned int j = 0; j < outputDataSize / (sizeof(float) * 2); ++j)
{
resultMap[*outFloatData] = j;
outFloatData++;
}
INFO_LOG("=================Result of picture %d=================", (i+1));
int cnt = 0;
for (auto it = resultMap.begin(); it != resultMap.end(); ++it)
{
if(++cnt > 5)
{
break;
}
INFO_LOG("Top %d: index[%d] value[%lf] ", cnt, it->second, it->first);
}
}
/*8-free resource */
aclmdlDestroyDesc(modelDesc_);
aclmdlUnload(modelId_);
INFO_LOG("Call aclmdlDestroyDesc success.");
aclrtFreeHost(pictureData);
pictureData = nullptr;
aclrtFreeHost(outputHostData);
outputHostData = nullptr;
aclrtFree(pictureDeviceData);
pictureDeviceData = nullptr;
aclDestroyDataBuffer(inputBuffer);
inputBuffer = nullptr;
aclmdlDestroyDataset(input_);
input_ = nullptr;
INFO_LOG("Free Picture data success.");
aclrtFree(outputDeviceData);
outputDeviceData = nullptr;
aclDestroyDataBuffer(outputBuffer);
outputBuffer = nullptr;
aclmdlDestroyDataset(output_);
output_ = nullptr;
ret = aclrtResetDevice(deviceId_);
ret = aclFinalize();
INFO_LOG("Call aclFinalize success.");
return;
}
testdynamicbatch();
昇腾CANN系列教程-AscendCL特性之模型推理(C++)
int32_t deviceId_ = 0;
uint32_t modelId = 0;
size_t pictureDataSize = 0;
void *pictureHostData = nullptr;
void *pictureDeviceData = nullptr;
aclmdlDataset *inputDataSet = nullptr;
aclDataBuffer *inputDataBuffer = nullptr;
aclmdlDataset *outputDataSet = nullptr;
aclDataBuffer *outputDataBuffer = nullptr;
aclmdlDesc *modelDesc = nullptr;
size_t outputDataSize = 0;
void *outputDeviceData = nullptr;
void *outputHostData = nullptr;
aclError InitResource()
{
aclError ret = aclInit(nullptr);
ret = aclrtSetDevice(deviceId_);
INFO_LOG("InitResource success!");
return ret;
}
void ReadPictureTotHost(const char *picturePath)
{
string fileName = picturePath;
ifstream binFile(fileName, ifstream::binary);
binFile.seekg(0, binFile.end);
pictureDataSize = binFile.tellg();
binFile.seekg(0, binFile.beg);
aclError ret = aclrtMallocHost(&pictureHostData, pictureDataSize);
binFile.read((char*)pictureHostData, pictureDataSize);
binFile.close();
INFO_LOG("ReadPictureTotHost !");
}
void CopyDataFromHostToDevice()
{
aclError ret = aclrtMalloc(&pictureDeviceData, pictureDataSize, ACL_MEM_MALLOC_HUGE_FIRST);
ret = aclrtMemcpy(pictureDeviceData, pictureDataSize, pictureHostData, pictureDataSize, ACL_MEMCPY_HOST_TO_DEVICE);
INFO_LOG("CopyDataFromHostToDevice !");
}
void CreateModelInput()
{
inputDataSet = aclmdlCreateDataset();
inputDataBuffer = aclCreateDataBuffer(pictureDeviceData, pictureDataSize);
aclError ret = aclmdlAddDatasetBuffer(inputDataSet, inputDataBuffer);
INFO_LOG("CreateModelInput!");
}
void CreateModelOutput()
{
modelDesc = aclmdlCreateDesc();
aclError ret = aclmdlGetDesc(modelDesc, modelId);
outputDataSet = aclmdlCreateDataset();
outputDataSize = aclmdlGetOutputSizeByIndex(modelDesc, 0);
ret = aclrtMalloc(&outputDeviceData, outputDataSize, ACL_MEM_MALLOC_HUGE_FIRST);
outputDataBuffer = aclCreateDataBuffer(outputDeviceData, outputDataSize);
ret = aclmdlAddDatasetBuffer(outputDataSet, outputDataBuffer);
INFO_LOG("CreateModelOutput !");
}
void LoadPicture(const char* picturePath)
{
ReadPictureTotHost(picturePath);
CopyDataFromHostToDevice();
CreateModelInput();
CreateModelOutput();
INFO_LOG("LoadPicture !");
}
void LoadModel(const char* modelPath)
{
aclError ret = aclmdlLoadFromFile(modelPath, &modelId);
INFO_LOG("LoadModel success !");
}
void Inference()
{
aclError ret = aclmdlExecute(modelId, inputDataSet, outputDataSet);
INFO_LOG("Inference ret %d !", ret);
}
void PrintResult()
{
aclError ret = aclrtMallocHost(&outputHostData, outputDataSize);
ret = aclrtMemcpy(outputHostData, outputDataSize, outputDeviceData, outputDataSize, ACL_MEMCPY_DEVICE_TO_HOST);
float* outFloatData = reinterpret_cast(outputHostData);
map> resultMap;
for (unsigned int j = 0; j < outputDataSize / sizeof(float);++j)
{
resultMap[*outFloatData] = j;
outFloatData++;
}
int cnt = 0;
for (auto it = resultMap.begin();it != resultMap.end();++it)
{
if(++cnt > 5)
{
break;
}
INFO_LOG("Top %d: index[%d] value[%lf] ", cnt, it->second, it->first);
}
}
void UnloadModel()
{
aclmdlDestroyDesc(modelDesc);
aclmdlUnload(modelId);
INFO_LOG("UnloadModel success !");
}
void UnloadPicture()
{
aclError ret = aclrtFreeHost(pictureHostData);
pictureHostData = nullptr;
ret = aclrtFree(pictureDeviceData);
pictureDeviceData = nullptr;
aclDestroyDataBuffer(inputDataBuffer);
inputDataBuffer = nullptr;
aclmdlDestroyDataset(inputDataSet);
inputDataSet = nullptr;
ret = aclrtFreeHost(outputHostData);
outputHostData = nullptr;
ret = aclrtFree(outputDeviceData);
outputDeviceData = nullptr;
aclDestroyDataBuffer(outputDataBuffer);
outputDataBuffer = nullptr;
aclmdlDestroyDataset(outputDataSet);
outputDataSet = nullptr;
INFO_LOG("UnloadPicture success !");
}
void DestroyResource()
{
aclError ret = aclrtResetDevice(deviceId_);
aclFinalize();
INFO_LOG("DestroyResource success !");
}
void mainTest()
{
const char *picturePath = "dog1_1024_683.bin";
const char *mdoelPath = "resnet50.om";
InitResource();
LoadModel(mdoelPath);
LoadPicture(picturePath);
Inference();
PrintResult();
UnloadModel();
UnloadPicture();
DestroyResource();
return;
}
mainTest();
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。