大多数情况下,Local Memory的存储,无法完全容纳算子的输入与输出的所有数据,需要每次搬运一部分输入数柜进行计算然后搬出,再敲运下一部分输入数据进行计算,直到得到完愁的最终结果,这个数据切分、分块计算的过程称之为Tiling过程
在Ascend c中,Tiling的直接表示形式就是结构体 (struct),简称Tiling结构体
Tiling结构体定义在Tiling头文件中,其中的每个结构体参数表示如何对输入数据进行切分,以及决定了计算过程的一些细节,结构体在host侧实例化,并通过指针传入kernel函数中
__global__aicore__void add_custom(GM_ADDR_X,GM_ADDR_Y,GM_ADDR_Z,GM_ADDR workspace,GM_ADDR tiling)
Tiling结构体中的值在host侧确定,根据具体入参的信息,完成各项结构体参数的计算,并实施搬运分别在host侧和device侧为Tiling结构体申请空间,将其从host侧搬运到device侧,H2D操作
固定shape输入回顾
由于输入的大小是已知的,每次搬运多少数据,总共搬运多少次均可以在编译时直接计算出来
当算子shape固定时,开发者使用不同shape时需要重新对算子进行编译,带来大量的算子二进制文件
动态shape的算子可以将形状通过核函数的入参传入核函数内,参与内部逻辑计算,从而符合不同shape下的使用场景
改装固定shape算子成功态shape
基于现有的固定shape算子,将其改装为动态shape的算子,将控制形状的BLOCK_DIM,TOTAL_LENGTH,TILE_NUM这些变量的来源变化为依靠外界输入得到,在核函数中额外传入一个tiling,它将指向控制核函数逻辑处理的至关重要的几个变量
主要操作流程:
Tiling结构体中的信息:
TOTAL_LENGTH: 总共需要计算的数据个数
TILE_NUM:每个核上计算数据分块的个数
注:USE_CORE_NUM为参与并行计算使用的核数,有独立接口GetBlockNum()可以在核函数内获得
核函数传入Tiling指针,与x,y,z的角色相同,添加获得tiling结构体的宏函数调用GET_TILING_DATA
CPU模式和NPU模式涉及到指针转化,用宏函数 CONVERT_TILING_DATA 将__ubuf_uint8 t* 转化为__ubuf__tilingstruct*
CPU模式和NPU模式之间逻辑的区别,用宏函数INIT_TILING_DATA区分tiling_data不同的初始化过程
为了方便而不失一般性,这里使用宏函数GET_TILING_DATA暴露给核函数进行调用
通过Ascend C算子孪生调试技术,充分发挥CPU和NPU的调测优势,提升算子调试效率
1.传统开发方式面临的问题:
Ascend C提供孪生调试技术,即在CPU模式下进行运算实际上会创建一个NPU的模型并模拟它的计算行为,以此进行业务功能的调试.相同的算子代码可以在CPU模式下进行精度调试,并无缝切换到NPU模式下运行
1.使用GDB调试
source /usr/local/Ascend/latest/bin/setenv.bash
gdb --args add_custom_cpu
set follow-fork-mode child
break add_custom.cpp:45
run
list
backtrace
print i
break add_custom.cpp;56
continue
display xLocal
quit
NPU模式下的仿真调试,本质是使用Mode仿真器运行算子,得到数据的计算模拟和指令的时序仿真,运行后会得到一系列dump及vcd文件,用于进行算子运行过程的分析
使用Model仿真器实际上走的还是NPU运行的编译阶段,只是实际调用的动态库文件替换成了Model仿真器指定的库文件,从而达到了"真实上板“到”仿真上板的目的,运行model仿真不需要使用真实的NPU环境
自定义算子工程是一个包含用户编写的host侧和kernel侧算子实现文件的,用于编译和安装自定义算子run包的工程框架
CANN开发套件包中提供了自定义算子工程生成工具msopen,可基于算子原型定义输出算子工程:包括算子host侧代码实现文件、算子kernel侧实现文件、算子适配插件以及工程编译配置文件
编译算子包
1.修改CMakePresets.json中ASCEND_CANN_PACKAGE为CANN软件包安装路径
2.执行算子工程编译脚本./build.sh
编译时,会在算子工程根目录下生成build_out目录,编译完成后,生成的算子run包会存放到build_out目录下
部署算子包
1.默认部署(推荐)
直接执行./custom_opp_xxx.run
执行完成后算子会部署在 < ASCEND CANN PACKAGE PATH>/opp/vendors目录下
2.指定算子包部署目录
执行./custom_opp_xxx.run --install-path=xxx
算子部署到指定目录下,此时需要将自定义安装目录添加到ACEND_CUSTOM_APP_PATH变量才能使算子得到生效
1.生成自定义算子工程
msopgen gen -i add_custom.json -c ai_core_Ascend910B1 -lan cpp -out ./custrom_op
2.编译自定义算子工程,生成自定义算子包
bash build.sh
3.部署自定义算子包
./custrom_opp_ubuntu_x86_x64.run
Ascend C算子快速调用方式:
Ascend C算子标准调用方式
单算子调用有模型执行和API执行两种方式:
单算子API执行:基于C/C++定义的API执行算子,无需提供单算子描述文件进行离线模型转换,直接调用单算子API接口
单算子模型执行:基于图IR执行算子,先变异算子(使用ATC工具将Ascend IR定义的单算子描述文件编译成算子om模型文件。在线编译单算子,再调用AscendCL接口加载算子模型,最后调用AscendCL接口执行算子
自定义算子编译后,会自动生成算子API(位于build_out/autogen目录下),可以直接在应用程序中调用,算子API的形式一般定义为“两段式结构”
使用第一段接口aclnnXXXGetWorkspaceSize,计算相关联的算子调用接口中需要workspace内存的大小
按照workspaceSize大小申请Device侧内存
使用第二段接口aclnnXXX,调用对应的单算子二进制执行计算
1.AscendCL初始化
调用aclInit接口实现初始化AscendCL
2.运行管理资源申请,依次申请运行管理资源: Device、Context、Stream
3.申请内存和传输数据
4.计算workspace并执行算子
5.调用acitSynchronizeStream接口阻塞应用运行,直到指定Stream中的所有任务都完成如果需要将Device上算子执行结果数据传输到Host,则需要调用aclrtMemcpy接口 (同步接口)或aclrtMemcpyAsync接口 (异步接口)通过内存复制的方式实现数据传输
6.运行管理资源释放
7.AscendCL去初始化
调用aclFinalize接口实现AscendCL去初始化
1.编译算子
根据算子编译的方式,分为离线编译(ofine)和在线编译(online):
2.加载算子模型文件
调用aclopSetModelDir接口,设置加载模型文件的目录,目录下存放单算子模型文件(*.om文件)
3.调用aclrtMalloc接口申请Device上的内存,存放执行算子的输入、输出数据如果需要将Host上数据传输到Device,则需要调用aclrtMemcpy接口 (同步接)或aclrtMemcpyAsync接口(异步接口)通过内存复制的方式实现数据传输
4.执行算子
调用aclopExecuteV2接口执行算子。每次执行算子时,系统内部都会根据算子描述信息匹配内存中的模型
5.调用aclrtSynchronizeStream接口阻塞应用运行,直到指定Stream中的所有任
务都完成
6.调用aclrtFree接口释放内存
如果需要将Device上的算子执行结果数据传输到Host,则需要调用acirtMemcpy接口(同步接口)或aclrtMemcpyAsync接口 (异步接口)通过内存复制的方式
必适步街
实现数据传输,然后再释放内存
1.编译并运行在线编译的算子API
cd aclnn_online_model
bash run.sh
2.编译并运行离线编译的单算子模型样例
cd acl_offline_model
bash run.sh --is-dynamic 0
3.编译并运行在线编译的单算子模型样例
cd acl_online_model
bash run.sh --is-dynamic 0
当完成自定义算子开发后,可进行UT和ST测试验证算子是否正常运行
UT(Unit Test:单元测试)是开发人员进行单算子运行验证的手段之一
ST(System Test:系统测试)是在真实的硬件环境中,验证算子功能的正确性
ST测试的主要功能是:基于算子测试用例定义文件*sn生成单算子的om文件,使用AscendCL接口加载并执行单算子om文件,验证算子执行结果的正确性
注:ST测试前,需要将要测试的算子部署到当前环境中
Ascend C算子UT的本质是通过Model仿真器校验Ascend C算子的运行结果,不依赖Device设备
Ascend C算子ST测试的本质是通过生成om离线模型和单算子模式应用程序,使用Device执行校验Ascend C算子的运行结果,依赖Device设备CANN开发者套件包中提供了ST测试工具:msopst,支持生成算子的ST测试用例并在硬件环境中执行
通过PyTorch框架进行模型的训练、推理时,会调用到很多算子进行计算目前PyTorch提供了常用的算子接口和接口的注册分发机制,可以将算子映射到不同的底层硬件设备
适配PyTorch的框架,称为PyTorch Adapter (PTA)
Ascend c适配了这套PyTorch算子接口和分发接口的机制,具备从PyTorch调用异腾AI处理器的能力。通过切换device实例能够快速迁移跑在其他设备(GPU等)上的PyTorch网络
码云: https://gitee.com/ascend/pytorch/tree/v1.11.0
为了Ascend c自定义算子能够在PyTorch深度学习框架中使能在安装好自定义算子包的基础上,用户需要额外提供如下文件(以v1.11.0为例)用于编译torch npu的whl包:
在npu native functions,yaml文件中给出定义,路经为torch npu/csrc/aten/npu native functions.yaml该文件对算子进行结构化解析从而实现自动化注册和Python接口绑定
custom: # 自定义算子,需要提供算子格式定义
- func: npu_dtype cast(Tensor self, ScalarType dtype) -> Tensor
variants: function, method
- func: npu_dtype_cast_(Tensor(a!) self, Tensor src) -> Tensor(a!)
variants: method
-func:lloc float_status(Tensor self) -> Tensor
variants: function, method
- func: npu get fioat status(Tensor self) -> Tensor
variants: function, method
custom_autograd: # 自定义继承自Function的自定义算子
-func: npu convolution(Tensor input Tensor weight, Tensor? bias. ..) -> Tensoi
用户通过开发算子适配插件,实现PyTorch原生算子的输入参数、输出参数和属性的格式转换,使转换后的格式与自定义算子的输入参数、输出参数和属性的格式相同
创建适配插件文件: Ascend C算子适配文件保存在torch npu/csrc/aten/ops/op_api目录下,大驼峰命名风格,命名格式:<算子名>+.cpp如: AddCustomKernelNpu.cpp
引入依赖头文件:适配异腾AI处理器的PyTorch源代码在torch npu/csrc/framework/utils中提供了适配常用的工具
定义实现算子适配主体函数:实现算子适配主体函数,根据Ascend C算子原型构造得到对应的input、output、 attr
重编译PvTorch框架或插件:请重新编译生成
完成算子注册分发和适配插件开发的操作后,需要重新编译PTA源码,获得torch_npu插件的whi安装包,安装完成后可以通过PyTorch框架调用到底层Ascend c自定义算子
使用PyTorch调用Ascend c自定义算子的关键步骤:
注:若要调用异腾提供的算子库API,需要额外安装opp kernels包