算子开发能力中级认证需要补充完整两方面代码,分别为:
op_host侧(sigmoid_custom_tiling.h+sigmoid_custom.cpp)
op_kernel侧(sigmoid_custom.cpp)
op_host侧:
sigmoid_custom_tiling.h:
这里我们需要定义一个tiling结构体以便后续用来给张量分块,结构体包含两个参数即可,一个为要处理张量的总长度(张量元素总数),另一个为一个AIcore要处理的数据块数,定义结构体用宏来实现,定义结构体模板如下:
BEDIN_TILING_DATA_DEF(结构体名)//开始定义结构体
TILING_DATA_FIELD_DEF(变量类型 变量名);//定义一个成员变量
....//定义..个成员变量
END_TILING_DATA_DEF();//结束结构体定义
结构体定义完成后,要将“自定义tiling数据结构”与“目标算子绑定”,注册到昇腾框架中
使用宏 REGISTER_TILING_DATA_CLASS(算子名,结构体名)
具体实现如下:
sigmoid_custom.cpp:
这里我们主要要在TilingFunc函数中实现对tiling对象成员变量的赋值,并将tiling对象与要在多少个AIcore上运行的变量保存到context中。
context是什么?context是昇腾AI框架与自定义算子之间“数据交互与控制中枢”,他封装了算子运行的关键信息(包括输入输出形状,Tiling参数,硬件配置等)
我们要实现tiling结构体,就要对结构体中的成员变量赋值。我们可以将tile_num设置为8,而totallength的值从context获取(上段中所说context中存储了输入输出形状,我们可以从context中获取张量的元素总数)
获取元素总数步骤如下:1 获取第0个张量的形状对象
context->GetInputShape(0)
2 从张量的形状对象中获取张量的原始形状(未经过框架自动优化调整的形状)
context->GetInputShape(0)->GetOriginShape()
3 计算张量所有维度的乘积,调用GetShapeSize()函数即可
context->GetInputShape(0)->GetOriginShape().GetShapeSize();
tiling对象成员变量的设置,用到了两个函数
这样我们tiling对象的成员变量都已经设置好了,接下来要将tiling保存到context中,并将tiling的总字节数告诉context,实现代码如下:
context->GetRawTilingData()获得框架预分配的原始tiling数据缓冲区对象
context->GetRawTilingData()->GetData()获得上述缓冲区的首地址
context->GetRawTilingData()->GetCapacity()获得上述缓冲区的最大容量
context->GetRawTilingData()->SetDataSize(tiling.GetDataSize())将tiling对象的真实大小保存到缓冲区中
需要用到的核心数自己定义,并通过context->SetBlockDim(核心数)保存到context中
此外,还需要申请算子运行时可能需要临时内存空间(该空间位于Global Memory中)我们这里不需要这片空间,以下是声明无需这片空间的写法:
host侧搞定
op_kernel侧(sigmoid_custom.cpp):
先写初始化函数 Init,我们要在其中实现在获取在Global Memory 中的本AIcore要处理的张量块首地址以及要处理的元素个数,要处理的元素个数=张量总元素个数/使用的核心数
要处理的张量首地址=总张量首地址+一个核心要处理的元素数量(this->blockLength = totalLength / GetBlockNum();//GetBlockNum()获取一共有多少个核心)*核心id
在 Ascend C(以及 C/C++ 的指针运算逻辑)中,blockLength不需要手动乘以元素大小,核心原因是指针的 “强类型特性” 会自动处理元素大小的偏移计算
同时要获取计算完要存储的位置的首地址和元素个数
我们还要初始化输入输出队列
初始化队列的三个参数(队列名,缓冲区个数,单个缓冲区大小)单个缓冲区的大小就是要处理的最小元素块大小 this->tileLength = this->blockLength / tileNum / BUFFER_NUM;为一个最小元素块包含的元素个数
pipe为管道对象,在算子类的私有变量中定义:
上述代码已经写好,不需要改变 TQue<QuePosition::逻辑位置, 缓冲区个数> inQueueX;
次数init函数大致写好,后续还需要在此处为零时缓冲区初始化
接下来要补充对Process()函数中的“loopCount”的定义,要循环次数就是一个核心要处理的最小块的个数,也就是一个核心要处理的元素数/最小快包含的元素数 int32_t loopCount = this->blockLength / this->tileLength;
以上用到的变量定义在算子类私有变量中
copyin:
思路:定义本地张量,将gm上的数据复制到本地张量中并让本地张量入队参与后续计算
DataCopy(目的张量,源张量,处理元素个数)
Compute:
需要取出本地张量
创建一个本地张量存储计算结果
本地张量xlocal进过一系列计算(调用各种api(用到的api有
)),将结果保存到ylocal中,并入队输出队列
期间需要用到一些临时缓冲区用于中间计算,临时缓冲区定义在算子类的私有变量中
init中临时缓冲区的初始化
计算中会需要一些标量,定义如下
DTYPE_X reallone=static_cast<DTYPE_X>(-1.0f);
最后需要释放xlocal
inQueueX.FreeTensor(xLocal);
copyout:
将定义ylocal,从输出队列中取出数据块给ylocal,并将ylocal传输到gm中,最后释放ylocal
最后补充核函数中算子对象调用初始化与执行的代码
环境搭建参考中级认证给的华为云环境搭建文档
每次启动华为云之后执行一下两条指令
source ~/.bashrc
source /home/ma-user/Ascend/ascend-toolkit/set_env.sh
完结
算子开发能力中级认证需要补充完整两方面代码,分别为:
op_host侧(sigmoid_custom_tiling.h+sigmoid_custom.cpp)
op_kernel侧(sigmoid_custom.cpp)
op_host侧:
sigmoid_custom_tiling.h:
这里我们需要定义一个tiling结构体以便后续用来给张量分块,结构体包含两个参数即可,一个为要处理张量的总长度(张量元素总数),另一个为一个AIcore要处理的数据块数,定义结构体用宏来实现,定义结构体模板如下:
BEDIN_TILING_DATA_DEF(结构体名)//开始定义结构体
TILING_DATA_FIELD_DEF(变量类型 变量名);//定义一个成员变量
....//定义..个成员变量
END_TILING_DATA_DEF();//结束结构体定义
结构体定义完成后,要将“自定义tiling数据结构”与“目标算子绑定”,注册到昇腾框架中
使用宏 REGISTER_TILING_DATA_CLASS(算子名,结构体名)
具体实现如下:
namespace optiling { BEGIN_TILING_DATA_DEF(SigmoidCustomTilingData) //考生自行定义tiling结构体成员变量 TILING_DATA_FIELD_DEF(uint32_t, totalLength); TILING_DATA_FIELD_DEF(uint32_t, tileNum); END_TILING_DATA_DEF; REGISTER_TILING_DATA_CLASS(SigmoidCustom, SigmoidCustomTilingData) }sigmoid_custom.cpp:
这里我们主要要在TilingFunc函数中实现对tiling对象成员变量的赋值,并将tiling对象与要在多少个AIcore上运行的变量保存到context中。
context是什么?context是昇腾AI框架与自定义算子之间“数据交互与控制中枢”,他封装了算子运行的关键信息(包括输入输出形状,Tiling参数,硬件配置等)
我们要实现tiling结构体,就要对结构体中的成员变量赋值。我们可以将tile_num设置为8,而totallength的值从context获取(上段中所说context中存储了输入输出形状,我们可以从context中获取张量的元素总数)
获取元素总数步骤如下:1 获取第0个张量的形状对象
context->GetInputShape(0)
2 从张量的形状对象中获取张量的原始形状(未经过框架自动优化调整的形状)
context->GetInputShape(0)->GetOriginShape()
3 计算张量所有维度的乘积,调用GetShapeSize()函数即可
context->GetInputShape(0)->GetOriginShape().GetShapeSize();
tiling对象成员变量的设置,用到了两个函数
这样我们tiling对象的成员变量都已经设置好了,接下来要将tiling保存到context中,并将tiling的总字节数告诉context,实现代码如下:
context->GetRawTilingData()获得框架预分配的原始tiling数据缓冲区对象
context->GetRawTilingData()->GetData()获得上述缓冲区的首地址
context->GetRawTilingData()->GetCapacity()获得上述缓冲区的最大容量
context->GetRawTilingData()->SetDataSize(tiling.GetDataSize())将tiling对象的真实大小保存到缓冲区中
需要用到的核心数自己定义,并通过context->SetBlockDim(核心数)保存到context中
此外,还需要申请算子运行时可能需要临时内存空间(该空间位于Global Memory中)我们这里不需要这片空间,以下是声明无需这片空间的写法:
host侧搞定
op_kernel侧(sigmoid_custom.cpp):
先写初始化函数 Init,我们要在其中实现在获取在Global Memory 中的本AIcore要处理的张量块首地址以及要处理的元素个数,要处理的元素个数=张量总元素个数/使用的核心数
要处理的张量首地址=总张量首地址+一个核心要处理的元素数量(this->blockLength = totalLength / GetBlockNum();//GetBlockNum()获取一共有多少个核心)*核心id
xGm.SetGlobalBuffer((__gm__ DTYPE_X *)x + this->blockLength * GetBlockIdx(), this->blockLength);在 Ascend C(以及 C/C++ 的指针运算逻辑)中,
blockLength不需要手动乘以元素大小,核心原因是指针的 “强类型特性” 会自动处理元素大小的偏移计算同时要获取计算完要存储的位置的首地址和元素个数
yGm.SetGlobalBuffer((__gm__ DTYPE_Y *)y + this->blockLength * GetBlockIdx(), this->blockLength);我们还要初始化输入输出队列
初始化队列的三个参数(队列名,缓冲区个数,单个缓冲区大小)单个缓冲区的大小就是要处理的最小元素块大小 this->tileLength = this->blockLength / tileNum / BUFFER_NUM;为一个最小元素块包含的元素个数
pipe为管道对象,在算子类的私有变量中定义:
TPipe pipe; //create queue for input, in this case depth is equal to buffer num TQue<QuePosition::VECIN, BUFFER_NUM> inQueueX; //create queue for output, in this case depth is equal to buffer num TQue<QuePosition::VECOUT, BUFFER_NUM> outQueueY;上述代码已经写好,不需要改变 TQue<QuePosition::逻辑位置, 缓冲区个数> inQueueX;
次数init函数大致写好,后续还需要在此处为零时缓冲区初始化
接下来要补充对Process()函数中的“loopCount”的定义,要循环次数就是一个核心要处理的最小块的个数,也就是一个核心要处理的元素数/最小快包含的元素数 int32_t loopCount = this->blockLength / this->tileLength;
以上用到的变量定义在算子类私有变量中
uint32_t blockLength; uint32_t tileNum; uint32_t tileLength;copyin:
思路:定义本地张量,将gm上的数据复制到本地张量中并让本地张量入队参与后续计算
LocalTensor<DTYPE_X> xLocal = inQueueX.AllocTensor<DTYPE_X>(); DataCopy(xLocal, xGm[progress * this->tileLength ], this->tileLength); inQueueX.EnQue(xLocal);DataCopy(目的张量,源张量,处理元素个数)
Compute:
需要取出本地张量
// 从输入队列中取出当前块的数据 LocalTensor<DTYPE_X> xLocal=inQueueX.DeQue<DTYPE_X>();创建一个本地张量存储计算结果
// 从输出队列分配空间用于存储结果 LocalTensor<DTYPE_Y> yLocal=outQueueY.AllocTensor<DTYPE_Y>();本地张量xlocal进过一系列计算(调用各种api(用到的api有
Muls(tmpTensor1, xLocal, inputVal1, this->tileLength); Exp(tmpTensor2, tmpTensor1, this->tileLength); Adds(tmpTensor3, tmpTensor2, inputVal2, this->tileLength); Duplicate(tmpTensor4,inputVal2,this->tileLength); Div(yLocal,tmpTensor4,tmpTensor3,this->tileLength);)),将结果保存到ylocal中,并入队输出队列
outQueueY.EnQue<DTYPE_Y>(yLocal); // 释放输入数据占用的空间 inQueueX.FreeTensor(xLocal);期间需要用到一些临时缓冲区用于中间计算,临时缓冲区定义在算子类的私有变量中
init中临时缓冲区的初始化
pipe.InitBuffer(tmpBuffer1, this->tileLength * sizeof(DTYPE_Y)); pipe.InitBuffer(tmpBuffer2, this->tileLength * sizeof(DTYPE_Y)); pipe.InitBuffer(tmpBuffer3, this->tileLength * sizeof(DTYPE_Y)); pipe.InitBuffer(tmpBuffer4, this->tileLength * sizeof(DTYPE_Y));计算中会需要一些标量,定义如下
DTYPE_X reallone=static_cast<DTYPE_X>(-1.0f);
最后需要释放xlocal
inQueueX.FreeTensor(xLocal);
copyout:
将定义ylocal,从输出队列中取出数据块给ylocal,并将ylocal传输到gm中,最后释放ylocal
AscendC::LocalTensor<half> yLocal = outQueueY.DeQue<half>(); DataCopy(yGm[progress * this->tileLength], yLocal, this->tileLength); outQueueY.FreeTensor(yLocal);最后补充核函数中算子对象调用初始化与执行的代码
//补充init和process函数调用内容 op.Init(x, y, tiling_data.totalLength, tiling_data.tileNum); op.Process();环境搭建参考中级认证给的华为云环境搭建文档
每次启动华为云之后执行一下两条指令
source ~/.bashrc
source /home/ma-user/Ascend/ascend-toolkit/set_env.sh
完结