掩码
概述
Mask(掩码)是Ascend C中用于控制数据元素是否参与计算的标识位序列。每一位掩码对应数据中的一个元素的位置,通过有效位和无效位标记,实现对数据操作的精细化开关控制。
掩码由固定位宽的数值表示:
- 有效位(通常为1):对应元素正常参与计算。
- 无效位(通常为0):对应元素被屏蔽,不执行操作。
在连续计算接口(即Tensor前n个数计算接口)中,Mask为输入参数n,表示前n个连续元素参与计算。
在高维切分接口中,Mask用于控制每次迭代内参与计算的元素。可通过连续模式和逐bit模式两种方式设置:
- 连续模式:表示每个迭代内前面连续的多少个元素参与计算。
- 逐bit模式:表示按位控制哪些元素参与计算。
基于Tensor前n个数计算及高维切分接口的Mask应用机制如图1掩码机制说明所示。
说明
该掩码处理能力在Atlas A2 训练系列产品/Atlas A2 推理系列产品和Atlas A3 训练系列产品/Atlas A3 推理系列产品上为硬件原生支持能力;在Ascend 950PR/Ascend 950DT上为保证兼容性进行了软仿实现,其硬件底层提供的能力与Atlas A2 训练系列产品/Atlas A2 推理系列产品和Atlas A3 训练系列产品/Atlas A3 推理系列产品存在一定差异。
掩码配置方式
掩码的设置方式由具体接口的调用方式和模板参数isSetMask共同决定,支持接口内设置Mask和接口外设置Mask两种模式。
表1 Mask配置方式
| 配置方式 | 说明 |
|---|---|
| 接口内设置Mask(默认) | •通过矢量计算API的入参直接传递mask值。 •矢量计算API的模板参数isSetMask(仅部分API支持)用于控制接口传参还是外部API配置,默认值为true,表示接口传参。Mask对应于高维切分计算API中的mask/mask[]参数或者tensor前n个数据计算API中的calCount参数。 |
| 接口外设置Mask | •通过SetVectorMask接口设置Mask值,矢量计算API的模板参数isSetMask设置为false,接口入参中的mask参数(对应于高维切分计算API中的mask/mask[]参数或者tensor前n个数据计算API中的calCount参数)不生效。 •适用于mask参数相同、多次重复使用的场景,无需在矢量计算API内部反复设置,会有一定的性能优势。 |
Mask支持以下工作模式:
| 工作模式 | 说明 |
|---|---|
| Normal模式 | 默认模式,支持单次迭代内的Mask能力,需要开发者配置迭代次数,额外进行尾块的计算。 Normal模式下,Mask用来控制单次迭代内参与计算的元素个数。 通过调用SetMaskNorm设置Normal模式。 |
| Counter模式 | 简化模式,直接传入计算数据量,自动推断迭代次数,不需要开发者去感知迭代次数、处理非对齐尾块的操作;但是不具备单次迭代内的Mask能力。 Counter模式下,Mask表示整个矢量计算参与计算的元素个数。 通过调用SetMaskCount设置Counter模式。 |
Mask操作的使用方式如下:
表3 Mask操作的使用方式
| 配置方式 | 工作模式 | 前n个数据计算API | 高维切分计算API |
|---|---|---|---|
| 接口内设置Mask | Normal模式 | isSetMask模板参数设置为true,通过接口入参传入mask,根据使用场景配置dataBlockStride、repeatStride参数。 | isSetMask模板参数设置为true,通过接口入参传入mask,根据使用场景配置dataBlockStride、repeatStride参数。repeatTime传入固定值即可,建议统一设置为1,该值不生效。 |
| 接口内设置Mask | Counter模式 | 不涉及。 | isSetMask模板参数设置为true,通过接口入参传入mask,根据使用场景配置dataBlockStride、repeatStride参数。repeatTime传入固定值即可,建议统一设置为1,该值不生效。 |
| 接口外设置Mask | Normal模式 | isSetMask模板参数设置为false,调用SetVectorMask设置Mask,之后调用前n个数据计算API;接口入参中的count建议设置成1。 | 调用SetVectorMask设置Mask,之后调用高维切分计算API。 • isSetMask模板参数设置为false,接口入参中的mask参数不生效;根据使用场景配置dataBlockStride、repeatStride参数。repeatTime传入固定值即可,建议统一设置为1,该值不生效。 |
注意:
仅部分API支持isSetMask,具体支持情况请参考对应API的函数原型;
接口外设置Mask性能实践参考:基于全局掩码复用的计算性能优化。
接口内设置Mask
Counter模式
前n个数据计算
采用Counter模式进行前n个数据计算,接口内部会先调用SetMaskCount,然后根据接口参数count调用SetVectorMask,再进行计算,计算完成后调用ResetMask、SetMaskNorm恢复系统默认Normal模式。
调用示例:
C++uint64_t count = 128; half addsValue = 1.0; AscendC::Adds<half, true>(dstLocal, srcLocal, addsValue, count);如图3所示,采用Counter模式进行前n个数据计算,通过count参数指定参与计算的元素数量为128,接口内部会自动完成Mask的设置。
高维切分:连续计算
在Counter模式连续计算中,Mask寄存器中的值只有低64bit有效,被当做参与计算的元素数量。高维切分矢量计算接口的repeatTime参数会被忽略,由系统内部根据元素数量自动计算。
调用示例:
C++half addsValue = 1.0; uint8_t repeatTimes = 1; AscendC::UnaryRepeatParams repeatParams{2, 2, 16, 16}; AscendC::SetMaskCount(); uint64_t mask[2] = {130, 0}; AscendC::Adds<half, true>(dstLocal, srcLocal, addsValue, mask, repeatTimes, repeatParams); AscendC::SetMaskNorm();如图4所示,采用Counter模式下的高维切分连续计算接口,mask数组中仅低64bit有效,表示参与计算的元素总数为130。该模式接口内部的repeatTimes参数无效,系统根据mask值自动计算迭代次数。repeatParams参数配置表示DataBlock间存在间隔,迭代间连续。
Normal模式
在Normal计算模式中,Mask寄存器会作为每个Repeat的掩码使用。只有高维切分支持此模式,在接口上区分为逐bit模式和连续模式。
注:Normal为默认模式。若未发生模式切换,则无需重复配置该模式。
高维切分:逐bit计算
调用示例:
C++half addsValue = 1.0; int8_t repeatTimes = 1; UnaryRepeatParams repeatParams {2, 2, 16, 16}; uint64_t mask[2] = {0x69A569A569A569A5, 0x69A569A569A569A5}; AscendC::Adds<half, true>(dstLocal, srcLocal, addsValue, mask, repeatTimes, repeatParams);如图5所示,采用Normal模式进行逐bit计算时,mask参数以位数组形式控制每个元素是否参与计算,详细展示了第一个DataBlock中元素的计算过程。repeatParams参数配置表示DataBlock间存在间隔,迭代间连续。
高维切分:连续计算
调用示例:
C++half addsValue = 1.0; int8_t repeatTimes = 1; UnaryRepeatParams repeatParams{2, 2, 16, 16}; uint64_t mask = 128; AscendC::Adds<half, true>(dstLocal, srcLocal, addsValue, mask, repeatTimes, repeatParams);如图6所示,采用Normal模式进行连续计算时,mask参数表示迭代内连续参与计算的元素数量为128。repeatParams参数配置表示DataBlock间存在间隔,迭代间连续。
说明
当数据类型为half(操作数为16位)时,每次迭代内能够处理的元素个数mask∈[0, 128]。当mask>128时,仍按一次repeat(128元素)执行,不支持超过128元素的mask控制。
接口外设置Mask
Counter模式
前n个数据计算
调用示例:
C++half addsValue = 1.0; AscendC::SetMaskCount(); AscendC::SetVectorMask<half, AscendC::MaskMode::COUNTER>(128); AscendC::Adds<half, false>(dstLocal, srcLocal, addsValue, 1); AscendC::SetMaskNorm(); // ResetMask无需在每次计算接口调用后调用,仅在需显式重置掩码状态时使用 AscendC::ResetMask();高维切分:连续计算
在Counter计算模式中,Mask寄存器中的值只有低64bit有效,被当做参与计算的元素数量。同时高维切分矢量计算接口的repeatTime参数会被忽略,由系统内部根据元素数量自动计算。
调用示例:
C++half addsValue = 1.0; int8_t repeatTimes = 1; UnaryRepeatParams repeatParams {2, 2, 16, 16}; AscendC::SetMaskCount(); AscendC::SetVectorMask<half, AscendC::MaskMode::COUNTER>(130); AscendC::Adds<half, false>(dstLocal, srcLocal, addsValue, AscendC::MASK_PLACEHOLDER, repeatTimes, repeatParams); AscendC::SetMaskNorm(); // ResetMask无需在每次计算接口调用后调用,仅在需显式重置掩码状态时使用 AscendC::ResetMask();
Normal模式
在Normal计算模式中,Mask寄存器会作为每个Repeat的掩码使用。只有高维切分支持此模式,在接口上区分为逐bit模式和连续模式。
注:Normal为默认模式。若未发生模式切换,则无需重复配置该模式。
高维切分:逐bit计算
调用示例:
C++half addsValue = 1.0; int8_t repeatTimes = 1; UnaryRepeatParams repeatParams{2, 2, 16, 16}; AscendC::SetMaskNorm(); AscendC::SetVectorMask<half, AscendC::MaskMode::NORMAL>(0x69A569A569A569A5, 0x69A569A569A569A5); AscendC::Adds<half, false>(dstLocal, srcLocal, addsValue, AscendC::MASK_PLACEHOLDER, repeatTimes, repeatParams); // ResetMask无需在每次计算接口调用后调用,仅在需显式重置掩码状态时使用 AscendC::ResetMask();高维切分:连续计算
调用示例:
C++half addsValue = 1.0; uint8_t repeatTimes = 1; AscendC::UnaryRepeatParams repeatParams{2, 2, 16, 16}; AscendC::SetMaskNorm(); AscendC::SetVectorMask<half, AscendC::MaskMode::NORMAL>(128); AscendC::Adds<half, false>(dstLocal, srcLocal, addsValue, AscendC::MASK_PLACEHOLDER, repeatTimes, repeatParams); // ResetMask无需在每次计算接口调用后调用,仅在需显式重置掩码状态时使用 AscendC::ResetMask();
说明
当数据类型为half(操作数为16位)时,每次迭代内能够处理的元素个数mask∈[0, 128]。当mask>128时,仍按一次repeat(128元素)执行,不支持超过128元素的mask控制。
通用说明
接口通用参数说明
接口需要进行掩码操作时,需要传入通用参数,这些参数用于控制计算元素范围、迭代行为及掩码配置。以API Adds为例,展开说明了掩码相关参数的含义、输入类型及使用注意事项。
tensor前n个数据计算
C++template <typename T, bool isSetMask = true> __aicore__ inline void Adds(const LocalTensor<T>& dst, const LocalTensor<T>& src, const T& scalarValue, const int32_t& count)tensor高维切分计算
mask逐bit模式
C++template <typename T, bool isSetMask = true> __aicore__ inline void Adds(const LocalTensor<T>& dst, const LocalTensor<T>& src, const T& scalarValue, uint64_t mask[], const uint8_t repeatTime, const UnaryRepeatParams& repeatParams)mask连续模式
C++template <typename T, bool isSetMask = true> __aicore__ inline void Adds(const LocalTensor<T>& dst, const LocalTensor<T>& src, const T& scalarValue, uint64_t mask, const uint8_t repeatTime, const UnaryRepeatParams& repeatParams)
表4 模板参数说明
| 参数名 | 描述 |
|---|---|
| isSetMask | 是否在接口内部设置Mask。 • true,表示在接口内设置Mask。 • false,表示在接口外设置Mask,开发者需要使用SetVectorMask接口设置mask值。这种模式下,接口入参中的mask值设置为占位符 MASK_PLACEHOLDER,用于占位,无实际含义。 |
| 参数名称 | 输入/输出 | 含义 |
|---|---|---|
| count | 输入 | 参与计算的元素个数。 在Counter模式下,当isSetMask = false,count会被转换成掩码表示。前count位设置为1,表示前count个元素参与计算。 接口提供两种模式的mask参数: •逐bit模式:输入为mask数组mask[],直接设置mask寄存器的值,输入的mask数组长度大于等于2,其中有效的位数与操作数的类型有关: 当操作数为16位时,一次迭代中有128个元素参与计算,需要使用两个uint64_t类型的数据进行设置,数组有效长度为2,maskLow∈[0, 264-1], maskHigh∈[0, 264-1]; 当操作数为32位时,一次迭代有64个数参与计算,仅需一个uint64_t的数据即可,数组有效长度为1,maskLow∈[0, 264-1], maskHigh = 0; 当操作数为64位时,一次迭代有32个元素参与计算,数组长度为1,mask[0]的有效位数为32,maskLow∈[0, 232-1],maskHigh = 0。 例如,mask=[8, 2],8和2分别表示低位的64bit和高位的64bit,8=0b1000,表示仅第4个元素参与计算;2=0b0010表示第66(64+2)个元素参与计算。 •连续模式:输入为uint64_t的值mask,表示mask寄存器从低位开始连续多少位被设置为1,表示前面连续的多少个元素参与计算。取值范围和操作数的数据类型有关,数据类型不同,每次迭代内能够处理的元素个数最大值不同。 当操作数为16位时,mask∈[0, 128]; 当操作数为32位时,mask∈[0, 64]; 当操作数为64位时,mask∈[0, 32]。 注:连续模式下,mask超出范围按照对应数据类型下mask最大值计算;mask = 0表示指令不会执行计算操作,该接口将被视为NOP(空操作);若设成了负值可能产生未定义行为。 |
注意
参数count的最大取值受限于输入数据总容量,该容量由系统UB大小限制及操作数数据类型共同约束:不同数据类型下,相同UB容量内可容纳的最大元素数量不同。
参数mask[]/mask仅在高维切分计算时有效,mask为0时,目的操作数保持原值。









