Ascend C Tilling计算
Tilling基本概念介绍
大多数情况下,Local Memory的存储,无法完全容纳算子的输入与输出的所有数据,需要每次搬运一部分输入数柜进行计算然后搬出,再敲运下一部分输入数据进行计算,直到得到完愁的最终结果,这个数据切分、分块计算的过程称之为Tiling过程
- 每次激运的那一部分数据块,叫做Tiling块
- 根据算子中不同输入形状确定搬入基本块大小的相关算法,叫做Tiling算法(或Tiing策略)
- 算子中实现Tiling算法的函数(一般定义在host侧的tiling头文件中),叫做Tiling函数 (或Tiling Function)
在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操作
- aclrtMallocHost((void**)(&tilingHost),tilingsize)
- acIrtMalloc((void**)&tilingDevice,tilingSize,ACL_MEM_MALLOC_HUGE_FIRST)
- aclrtMemcpy(tilingDevice, tilingsize, tilingHost, tilingsize, ACL_MEMCPY_HOST_TO_DEVICE)
固定Shape场景的tiling实现
固定shape输入回顾
由于输入的大小是已知的,每次搬运多少数据,总共搬运多少次均可以在编译时直接计算出来
当算子shape固定时,开发者使用不同shape时需要重新对算子进行编译,带来大量的算子二进制文件
动态shape的算子可以将形状通过核函数的入参传入核函数内,参与内部逻辑计算,从而符合不同shape下的使用场景
改装固定shape算子成功态shape
基于现有的固定shape算子,将其改装为动态shape的算子,将控制形状的BLOCK_DIM,TOTAL_LENGTH,TILE_NUM这些变量的来源变化为依靠外界输入得到,在核函数中额外传入一个tiling,它将指向控制核函数逻辑处理的至关重要的几个变量
动态shape场景的tiling结构体
主要操作流程:
Tiling结构体中的信息:
TOTAL_LENGTH: 总共需要计算的数据个数
TILE_NUM:每个核上计算数据分块的个数
注:USE_CORE_NUM为参与并行计算使用的核数,有独立接口GetBlockNum()可以在核函数内获得
动态shape场景的tiling解析函数
核函数传入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暴露给核函数进行调用