2023·CANN训练营第二季——跟着sample仓例程,动手写(改)自己的AscendC算子

前言:

        AscendC算子先进,硬核吗?必须是!那AscendC算子开发很难了,必须不是,算子开发很难,但AscendC开发算子不难!把复杂的事情流程(范式)化,把困难的事情简单(SPMD)化,这才是AscendC算子开发的硬核所在。这篇文档,交流下通过参考例程,动手写(改)出自己的AscendC算子。

        选择LeakyReLU算子作为样例,有点投机取巧的成分,因为sample仓本身就有部分代码。拿它作为例子,是为了展示一个知识点“通过TilingData传递算子的属性信息”。LeakyReLU算子有一个“negative_slope”,这是个标量,本例就是将这个属性值通过TilingData传递给核函数,进行运算的。本篇以实战为主,相关知识都给了官方链接,有兴趣的小伙伴可以参考阅读。受限于篇幅,本篇仅完成算子工程代码和简单核函数方式调用工程的编写,单算子调用工程以后再展开写。下面开始我们的AscendC编程之旅。

一、准备开发环境

        按照“Ascend C环境准备 https://www.hiascend.com/forum/thread-0235128261452483095-1-1.html?fid=0163125572293226003”贴,选择免费的modelarts CodeLab搭建开发环境,在此环境上,还可以完成CPU侧的验证,而且这个环境自带pytorch,所以也可以在算子分析阶段用来熟悉算子。如果需要进行NPU实际环境的验证,可参考“AscendC算子NPU开发调试环境成功搭建篇https://www.bilibili.com/read/cv26991439/ ”,完成环境搭建。

        使用此环境需要注意,由于modelarts,只保留work目录的内容(安装包放在work目录,就需要每次重新下载了),所以每次使用前需要重新安装toolkits和算子开发包,并配置环境变量。

        安装toolkit和算子开发包:

./Ascend-cann-toolkit_7.0.RC1.alpha002_linux-x86_64.run --install --force
tar -xf Ascend-cann-communitysdk_7.0.RC1.alpha002_linux-x86_64.tar.gz -C ~/Ascend/ascend-toolkit/latest

        配置环境变量:

source /home/ma-user/Ascend/ascend-toolkit/set_env.sh
export ASCEND_CUSTOM_PATH=$HOME/Ascend/ascend-toolkit/latest
export ASCEND_HOME_DIR=$HOME/Ascend/ascend-toolkit/latest
export PATH=/home/ma-user/work/cmake-3.26.4-linux-x86_64/bin:$PATH

二、下载参考例程:

        昇腾的sample仓(https://gitee.com/ascend/samples.git)提供了AscendC算子的代码例程,目前有两处:

1)samples/operator目录下,有“AddCustomSample”和“LeakyReLUCustomSample”两个例程。

2)samples/ cplusplus / level1_single_api / 4_op_dev / 6_ascendc_custom_op目录下,有一些例程。

        两部分例程都很好,第2个目录下例程知识点比较全,第1个目录下例程比较简单且结构更为简洁,比较适合拿来作为初学的样板做练习。本文以“samples/operator目录”下的AddCustomSample工程作为模板。

三、开发过程

1、算子分析

1)先去官网找一下,算子的定义https://pytorch.org/docs/stable/generated/torch.nn.LeakyReLU.html#torch.nn.LeakyReLU,然后在pytorch环境下,运行下算子,了解各种参数的意义。

2)分析计算实现的方法,查找AscendC官方文档,在标量双目指令中有LeakyRelu算子,本例可以直接使用。对没有直接API实现的算子,需要先分解成基本API的组合。

3)出算子设计规格表:假定输入类型为float16,形状8 * 1024(和AddCustom保持一致),且不考虑“inplace的情形

        关于算子分析可参看“文档首页>CANN社区版>7.0.RC1.alpha003>算子开发>Ascend C算子开发>算子开发(基础篇)>矢量编程>算子分析 昇腾社区-官网丨昇腾万里 让智能无所不及”,

        如下图所示:左侧是Add例程,右侧是参考例程写出的“LeakyReluCustom”。红色的是有差异的地方,请记住这些或者等我们完成代码编写后再回头看一下这儿,会发现所做的修改都和此处有关系。

2、编写LeakyReLU算子原型定义json文件

        为了方便描述,我将add_custom.json输入类型做了简化,仅保留了float16。根据上面的分析表,将输入和输出写对就可以了,这里面需要注意“属性”的写法。具体写法可参考文档:“文档首页>CANN社区版>7.0.RC1.alpha003>算子开发>Ascend C算子开发>算子开发(进阶篇)>基于msopgen工具创建算子工程

昇腾社区-官网丨昇腾万里 让智能无所不及

3、生成算子工程

        使用msopgen生成算子工程。

/home/ma-user/Ascend/ascend-toolkit/latest/python/site-packages/bin/msopgen gen -i ./leakyrely_custom.json -c ai_core-ascend910,ai_core-ascend910B,ai_core-ascend310p -lan cpp -out ./LeakyReLUCustom

      关于msopgen的使用,请参考昇腾社区的文档“文档首页>CANN社区版>7.0.RC1.alpha003>算子开发>Ascend C算子开发>附录>msopgen工具使用>使用说明”

昇腾社区-官网丨昇腾万里 让智能无所不及

      参数含义:

  • -i:指定算子原型定义文件add_custom.json所在路径,请根据实际情况修改。
  • -c:ai_core-<soc_version>代表算子在AI Core上执行,<soc_version>为昇腾AI处理器的型号。多个处理器之间用逗号“,”分割。
  • -lan: 参数cpp代表算子基于Ascend C编程框架,使用C++编程语言开发。
  • -out:生成文件所在路径,可配置为绝对路径或者相对路径,并且工具执行用户对路径具有可读写权限。若不配置,则默认生成在执行命令的当前路径。

      生成的目录结构如下图右侧:此处主要修改三个文件:算子tiling定义文件;host侧实现文件;kernel侧实现文件。

4、修改device侧和host侧的代码

     1)修改host/leaky_relu_custom_tiling.h

      tilingdata是用来计算数据切分的,在固定算子shape的情形下,只需要totalLength和tileNum即可,leakyreluCustom算子需要传递value的属性值,所以增加1个value。改动处都用红框标记了,这里需要注意的就是增加value那一行。进一步学习,可参考“文档首页>CANN社区版>7.0.RC1.alpha003>算子开发>Ascend C算子开发>算子开发(进阶篇)>host侧算子实现>Tiling实现 昇腾社区-官网丨昇腾万里 让智能无所不及”。

      2)修改host/leaky_relu_custom.cpp代码

      host侧算子实现开发包括Tiling实现、Shape推导等函数实现、算子原型注册。进一步学习,可参考“文档首页>CANN社区版>7.0.RC1.alpha003>算子开发>Ascend C算子开发>算子开发(进阶篇)>host侧算子实现>host侧算子实现概述 昇腾社区-官网丨昇腾万里 让智能无所不及

      首先是Tiling实现,这里面需要注意的是获取算子value属性代码,其余的可以和例程保持一致即可。GetAttrs()可在API参考里查找。“文档首页>CANN社区版>7.0.RC1.alpha003>算子开发>Ascend C算子开发>API参考>Host侧实现API>basics>ComputeNodeInfo类>GetAttrs”

      Shape推导的代码不用修改,输出shape和输入shape保持一致。

      算子原型注册代码有差异,仔细看的话,会发现和算子原型定义的那个json文件是一致的。但此处通过工具自动生成,也不需要修改。请注意右侧用红框标出的部分,特别是关于属性的定义“this->Attr("value").Float();”

      3)修改device侧代码

      这是算子编程的很重要的部分,需要修改的地方如下图所示。进一步可阅读“文档首页>CANN社区版>7.0.RC1.alpha003>算子开发>Ascend C算子开发>算子开发(进阶篇)>kernel侧算子实现 昇腾社区-官网丨昇腾万里 让智能无所不及”。

      算子类实现代码,进一步阅读,参考“文档首页>CANN社区版>7.0.RC1.alpha003>算子开发>Ascend C算子开发>算子开发(基础篇)>矢量编程>算子类实现 昇腾社区-官网丨昇腾万里 让智能无所不及”。这部分内容建议刚开始学的小伙伴要好好看明白,这里面包含了“多核并行”,“编程范式”,“流水线”等众多AscendC编程思想。虽说以后编写代码也可以ctrl+c,和ctrl+v,但理解了就能一劳永逸。

5、编写简单核函数调用代码

      简单、粗暴,直接将例程的“kernel_direct_call"目录拷过来,然后将下图中的三个文件改名。这三个文件,加上run.sh,我们只需要修改这四个文件,就可以完成调用代码的改写。这部分内容,有兴趣的小伙伴,参看“文档首页>CANN社区版>7.0.RC1.alpha003>算子开发>Ascend C算子开发>算子开发(基础篇)>核函数运行验证 昇腾社区-官网丨昇腾万里 让智能无所不及

      1)kernel_call_leakyrelu_custom_tiling.h

      这个文件,是模拟算子工程host侧算子实现和kernel侧算子实现,传递tilingdata数据用的。改动很小,但需要花时间去好好阅读下,感觉此处解密了AscendC算子这一功能的实现方式。

      2)kernle_call_leakyrelu_custom.py

      此脚本,生成输入数据,然后计算出输出数据,并落盘,然后再run.sh脚本里与AscendC算子的结果进行比较。

      3)main.cpp

      这里面是分别cpu和npu方式调用核函数的代码,“孪生调试”。代码修改主要就是名称,函数的参数数量之类,版面首限,也没有办法展开,不再赘述。此处需要注意的是读取tilingdata模拟数据时,因为增加了算子属性value,所以读取长度,需要再原来的基础上增加sizeof(float)。

      4)run.sh

      主要修改2处:

      修改算子工程名

      需要将算子工程op_kernel的算子实现代码拷贝到核函数调用工程里,需要对路径名和文件名做相应修改。run.sh其余部分无需修改,它是通过比较kernle_call_leakyrelu_custom.py和AscendC算子对相同数据的计算结果的md5sum,来判断结果是否一致的。

四、编译运行

1、CPU模式

      1)将代码做成zip格式的压缩包,启动环境,将代码上传到环境。

      2)安装toolkit和算子开发包。(参见第一节,搭建cpu开发运行环境章节)

      3)解压缩。并给算子工程“/util”目录下文件,赋权限750,否则会报错。

      4)编译运行

      进入“kernel_direct_call”目录,运行“bash run.sh ascend910B1 cpu”

      运行结果:

2、npu模式

      参考“AscendC算子NPU开发调试环境成功搭建篇https://www.bilibili.com/read/cv26991439/ ”搭建NPU的调试运行环境。并把代码搞里头,过程参考cpu模式。

      进入kernel_direct_call目录,运行 bash run.sh ascend910B1 npu_onboard 

      运行结果如下:

      至此,完成了leakyrelu算子的开发和核函数调用方式的验证。

附录:

      看到这儿的小伙伴,如果还没有加入CANN训练营,可以按下面的链接进行报名,不仅可以学到基于昇腾AI开发的知识,还有大奖可以拿。

报名链接:https://www.hiascend.com/developer/activities/details/84b950830fc44476851860f51f0873a2/signup?channelCode=0&recommended=234384

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值