深入理解华为 CANN 中的 Broadcast 算子实现:从底层机制到工程化落地

深入理解华为 CANN 中的 Broadcast 算子实现:从底层机制到工程化落地

在深度学习算子开发的世界里,Broadcast 是一个看似简单,却在底层实现上极富挑战性的概念。对用户而言,Broadcast 只是让两个 shape 不一致的张量也能 "自然相加";但对于算子开发者而言,它涉及 数据扩维策略、对齐约束、核内 Tiling 切分、UB 缓冲区布局以及对硬件特性的深度理解

本文将以 Add 算子中的 Broadcast 场景为例,系统讲解 CANN 框架下 Broadcast 机制的设计方式、Tiling 切分策略以及设备侧核函数的完整处理流程。希望通过本文,读者不仅能理解 "怎么做",更能理解 "为什么这样做"。


1. 为什么 Broadcast 在 CANN 算子中如此关键?

在深度学习模型中,Add、Mul、Sub 等基础操作经常用于对不同 shape 的张量执行计算。例如:

  • 一个 shape 为 (32, 8) 的张量与
  • 一个 shape 为 (32, 1) 的张量相加

框架层面可以轻松处理这种计算,但设备侧算子却不能直接使用不一致的 shape 进行核函数执行。

Broadcast 的核心功能是------
将较小维度的数据在某一个轴上扩展,使其与较大维度保持一致,从而支持逐元素计算。

然而,这看似简单的处理在底层却涉及多维问题:

  • 如何理解广播轴、扩展系数(coef)?
  • 扩张后的数据在 Unified Buffer 中如何排布?
  • GM(Global Memory) 中的读取地址如何对齐?
  • 多核 Tiling 后,每个核应处理哪些广播后的数据块?

这一切构成了本文的核心内容。


2. Broadcast 的判定条件与扩展特性

在 CANN 的 Ascend C 编程中,只有满足特定模式的数据才能执行硬件 Broadcast:

  • 两个输入的维度数量必须一致;
  • 仅允许其中一个维度不同;
  • 且较小维度必须为 1。

例如:

输入 shape
x (32, 8)
y (32, 1)

第二个输入在 axis=1 上为 1,因此能够进行广播,广播系数为:

复制代码
coef = 8 / 1 = 8

此外,硬件要求输入地址需要 32 字节对齐 ,这对 GM 的读写方式也带来额外限制,因此开发者往往需要使用 DataCopyPad 来规避越界地址或未对齐的问题。


3. Tiling:Broadcast 场景的核心设计挑战

3.1 添加 Broadcast 所需的 Tiling 描述信息

在普通 Add 算子中,我们只需描述输入长度与 tile 切分信息。而当 Broadcast 参与设计时,Tiling 结构体需要额外记录:

  • xLen / yLen:两个输入的总长度
  • axis:需要进行 Broadcast 的轴
  • coef:广播扩展倍数

示例结构:

cpp 复制代码
struct AddCustomTilingData {
    uint32_t xLen;
    uint32_t yLen;
    uint32_t coef;
    uint32_t axis;
    ...
};

coef 的作用极其关键,它决定了 kernel 中访问 y 数据的方式、扩展后的 shape 计算方式,以及 UB 内的排布方式。


4. Tiling 切分策略:以 "shorterAxisLen" 为核心

为了减少冗余计算、提升多核并行效率,切分逻辑采用以下关键设计:

4.1 以较小维度长度 shorterAxisLen 作为主切分依据

设:

复制代码
shorterAxisLen = min(xLen, yLen)
totalLength    = max(xLen, yLen)

因为较短的维度在 Broadcast 后真正的有效数据仍然只有 shorterAxisLen,在 Tiling 时应以 shorterAxisLen 进行分核。

广播后真实处理的数据长度为:

复制代码
processed_length = shorterAxisLen * coef

这也是各核通常搬入的 GM 数据量。


5. UB 内数据块长度计算:对齐 coef 与 BUFFER_NUM

Unified Buffer 的利用效率直接影响核函数性能,因此必须对 tileLength 做齐整处理。

核心对齐策略如下:

复制代码
ubBlockAligned =
    if UB_BLOCK_NUM * alignNum / (coef * BUFFER_NUM) * (coef * BUFFER_NUM) == 0:
        UB_BLOCK_NUM
    else:
        UB_BLOCK_NUM * alignNum / (coef * BUFFER_NUM) * (coef * BUFFER_NUM)

这句看似复杂,其背后逻辑十分明确:

👉 为了确保 UB 中的 Y 数据广播后能整齐地与 X 对应,UB 块长度必须同时满足:

  • coef 的倍数(广播需要 repeated coef 次)
  • BUFFER_NUM 的倍数(流水线分组要求)
  • UB_BLOCK_NUM 的基准大小(硬件限制)

这体现了 CANN 算子设计中最重要的一点:

所有 tiled 数据必须"形状可用、对齐可控、排布合理"。


6. 核函数(Device Side)的实现流程详解

核函数执行阶段主要包含三项关键任务:

  • 确定 GM 输入指针(长输入与短输入)
  • 初始化 UB Buffer
  • 根据 coef 处理对齐后的数据搬入与广播计算

6.1 初始化:选择需要广播的输入

cpp 复制代码
if (tiling.xLen > tiling.yLen) {
    longerInputPtr = x;
    shorterInputPtr = y;
} else {
    longerInputPtr = y;
    shorterInputPtr = x;
}
this->coef = tiling.coef;

其中 shorterInputPtr 指向需要进行广播的张量。

6.2 GM Buffer 设置:根据核编号偏移

由于不同核处理不同 tile,需要根据 blockIdx 计算偏移,并对 shorterInputPtr 应用:

复制代码
offset / coef

以确保访问的是未 broadcast 的数据。

6.3 CopyIn:DataCopyPad 保证 32 字节对齐

因为 broadcast 后数据跨度变化,导致 GM 地址不一定对齐,因此引入 DataCopyPad:

cpp 复制代码
AscendC::DataCopyPad<dataType>(xLocal, xGm[offset], copyParams, padParams);
AscendC::DataCopyPad<dataType>(yLocal, yGm[offset / coef], copyParams, padParams);

这保证了:

  • 地址未对齐也可安全访问
  • UB 数据填充完整

7. Compute:Broadcast + Add 的核心实现

7.1 计算广播前后 shape

对于长度 tileLength:

复制代码
srcShape = { tileLength / coef, 1 }
dstShape = { tileLength / coef, coef }

示意:

输入 yLocal 广播后 broadcastTmp
32 x 1 32 x coef

7.2 调用硬件 Broadcast API

cpp 复制代码
AscendC::BroadCast<dataType, 2, 1>(broadcastTmpTensor, yLocal, dstShape, srcShape);

该 API 是硬件级广播,具有极高吞吐效率。

7.3 Add 计算(伪代码)

cpp 复制代码
zLocal = xLocal + broadcastTmpTensor;

实现中通常使用 VectorAdd 或 Add DSL 指令完成。


8. CopyOut:仍然需要 DataCopyPad

由于 tile 的最后一块可能不是对齐长度,需要使用 Pad 写回:

cpp 复制代码
AscendC::DataCopyPad<dataType>(zGm[offset], zLocal, copyParams);

确保 GM 写回始终合法。


9. 总结:Broadcast 场景下 Add 算子的工程化价值

Broadcast 是许多高层操作的基础,一旦底层实现合理,可以极大提升算子执行效率。

本文总结 Broadcast 场景中最关键的设计点:

★算子设计思路

  • 以较小轴长度为切分基础(shorterAxisLen)
  • coef 是所有计算的核心倍数参数

★内存访问

  • GM 地址必须满足 32 字节对齐,因此 DataCopyPad 必不可少
  • broadcast 后数据块必须与 coef × BUFFER_NUM 对齐

★多核并行

  • former/tail 切分策略提升核间负载均衡
  • tileLength/lastTileLength 区分尾块

★计算部分

  • 使用 AscendC::BroadCast API 完成硬件级扩张
  • Add 计算在 UB 中即可完成,减少 GM 访问

训练营简介

2025年昇腾CANN训练营第二季,基于CANN开源开放全场景,推出0基础入门系列、码力全开特辑、开发者案例等专题课程,助力不同阶段开发者快速提升算子开发技能。获得Ascend C算子中级认证,即可领取精美证书,完成社区任务更有机会赢取华为手机,平板、开发板等大奖。

报名链接:https://www.hiascend.com/developer/activities/cann20252#cann-camp-2502-intro

相关推荐
萌虎不虎8 小时前
【在鸿蒙系统中实现拍照预览功能】
华为·harmonyos
萌虎不虎10 小时前
【鸿蒙实现显示屏测试实现方法】
华为·harmonyos
不爱吃糖的程序媛14 小时前
Flutter 开发的鸿蒙AtomGit OAuth 授权应用
华为·harmonyos
不爱吃糖的程序媛21 小时前
鸿蒙PC命令行开发 macOS 上解决 pkg-config 命令未安装的问题
macos·华为·harmonyos
brave and determined1 天前
CANN训练营 学习(day10)昇腾AI算子ST测试全攻略:从入门到精通
自动化测试·人工智能·log4j·算子·fuzz·测试实战·st测试
yumgpkpm1 天前
Cloudera CDP7、CDH5、CDH6 在华为鲲鹏 ARM 麒麟KylinOS做到无缝切换平缓迁移过程
大数据·arm开发·华为·flink·spark·kafka·cloudera
狮子也疯狂1 天前
【生态互联】| 鸿蒙三方库的选择与适配策略
华为·harmonyos
不爱吃糖的程序媛1 天前
鸿蒙Lycium 交叉编译框架完全指南
华为·harmonyos
路人与大师1 天前
PaddleOCR VL 华为NPU 910B 环境配置完成报告
华为
yenggd1 天前
华为sr-mpls TE配置案例
网络·华为