开发者
资源
[object Object]

如下图中的示例,算子的输入shape为(1,2048),支持的数据类型为half类型,输入数据可以对齐到一个datablock的大小(32字节),输入数据为2048 * 2 / 32 = 128个datablock,因此可以平均分配到每个核上(假设使用8个核),每个核上处理256个数,16个datablock。此时不需要进行尾块处理。

图 1 shape对齐场景[object Object][object Object]

针对一些shape,比如算子的输入shape为(1,1904),支持的数据类型为half类型,输入数据可以对齐到一个datablock的大小(32字节),可以平均分配到每个核上(假设使用8个核),每个核上处理238个数,238个数无法均分到datablock上,分满14个datablock后,剩余14个数(28字节),多核切分后需要进行尾块处理。

对于不同shape的输入进行数据切分时,可能会发生Tiling后的数据平均分配到多核上,但每个核内的数据无法均分的情况。针对此种场景,在Tiling参数中增加变量lastTileLength,用来表示最后一个分块,即尾块的大小。因此,在定义算子的Tiling结构体时包含以下四个成员:

  • blockLength:每个核上计算的数据长度;
  • tileNum:每个核上切分的主块数据块的个数;
  • tileLength:每个核上主块数据块的长度;
  • lastTileLength:每个核上尾块的长度。

图 2 多核Tiling尾块示意图[object Object][object Object]

[object Object]

算子的Tiling结构体定义如下:

[object Object]

Host侧Tiling实现的主要内容为计算以上四个成员变量。步骤如下:

  1. 判断数据总长度totalLength是否满足32字节对齐,如不满足,则计算totalLength向上32字节对齐后的长度totalLengthAligned。

    [object Object]
  2. 判断totalLengthAligned是否能被使用的核数NumBlocks均分,如果可以,则计算每个核上计算数据长度blockLength。

    [object Object]
  3. 计算tileNum。为了减少数据搬运开销,应尽量使用核内的Unified Buffer空间。基于每个核上的计算量以及可用Unified Buffer空间的大小,计算tileNum。

    [object Object]
  4. 根据计算出的tileNum,计算tileLength和lastTileLength。

    如果每个核的计算量能够被当前可用Unified Buffer空间均分,则按照无尾块场景处理。

    [object Object]

    反之,按照尾块场景处理,尾块长度为单核计算数据长度 - tileNum * tileLength。

    [object Object]

Host侧Tiling实现的代码如下:

[object Object]

(1,1904)形状的输入数据计算后,tiling结构体内各个变量的值如下:

[object Object]
[object Object]

相比,在Init函数中通过Pipe内存管理对象为输入输出Queue分配内存时,取tileLength与lastTileLength中的最大值作为分配内存的长度。例如,当单核需要计算的长度小于UB可用空间时,按照仅有尾块处理,此时tileLength为0,而lastTileLength为数据块长度。因此,需要取两者中的较大值来分配内存。

[object Object]

由于尾块长度为lastTileLength,与主块数据块的长度不同,因此在CopyIn函数、Compute函数、CopyOut函数中传入本次循环待处理的数据块长度参数tileLength,即待处理的主块或尾块的数据长度。

Process函数实现代码如下:

[object Object]

CopyIn函数实现代码如下:

[object Object]

Compute函数实现代码如下:

[object Object]

CopyOut函数实现代码如下:

[object Object]