Thanks for sending an requirement! Please fill in the following template to help quickly solve your problem.
使用AscendC对LpLoss算子进行开发与重构,实现了AscendC实现的LpLoss算子对Atlas A2 训练系列产品(Atlas 800I/T A2)硬件的适配。
浙工大-智能计算研究所
LpLoss算子的主要功能是计算预测值(predict)与标签值(label)之间的Lp范数损失。该算子当前主要支持p=1的场景,即计算逐元素的绝对值误差(L1 Loss)。在模型训练和评估场景中,该算子常用于回归任务或作为各种正则化项的基础计算模块,以衡量预测结果与真实情况的偏差。
该算子支持灵活的规约模式(reduction),包括不规约输出逐元素损失(none)、输出所有元素损失的平均值(mean)以及输出求和值(sum)。实现了LpLoss算子的AscendC实现,有效替代原有算子在对应昇腾硬件路径上的实现,且进一步优化了在bfloat16和float16下的计算精度与性能。
host侧设计
Host侧由算子定义、推导与tiling三部分构成,核心职责是把输入约束、并行策略和kernel运行参数固化为可直接消费的tiling数据。
(1) 算子定义与推导行为
在定义侧,输入为predict和label,输出为y;输入和输出dtype支持float16、float32、bfloat16。Shape推导依赖于属性reduction:当reduction="none"时,输出y的shape与输入predict完全一致;当reduction="mean"或"sum"时,输出y的shape被推导为0维标量。AICore配置启用动态shape与动态rank能力。
reduction
reduction="none"
reduction="mean"
"sum"
(2) 输入合法性与广播约束
输入张量predict和label的shape必须完全一致,底层Kernel不支持广播机制(若需要广播,在上层aclnn API封装处会提前调用BroadcastTo小算子进行处理)。此外,输入和输出的数据类型必须保持严格一致,且数据格式仅支持ND。
(3) 分核策略与数据切分
分核与切分由Tiling阶段自动根据硬件Unified Buffer (UB) 大小和输入数据类型推算。策略如下:
首先根据数据类型(fp32、fp16/bf16)以及是否需要规约(reduction),计算每个数据元素所需的UB空间比例(ubDataNumber)。
根据实际的UB空间上限,评估采用双缓冲(Double Buffer)还是单缓冲(Single Buffer),以此作为生成TilingKey的依据。
结合系统总数据量和多核数量,使用大核(bigCore)和小核(smallCore)策略来进行任务均匀分配。大核处理更多的Block数据,小核处理剩余部分,从而保证在数据量无法整除核数时的负载均衡。
(4) 规约内存规划
如果算子属性为规约模式("mean"或"sum"),Host侧不仅会配置Tiling参数,还会为Kernel申请额外的Workspace内存,用于跨核数据同步(SyncAll)以及暂存每个Core计算的局部累加结果(Partial Sum)。
"mean"
kernel侧设计
kernel采用模板类 + TilingKey分发设计,整体执行链路针对是否需要规约拆分为ProcessNone与ProcessReduce两条执行路径。内部按tile循环执行CopyIn、Compute、CopyOut。
ProcessNone
ProcessReduce
(1) 入口分发与数据类型路径
Kernel入口通过TilingKey(区分双缓冲和单缓冲模式schMode)分发到对应的具体实现,再根据预测值、标签值的类型实例化对应模板。根据算子是否需要Reduce,模板会分别初始化不同的内部结构与队列。
schMode
(2) Init阶段
Init根据Host下发的Tiling数据(大核/小核切分参数),计算当前核处理的全局偏移(globalBufferIndex)和实际处理的数据量(coreDataNum)。
随后完成GM(Global Memory)窗口绑定。
本地资源方面,初始化双缓冲/单缓冲的输入队列(TPipe)和输出队列。若是float16或bfloat16类型,还会额外申请float32格式的计算缓冲区(calcBuf),以便提升计算精度;若是规约模式,还会初始化用于跨核同步和Reduce的Buffer。
(3) Process与tile循环
根据IS_REDUCE的值走不同流程。对每个Core分配的数据量,按tileDataNum切分为多个tile进行循环。满tile直接处理,尾块(tail block)则根据动态长度限制处理量。
IS_REDUCE
tileDataNum
(4) CopyIn阶段
从predict和label的GM地址向Local Tensor搬运数据。满tile直接DataCopy;对于尾块(Tail tile),通过设置DataCopyPad按实际剩余长度搬运并补齐数据,避免越界访问。
DataCopyPad
(5) Compute阶段
核心逻辑是计算 y = |predict - label|。
y = |predict - label|
对于float32路径:直接对输入执行Sub(相减)和Abs(绝对值)操作。
Sub
Abs
对于float16与bfloat16路径:为保证精度,先将输入使用Cast转换为float32格式放入临时计算缓冲,执行Sub和Abs后,再将结果Cast回对应的原始数据类型(bfloat16使用CAST_RINT,float16使用CAST_ROUND)。
Cast
CAST_RINT
CAST_ROUND
如果处于规约模式(ProcessReduce):不会立即进行格式回退,而是在当前核内调用ReduceSum,将各个Tile的float32格式差值累加到该核局部的累加器中。
ReduceSum
(6) CopyOut与Global Reduction阶段
对于ProcessNone模式:计算结果写回输出队列,并通过DataCopy写回至GM对应的偏移位置中。
对于ProcessReduce模式:在核内所有的Tile处理完毕后,执行全局归约操作。首先通过SyncAll执行全表屏障同步;接着由0号核(Core 0)从Workspace中读取其他所有核的局部累加和,执行全局ReduceSum;如果是"mean"模式,则将结果乘以meanFactor(1.0 / totalNum);最后将结果Cast为输出数据类型并写回GM的首地址。
SyncAll
meanFactor
1.0 / totalNum
算子支持的数据类型
float16、float32、bfloat16
需求已合入,此issue关闭
Thanks for sending an requirement! Please fill in the following template to help quickly solve your problem.
Backgroud(背景信息)
使用AscendC对LpLoss算子进行开发与重构,实现了AscendC实现的LpLoss算子对Atlas A2 训练系列产品(Atlas 800I/T A2)硬件的适配。
Origin(信息来源)
浙工大-智能计算研究所
Benefit / Necessity (价值/作用)
LpLoss算子的主要功能是计算预测值(predict)与标签值(label)之间的Lp范数损失。该算子当前主要支持p=1的场景,即计算逐元素的绝对值误差(L1 Loss)。在模型训练和评估场景中,该算子常用于回归任务或作为各种正则化项的基础计算模块,以衡量预测结果与真实情况的偏差。
该算子支持灵活的规约模式(reduction),包括不规约输出逐元素损失(none)、输出所有元素损失的平均值(mean)以及输出求和值(sum)。实现了LpLoss算子的AscendC实现,有效替代原有算子在对应昇腾硬件路径上的实现,且进一步优化了在bfloat16和float16下的计算精度与性能。
Design(设计方案)
host侧设计
Host侧由算子定义、推导与tiling三部分构成,核心职责是把输入约束、并行策略和kernel运行参数固化为可直接消费的tiling数据。
(1) 算子定义与推导行为
在定义侧,输入为predict和label,输出为y;输入和输出dtype支持float16、float32、bfloat16。Shape推导依赖于属性
reduction:当reduction="none"时,输出y的shape与输入predict完全一致;当reduction="mean"或"sum"时,输出y的shape被推导为0维标量。AICore配置启用动态shape与动态rank能力。(2) 输入合法性与广播约束
输入张量predict和label的shape必须完全一致,底层Kernel不支持广播机制(若需要广播,在上层aclnn API封装处会提前调用BroadcastTo小算子进行处理)。此外,输入和输出的数据类型必须保持严格一致,且数据格式仅支持ND。
(3) 分核策略与数据切分
分核与切分由Tiling阶段自动根据硬件Unified Buffer (UB) 大小和输入数据类型推算。策略如下:
首先根据数据类型(fp32、fp16/bf16)以及是否需要规约(reduction),计算每个数据元素所需的UB空间比例(ubDataNumber)。
根据实际的UB空间上限,评估采用双缓冲(Double Buffer)还是单缓冲(Single Buffer),以此作为生成TilingKey的依据。
结合系统总数据量和多核数量,使用大核(bigCore)和小核(smallCore)策略来进行任务均匀分配。大核处理更多的Block数据,小核处理剩余部分,从而保证在数据量无法整除核数时的负载均衡。
(4) 规约内存规划
如果算子属性为规约模式(
"mean"或"sum"),Host侧不仅会配置Tiling参数,还会为Kernel申请额外的Workspace内存,用于跨核数据同步(SyncAll)以及暂存每个Core计算的局部累加结果(Partial Sum)。kernel侧设计
kernel采用模板类 + TilingKey分发设计,整体执行链路针对是否需要规约拆分为
ProcessNone与ProcessReduce两条执行路径。内部按tile循环执行CopyIn、Compute、CopyOut。(1) 入口分发与数据类型路径
Kernel入口通过TilingKey(区分双缓冲和单缓冲模式
schMode)分发到对应的具体实现,再根据预测值、标签值的类型实例化对应模板。根据算子是否需要Reduce,模板会分别初始化不同的内部结构与队列。(2) Init阶段
Init根据Host下发的Tiling数据(大核/小核切分参数),计算当前核处理的全局偏移(globalBufferIndex)和实际处理的数据量(coreDataNum)。
随后完成GM(Global Memory)窗口绑定。
本地资源方面,初始化双缓冲/单缓冲的输入队列(TPipe)和输出队列。若是float16或bfloat16类型,还会额外申请float32格式的计算缓冲区(calcBuf),以便提升计算精度;若是规约模式,还会初始化用于跨核同步和Reduce的Buffer。
(3) Process与tile循环
根据
IS_REDUCE的值走不同流程。对每个Core分配的数据量,按tileDataNum切分为多个tile进行循环。满tile直接处理,尾块(tail block)则根据动态长度限制处理量。(4) CopyIn阶段
从predict和label的GM地址向Local Tensor搬运数据。满tile直接DataCopy;对于尾块(Tail tile),通过设置
DataCopyPad按实际剩余长度搬运并补齐数据,避免越界访问。(5) Compute阶段
核心逻辑是计算
y = |predict - label|。对于float32路径:直接对输入执行
Sub(相减)和Abs(绝对值)操作。对于float16与bfloat16路径:为保证精度,先将输入使用
Cast转换为float32格式放入临时计算缓冲,执行Sub和Abs后,再将结果Cast回对应的原始数据类型(bfloat16使用CAST_RINT,float16使用CAST_ROUND)。如果处于规约模式(
ProcessReduce):不会立即进行格式回退,而是在当前核内调用ReduceSum,将各个Tile的float32格式差值累加到该核局部的累加器中。(6) CopyOut与Global Reduction阶段
对于
ProcessNone模式:计算结果写回输出队列,并通过DataCopy写回至GM对应的偏移位置中。对于
ProcessReduce模式:在核内所有的Tile处理完毕后,执行全局归约操作。首先通过SyncAll执行全表屏障同步;接着由0号核(Core 0)从Workspace中读取其他所有核的局部累加和,执行全局ReduceSum;如果是"mean"模式,则将结果乘以meanFactor(1.0 / totalNum);最后将结果Cast为输出数据类型并写回GM的首地址。算子支持的数据类型
float16、float32、bfloat16