Ascend Pytorch算子适配层开发

适配方法

找到和PyTorch算子功能对应的NPU TBE算子,根据算子功能计算出输出Tensor的size,再根据TBE算子原型构造对应的input/output/attr,传递给ACL完成TBE算子的执行。

说明:

TBE算子实现的源文件存放路径由开发套件包Toolkit的安装方式决定:

  • 若使用root用户安装,则存放在:/usr/local/Ascend/ascend-toolkit/latest/opp/op_impl/built-in/ai_core/tbe/impl/
  • 若使用非root用户安装,则存放在:~/.local/Ascend/ascend-toolkit/latest/opp/op_impl/built-in/ai_core/tbe/impl/

开发者可以通过查看算子实现源文件,确定算子的功能。

存放路径和命名格式

对NPU的TBE算子适配文件保存在pytorch/aten/src/ATen/native/npu目录下,命名风格采用大驼峰,命名格式:<算子名> + <KernelNpu>.cpp,如:AddKernelNpu.cpp。

适配步骤

须知:

适配代码基于C++开发。

  1. 引入依赖头文件。
  1. 2.  #include "ATen/native/npu/utils/CalcuOpUtil.h"
  1. 3.  #include "ATen/native/npu/utils/KernelNpuOutputSize.h"
  1. #include "ATen/native/npu/utils/NpuUtils.h"

说明:

"CalcuOpUtil.h"文件中主要包含与ACL接口相关的函数。

"KernelNpuOutputSize.h"中主要包含算子输出shape的推导函数。

"NpuUtils.h"文件中主要包含公共能力的函数。

  1. 定义Add算子适配主体函数。

结合native_functions.yaml 中 add算子的分发定义,算子适配中应包含如下函数:

  • add_npu_input 构造输入的NPUTensorDesc对象
  • add_npu_output 构造输出的NPUTensorDesc对象
  • add_npu_attr 构造NPU TBE Add算子attr属性
  • add_out_npu 算子适配函数(yaml中npu派发函数,支持传入输出tensor),other参数支持 Tensor & Scalar
  • add_npu 算子适配函数(yaml中npu派发函数),other参数支持 Tensor & Scalar
  1. 实现函数 add_npu_input。

将NPU适配函数(add_npu_input)的输入构造成NPUTensorDesc对象。

  1. // 输入参数为"self": "Tensor"和"other": "Tensor"时,适配函数add_npu_input的实现
  1. SmallVector<NPUTensorDesc, N> add_npu_input(const Tensor& self,const Tensor& other) {
  1.     bool isSelfWrapped = CalcuOpUtil::is_scalar_wrapped_to_tensor(self);
  1.     bool isOtherWrapped = CalcuOpUtil::is_scalar_wrapped_to_tensor(other);
  1.     auto inputs = CalcuOpUtil::create_npu_input_tensor_desc({self, other});
  1.  
  1.     // 't + 2' to work with any type of tensor, not just LongTensor (which is what 
  1.     // integersin Python represent).
  1.     if (isSelfWrapped && (!isOtherWrapped)) {
  1.         inputs[0].scalarType = other.scalar_type(); 
  1.     } else if (isOtherWrapped && (!isSelfWrapped)) {
  1.         inputs[1].scalarType = self.scalar_type(); 
  1.     }
  1.  
  1.     return inputs;
  1. }
  1. // 输入参数为"self": "Tensor"和"other": "Scalar"时,适配函数add_npu_input的实现
  1. SmallVector<NPUTensorDesc, N> add_npu_input(const Tensor& self,const Scalar& other) {
  1.     return CalcuOpUtil::create_npu_input_tensor_desc({self});
  1. }
  1. 实现函数 add_npu_output。

将函数 add_npu_output的输出tensor对象构造成NPUTensorDesc对象。

  1. // 输出参数为 "Tensor" 时,适配函数add_npu_output的实现
  1. SmallVector<NPUTensorDesc, N> add_npu_output(const Tensor& result) {
  1.     return CalcuOpUtil::create_npu_output_tensor_desc({result});
  1. }

说明:

一般来说,算子的输出不需要特殊处理,直接调用CreateNpuOutputTensorDesc即可。

  1. 实现函数 add_npu_attr。

根据NPU TBE算子原型中所需的attr规格,将参数适配成NPU TBE算子原型所需要的attr属性。

  1. // 输入参数为"other": "Tensor"和"alpha": "Scalar"时,对应的适配函数add_npu_attr实现
  1. SmallVector<NPUAttrDesc, N> add_npu_attr(const Tensor& self, const Tensor& other, Scalar alpha) {
  1.     float value = CalcuOpUtil::get_scalar_float_value(alpha);
  1.     NPUAttrDesc npuAttrScalar = NPUAttrDesc("alpha", value);
  1.     SmallVector<NPUAttrDesc, N> attrs = {npuAttrScalar};
  1.     return attrs;
  1. }
  1. // 输入参数为"other": "Scalar"和"alpha": "Scalar"时,对应的适配函数adds_npu_attr实现
  1. SmallVector<NPUAttrDesc, N> adds_npu_attr(const Tensor& self,const Scalar& other,const Scalar& alpha) {
  1.     float otherValue = CalcuOpUtil::get_scalar_float_value(other);
  1.     float alphaValue = CalcuOpUtil::get_scalar_float_value(alpha);
  1.     float value = otherValue * alphaValue; 
  1.     NPUAttrDesc npuAttrValue = NPUAttrDesc("value", value); 
  1.     SmallVector<NPUAttrDesc, N> attrs = {npuAttrValue};
  1.     return attrs;
  1. }
  1. 实现函数 add_out_npu。
  1. 9.  Tensor& add_out_npu(Tensor& result, const Tensor& self, const Tensor& other, Scalar alpha) {
  1. 10.     if (other.dim() == 0 && !other.is_npu()) {
  1. 11.         adds_out_npu(result, self, other.item(), alpha);
  1. 12.     } else if (self.dim() == 0 && !self.is_npu()) {
  1. 13.         adds_out_npu(result, other, self.item(), alpha); 
  1. 14.     } else {
  1. 15.         // constructs the input and output NPUTensorDesc
  1. 16.         auto inputs = add_npu_input(self, other);
  1. 17.         auto outputs = add_npu_output({result});
  1. 18.  
  1. 19.         // constructs the attr of the NPUAttrDesc
  1. 20.         auto attrs = add_npu_attr(self, other, alpha);
  1. 21.         // executing the NPU operator  
  1. 22.         CalcuOpUtil::execute_npu_operate("Axpy", inputs, outputs, attrs); 
  1. 23.     }
  1. 24.  
  1. 25.     return result;
  1. }

说明:

add_out_npu和add_npu的差别是add_out_npu支持显示指定输出tensor,往输出tensor中写入结果。

  1. 实现函数 add_npu。
  2. 定义并实现算子的shape推导函数,根据输入参数计算输出的size。

Shape推导函数定义规范:

  1. "NPU适配函数名称" + "_" + "output" + "_" + "size",如add_npu_output_size();

说明:

  • Shape推导函数定义和实现存放在 pytorch/aten/src/ATen/native/npu/utils,对应的头文件和实现在 KernelNpuOutPutSize.h 和 KernelNpuOutPutSize.cpp中。
  • 在KernelNpuOutPutSize.h中,函数存放位置按照函数名字排序。
  1. //输入参数为"self": "Tensor"和"other": "Tensor"时,Shape推导该函数
  1. SmallVector<int64_t, SIZE> add_npu_output_size(const Tensor& self,const Tensor& other) {
  1.     return broadcast_ops_npu_output_size(self, other);    //定义Shape推导函数
  1. }
  1.  
  1. // 输入参数为"self": "Tensor"和"other": "Scalar"时,Shape推导该函数
  1. IntArrayRef add_npu_output_size(const Tensor& self, const Scalar& other) {
  1.     return input_same_output_size(self);    
  1. }

说明:

broadcast_ops_npu_output_size函数的作用是:当两个参数符合PyTorch广播机制时,函数会将两个参数自动扩展为相等大小

  1. 调用对应的shape推导函数计算输出的size。
  2. 根据输出的size调用at::empty_with_ format创建输出Tensor,函数支持指定输出Tensor的format,默认为NCHW格式。

说明:

当前制定的Format设置规则为重型算子锚点扩散+连续性法则混合规则。

  • 重型算子如卷积、Matmul,只支持某种特定format,适配时显示指定为其需要的format,format向周边扩散。
  • 而连续性法则指的是算子对格式不敏感,算子format指定为与第一个输入tensor的format相同即可。
  • NPU中的卷积只支持NC1HWC0格式,所以需要显式指定为NC1HWC0格式
  1. 将构造好的输出Tensor和其他参数传给add_out_npu进行运算
  1. e.  // 输入参数为"self": "Tensor"和"other": "Tensor"时,对应的适配函数add_npu实现
  1. f.  //调用对应的Shape推导函数计算输出的size
  1. g.  Tensor add_npu(const Tensor& self, const Tensor& other, Scalar alpha) {
  1. h.      Tensor outputTensor = add_dest_output(self, other);
  1. i.      auto outputSize = add_npu_output_size(self, other);  
  1. j.   
  1. k.  //根据输出的size调用at::empty_with_format创建输出Tensor,函数支持指定输出Tensor的format,默认为NCHW格式
  1. l.      Tensor result = at::empty_with_format(outputSize, outputTensor.options(), CalcuOpUtil::get_tensor_npu_format(outputTensor));
  1. m.   
  1. n.  //将构造好的输出Tensor和其他参数传给add_out_npu进行运算
  1. o.      add_out_npu(result, self, other, alpha);
  1. p.      return result;
  1. q.  }
  1. r.   
  1. s.  // 输入参数为"self": "Tensor"和"other": "Scalar"时,对应的适配函数add_npu实现
  1. t.  //调用对应的Shape推导函数计算输出的size
  1. u.  Tensor add_npu(const Tensor& self, Scalar other, Scalar alpha) {
  1. v.      auto outputSize = add_npu_output_size(self, other); 
  1. w.    
  1. x.  //根据输出的size调用at::empty_with_format创建输出Tensor,函数支持指定输出Tensor的format,默认为NCHW格式
  1. y.      Tensor result = at::empty_with_format(outputSize, self.options(), CalcuOpUtil::get_tensor_npu_format(self)); 
  1. z.      
  1. aa. //将构造好的输出Tensor和其他参数传给add_out_npu进行运算
  1. bb.     adds_out_npu(result, self, other, alpha);
  1. cc.     return result;
  1. }

Ascend Pytorch算子适配层开发的更多相关文章

  1. Ascend Pytorch算子功能验证

    Ascend Pytorch算子功能验证 编写测试用例 以add算子为例,测试脚本文件命名为:add_testcase.py.以下示例仅为一个简单的用例实现,具体算子的实现,需要根据算子定义进行完整的 ...

  2. 使用xshell+xmanager+pycharm搭建pytorch远程调试开发环境

    1. 相关软件版本 xshell: xmanager: pycharm: pycharm破解服务器:https://jetlicense.nss.im/ 2. 将相应的软件安装(pojie好) a&g ...

  3. 深入浅出PyTorch(算子篇)

    Tensor 自从张量(Tensor)计算这个概念出现后,神经网络的算法就可以看作是一系列的张量计算.所谓的张量,它原本是个数学概念,表示各种向量或者数值之间的关系.PyTorch的张量(torch. ...

  4. 昇思MindSpore全场景AI框架 1.6版本,更高的开发效率,更好地服务开发者

    摘要:本文带大家快速浏览昇思MindSpore全场景AI框架1.6版本的关键特性. 全新的昇思MindSpore全场景AI框架1.6版本已发布,此版本中昇思MindSpore全场景AI框架易用性不断改 ...

  5. 带你学习MindSpore中算子使用方法

    摘要:本文分享下MindSpore中算子的使用和遇到问题时的解决方法. 本文分享自华为云社区<[MindSpore易点通]算子使用问题与解决方法>,作者:chengxiaoli. 简介 算 ...

  6. pytorch 入门指南

    两类深度学习框架的优缺点 动态图(PyTorch) 计算图的进行与代码的运行时同时进行的. 静态图(Tensorflow <2.0) 自建命名体系 自建时序控制 难以介入 使用深度学习框架的优点 ...

  7. 深度学习框架PyTorch一书的学习-第五章-常用工具模块

    https://github.com/chenyuntc/pytorch-book/blob/v1.0/chapter5-常用工具/chapter5.ipynb 希望大家直接到上面的网址去查看代码,下 ...

  8. Halcon算子学习

    * dev前缀的算子表示开发编译器相关的函数 dev_update_window ('off')//halcon编辑器图形输出界面参数是否都更新,off表示手动,on表示全部更新 dev_close_ ...

  9. 那些最全面的Windows10安装pytorch踩过的坑以及如何应用

    那些最全面的Windows10安装pytorch踩过的坑以及如何应用 一.pytorch简介 2017年1月,由Facebook人工智能研究院(FAIR)基于Torch推出了PyTorch.它是一个基 ...

随机推荐

  1. hdu4115 2sat

    题意:       两个人玩剪刀石头布,他们玩了n把,给了你A这n把都出了什么,问你B能否会赢,其中A会限制B某些局数出的要相同,某些局数出的要不同,只要B满足他的限制,并且没没有输掉任何一把就算赢( ...

  2. 病毒木马查杀实战第017篇:U盘病毒之专杀工具的编写

    前言 经过前几次的讨论,我们对于这次的U盘病毒已经有了一定的了解,那么这次我们就依据病毒的行为特征,来编写针对于这次U盘病毒的专杀工具. 专杀工具功能说明 因为这次是一个U盘病毒,所以我打算把这次的专 ...

  3. 逆向 stdio.h 函数库 fseek 函数(调试版本)

    0x01 fseek 函数 函数原型:int fseek(FILE *stream, long int offset, int whence) 函数功能:设置流 stream 的文件位置为给定的偏移 ...

  4. C++ STL 思维导图,脑图,树形图。

    https://blog.csdn.net/weixin_41743247/article/details/90635931

  5. Day006 什么是方法

    什么是方法? ​ 方法是语句的集合,他们在一起执行一个功能. 方法是解决一类问题的步骤的有序集合. 方法包含于类和对象中. 方法在程序中被创建,在其他地方被引用. 设计方法的原则 方法的本意是功能块, ...

  6. 关于Aborted connection告警日志的分析

    前言: 有时候,连接MySQL的会话经常会异常退出,错误日志里会看到"Got an error reading communication packets"类型的告警.本篇文章我们 ...

  7. CRM系统有哪几种常见类型?

    随着市场的快速变化,客户开始变得越来越重要,因此CRM客户管理系统开始逐渐被企业所认可.从CRM系统进入中国市场到现在十余年的发展中,越来越多的CRM厂商开始出现.为了满足不同行业.不同类型的企业的需 ...

  8. Map&Set的理解

    Set子接口 特点:无序.无下标.元素不可重复. 方法:全部继承自Collection中的方法. Set实现类 HashSet: 基于HashCode实现了不重复. 当存入元素的哈希码相同时,会调用e ...

  9. Java关键字(八)——synchronized

    synchronized 这个关键字,我相信对于并发编程有一定了解的人,一定会特别熟悉,对于一些可能在多线程环境下可能会有并发问题的代码,或者方法,直接加上synchronized,问题就搞定了. 但 ...

  10. [源码分析] 定时任务调度框架 Quartz 之 故障切换

    [源码分析] 定时任务调度框架 Quartz 之 故障切换 目录 [源码分析] 定时任务调度框架 Quartz 之 故障切换 0x00 摘要 0x01 基础概念 1.1 分布式 1.1.1 功能方面 ...