如何高效处理Ascend C非对齐数据?优化技巧全解析

举报
昇腾CANN 发表于 2025/06/30 20:01:50 2025/06/30
【摘要】 对于Ascend C算子开发者而言,为了更高效地操作变量,通常需要满足相应的数据对齐要求。例如在使用DataCopy接口进行数据传输时,必须确保搬运的数据长度和操作数在UB上的起始地址为32字节对齐;在进行向量计算时,操作数的起始地址也需满足32字节对齐的要求。然而,在大多数场景下,会遇到大量非对齐的数据情况。本文提供了完整的处理方案,帮助开发者更加灵活地应对数据非对齐的情况。1. 数据非对...

对于Ascend C算子开发者而言,为了更高效地操作变量,通常需要满足相应的数据对齐要求。例如在使用DataCopy接口进行数据传输时,必须确保搬运的数据长度和操作数在UB上的起始地址为32字节对齐;在进行向量计算时,操作数的起始地址也需满足32字节对齐的要求。然而,在大多数场景下,会遇到大量非对齐的数据情况。

本文提供了完整的处理方案,帮助开发者更加灵活地应对数据非对齐的情况。


1. 数据非对齐的典型场景

首先介绍一些非对齐搬运和计算的实例。

  • 非对齐搬入:当需要从Global拷贝11个half数值到Local时,为保证搬运的数据长度32字节对齐,使用DataCopy拷贝16个half(32B)数据到Local上,Local[11]~Local[15]被设置成无效数据-1。下文描述中的Global指Global Memory上的tensor,Local指Local Memory上的tensor。


(非对齐搬入)

  • 非对齐搬出:当需要从Local拷贝11个half数值到Global时,为保证搬运的数据长度32字节对齐,使用DataCopy拷贝16个half(32B)数据到Global上,Global[11]~Global[15]被设置成无效数据-1。


(非对齐搬出)

  • 矢量计算起始地址非32字节对齐的错误示例:矢量计算时需要保证起始地址32字节对齐,如下的示例中,从Local1[7],即LocalTensor的第8个数开始计算,起始地址不满足32字节对齐,是错误示例。


(矢量计算起始地址非32字节对齐的错误示例)

 2. DataCopyPad接口提供非对齐数据搬运功能

目前,对于Atlas A2 训练系列产品、Atlas 800I A2 推理产品、A200I A2 Box 异构组件、Atlas A3 训练系列产品、Atlas A3 推理系列产品以及Atlas 200I/500 A2 推理产品,DataCopyPad接口提供了非对齐数据搬运的功能,开发者可以使用该接口解决非对齐场景下的数据搬运问题。


使用DataCopyPad接口时,主要关注DataCopyExtParams和DataCopyPadExtParams的参数配置,DataCopyExtParams用于控制搬运的数据块大小、步长间隔等信息,DataCopyPadExtParams用于控制Pad补齐的相关信息。


表2-1DataCopyExtParams结构体参数定义


表2-2DataCopyPadExtParams<T>结构体参数定义


接下来我们通过Global Memory->Local Memory和Local Memory->Global Memory通路的DataCopyPad接口参数配置示例来进一步了解DataCopyPad接口的使用方法。

对于Global Memory->Local Memory(GM->VECIN/VECOUT)的数据搬运,各参数使用说明如下:

  • 当blockLen+leftPadding+rightPadding满足32字节对齐时,isPad为false,左右两侧填充的数据值会默认为随机值;否则为paddingValue。

  • 当blockLen+leftPadding+rightPadding不满足32字节对齐时,框架会填充一些假数据dummy,保证填充后的数据(左右填充的数据+blockLen+假数据)为32字节对齐。若leftPadding、rightPadding都为0:dummy会默认填充待搬运数据块的第一个元素值;若leftPadding/rightPadding不为0:isPad为false,左右两侧填充的数据值和dummy值均为随机值;否则为paddingValue。

配置示例1:

  • blockLen为64,每个连续传输数据块包含64Bytes;srcStride为1,因为源操作数的逻辑位置为GM,srcStride的单位为Byte,也就是说源操作数相邻数据块之间间隔1Byte;dstStride为1,因为目的操作数的逻辑位置为VECIN/VECOUT,dstStride的单位为dataBlock(32Bytes),也就是说目的操作数相邻数据块之间间隔1个dataBlock。

  • blockLen+leftPadding+rightPadding满足32字节对齐,isPad为false,左右两侧填充的数据值会默认为随机值;否则为paddingValue。此处示例中,leftPadding、rightPadding均为0,则不填充。

  • blockLen+leftPadding+rightPadding不满足32字节对齐时,框架会填充一些假数据dummy,保证填充后的数据(左右填充的数据+blockLen+假数据)为32字节对齐。leftPadding/rightPadding不为0:若isPad为false,左右两侧填充的数据值和dummy值均为随机值;否则为paddingValue。


配置示例2:

  • blockLen为47,每个连续传输数据块包含47Bytes;srcStride为1,表示源操作数相邻数据块之间间隔1Byte;dstStride为1,表示目的操作数相邻数据块之间间隔1个dataBlock。

  • blockLen+leftPadding+rightPadding不满足32字节对齐,leftPadding、rightPadding均为0:dummy会默认填充待搬运数据块的第一个元素值。

  • blockLen+leftPadding+rightPadding不满足32字节对齐,leftPadding/rightPadding不为0:若isPad为false,左右两侧填充的数据值和dummy值均为随机值;否则为paddingValue。


对于Local Memory->Global Memory(VECIN/VECOUT->GM)的数据搬运,不需要开发者自行填充数据,所以无需关注DataCopyPadExtParams,仅需要关注DataCopyExtParams。

当每个连续传输数据块长度blockLen不满足32字节对齐,由于UB要求32字节对齐,框架在搬出时会自动补充一些假数据来保证对齐,但在当搬到GM时会自动将填充的假数据丢弃掉。

下图呈现了该场景下需要传入的DataCopyParams示例和假数据补齐的原理。blockLen为47,每个连续传输数据块包含47Bytes,不满足32字节对齐;srcStride为1,表示源操作数相邻数据块之间间隔1个dataBlock;dstStride为1,表示目的操作数相邻数据块之间间隔1Byte。框架在搬出时会自动补充17Bytes的假数据来保证对齐,搬到GM时再自动将填充的假数据丢弃掉。


 3. 不支持DataCopyPad接口,如何处理非对齐数据

然而,某些型号(如Atlas 推理系列产品和Atlas 训练系列产品 ),目前仍不支持DataCopyPad接口,需要参考如下方案处理。


(非对齐处理方案示意图)

由于搬入时搬运的数据长度必须保证32字节对齐。数据长度非对齐的情况下,从Global逐行搬运Tensor数据到Local中,Local中每行都存在冗余数据。

搬入后,进行矢量计算时对冗余数据的处理方式有以下几种:

  • 冗余数据参与计算。一般用于elewise计算场景。

  • 通过mask参数掩掉冗余数据。一般用于轴归约计算等场景。

  • 通过Duplicate逐行清零。计算前,针对每一行数据,调用基础API Duplicate对冗余数据位置填充0值。

  • 通过Pad一次性清零。计算前,针对多行数据,可以采用高阶API Pad接口对冗余数据一次性清零。

由于搬出时搬运的数据长度和操作数的起始地址(UB上)必须保证32字节对齐,搬出时可以选择去除冗余数据或者带着冗余数据搬出的方式。

  • 使用UnPad接口去除冗余数据后搬出。待搬出的有效数据总长度满足32字节对齐时,可使用高阶API UnPad接口去除冗余数据并完整搬出。

  • 使用GatherMask收集有效数据后搬出。待搬出的有效数据总长度大于等于32字节时,可使用GatherMask重新收集有效数据,保证搬出的有效数据起始地址和数据长度32字节对齐。

  • 带冗余数据搬出。注意多核处理时开启原子加(使用SetAtomicAdd接口),防止数据踩踏。


下面分别对上述几种处理方案做详细说明,相关代码样例请参考样例介绍。


1)冗余数据参与计算

如下图所示,对前11个half数据进行Abs计算,冗余数据可以参与计算,不影响最终结果。步骤为:

a.使用DataCopy从GLobal搬运16个half数据到Local1中,包含冗余数据-11~-15;

b.直接使用Abs做整块计算,不用计算尾块大小,冗余数据参与计算。


(冗余数据参与计算)

2)使用mask掩掉冗余数据

如下图所示,假设输入数据的shape为16 * 4,将输入数据搬入到UB后每行数据前4个half数据为有效数据,其余为冗余数据。为只对前4个half数据进行ReduceMin计算,可以通过设置mask参数的方法掩掉冗余数据。针对每行数据的处理步骤为:

a.使用DataCopy从Global搬运16个half数据到Local1中;

b.对归约计算的目的操作数Local2清零,如使用Duplicate等;

c.进行归约操作,将ReduceMin的mask模式设置为前4个数据有效,从而掩掉冗余数据。


(使用mask掩掉脏数据)

3)通过Duplicate逐行清零。

如下图所示,对于搬入后的非对齐数据,逐行进行Duplicate清零处理,步骤为:

a.使用DataCopy从Global搬运16个half数据到Local中;

b.使用基础API Duplicate,按照如下方式设置mask值,控制仅后5个元素位置有效,将冗余数据填充为0。

uint64_t mask0 = ((uint64_t)1 << 16) - ((uint64_t)1 << 11);  uint64_t mask[2] = {mask0, 0};


(通过Duplicate逐行清零)

4)通过Pad一次性清零。

如下图所示,假设输入数据的shape为16 * 6,搬入Local后大小为16 * 16,每行都包含冗余数据,逐行清零性能较差,可以使用Pad一次性清零,步骤为:

a.将16 * 6的数据从GM上逐行搬入UB后,每行有6个有效数据;

b.使用Pad接口将冗余数据位置填充为0。(对应Pad接口使用场景为:tensor的width已32B对齐,但是有部分冗余数据)。


(通过Pad一次性清零)

5)使用UnPad接口去除冗余数据后搬出。

如下图所示,Local内存大小为16*16,每行中只有前6个数为有效数据, 要搬出的有效数据16 * 6满足32B对齐,可以使用UnPad接口去除冗余数据并完整搬出。步骤如下:

a.使用UnPad高阶API去除冗余值;

b.使用DataCopy搬运出连续的16 * 6个half数据到Global中。


(使用UnPad接口去除冗余数据后搬出)

6)使用GatherMask收集有效数据后搬出。

如下图所示,为搬出19个half数据到Global中,有16-18这3个数据的搬运无法满足对齐要求,使用GatherMask对有效数据进行重新收集,收集3-18这16个数据并搬出。步骤如下:

a.完整拷贝前16个half(32B)数据到Global中;

b.使用GatherMask接口,将Local1[3]~[18]的数Gather到Local2中,Local2从对齐地址开始;

c.从Local2中搬运Gather的数据(32B整数倍)到Global中。


(使用GatherMask收集有效数据后搬出)

7)带冗余数据搬出

如下图所示,有4个核参与计算,每个核拷贝出4个数,每个核上拷贝的数据长度不满足32字节对齐,采用将冗余数据一起搬出的方式,步骤如下:

a.将目标Global完整清零,可以通过在host清零或者在kernel侧用UB覆盖的方式处理;

b.将本核内的Local数据,除了要搬出的4个有效数据,其余冗余部分清零(使用Duplicate接口);

c.使用原子累加的方式拷贝到Global,原子累加结合冗余数据清零,确保不会出现数据踩踏。


(带冗余数据搬出)

4. 更多学习资源

欢迎访问昇腾社区Ascend C信息专区,获取Ascend C更多学习资源。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。