Vector算子灵活运用Counter模式
【优先级】高
【描述】Normal模式下,通过迭代次数repeatTimes和掩码mask,控制Vector算子中矢量计算API的计算数据量;当用户想要指定API计算的总元素个数时,首先需要自行判断是否存在不同的主尾块,主块需要将mask设置为全部元素参与计算,并且计算主块所需迭代次数,然后根据尾块中剩余元素个数重置mask,再进行尾块的运算,这中间涉及大量Scalar计算。Counter模式下,用户不需要计算迭代次数以及判断是否存在尾块,将mask模式设置为Counter模式后,只需要设置mask为{0, 总元素个数},然后调用相应的API,处理逻辑更简便,同时减少代码量和Scalar计算量。
提示:Normal模式和Counter模式、掩码的介绍可参考掩码操作。
以下反例和正例中的代码均以AddCustom算子为例,修改其中如下Add接口的调用代码,以说明Counter模式的优势。
AscendC::Add(zLocal, xLocal, yLocal, this->tileLength);
【反例】
输入数据类型为half的xLocal, yLocal,数据量均为15000。Normal模式下,每个迭代内参与计算的元素个数最多为256B/sizeof(half)=128个,所以15000次Add计算会被分为:主块计算15000/128=117次迭代,每次迭代128个元素参与计算;尾块计算1次迭代,该迭代15000-117*128=24个元素参与计算。从代码角度,需要计算主块的repeatTimes、尾块元素个数;主块计算时,设置mask值为128,尾块计算时,需要设置mask值为尾块元素个数24;这些过程均涉及Scalar计算。
uint32_t ELE_SIZE = 15000; AscendC::BinaryRepeatParams binaryParams; uint32_t numPerRepeat = 256 / sizeof(DTYPE_X); // DTYPE_X为half数据类型 uint32_t mainRepeatTimes = ELE_SIZE / numPerRepeat; uint32_t tailEleNum = ELE_SIZE % numPerRepeat; AscendC::SetMaskNorm(); AscendC::SetVectorMask<DTYPE_X, AscendC::MaskMode::NORMAL>(numPerRepeat); // 设置normal模式mask,使每个迭代计算128个数 AscendC::Add<DTYPE_X, false>(zLocal, xLocal, yLocal, AscendC::MASK_PLACEHOLDER, mainRepeatTimes, binaryParams); // MASK_PLACEHOLDER值为0,此处为mask占位,实际mask值以SetVectorMask设置的为准 if (tailEleNum > 0) { AscendC::SetVectorMask<DTYPE_X, AscendC::MaskMode::NORMAL>(tailEleNum); // 设置normal模式mask,使每个迭代计算24个数 // 偏移tensor的起始地址,在xLocal和yLocal的14976个元素处,进行尾块计算 AscendC::Add<DTYPE_X, false>(zLocal[mainRepeatTimes * numPerRepeat], xLocal[mainRepeatTimes * numPerRepeat], yLocal[mainRepeatTimes * numPerRepeat], AscendC::MASK_PLACEHOLDER, 1, binaryParams); } AscendC::ResetMask(); // 还原mask值
【正例】
输入数据类型为half的xLocal, yLocal,数据量均为15000。Counter模式下,只需要设置mask为所有参与计算的元素个数15000,然后直接调用Add指令,即可完成所有计算,不需要繁琐的主尾块计算,代码较为简练。
当要处理多种15000个元素的矢量计算时,Counter模式的优势更明显,不需要反复修改主块和尾块不同的mask值。
uint32_t ELE_SIZE = 15000; AscendC::BinaryRepeatParams binaryParams; AscendC::SetMaskCount(); AscendC::SetVectorMask<DTYPE_X, AscendC::MaskMode::COUNTER>(ELE_SIZE); // 设置counter模式mask,总共计算15000个数 AscendC::Add<DTYPE_X, false>(zLocal, xLocal, yLocal, AscendC::MASK_PLACEHOLDER, 1, binaryParams); // MASK_PLACEHOLDER值为0,此处为mask占位,实际mask值以SetVectorMask设置的为准 AscendC::ResetMask(); // 还原mask值
【性能数据】
从上述三幅性能对比图和示例代码可以看到,使用Counter模式能够大幅度简化代码,易于维护,同时能够降低Scalar和Vector计算耗时,获得性能提升。