昇腾 Ascend C 的 Tiling 到底怎么设计?一篇讲透
一、为什么必须 Tiling
写算子最容易翻车的地方不是核函数,是 Tiling。Tiling 没设计好,代码能跑但性能惨不忍睹;设计错了,直接精度错误或越界崩溃。但教材往往一句话带过——“把数据分块”。这篇把它拆到能上手。
两个硬约束:
- UB 太小。AI Core 片上 UB 只有几百 KB,而输入动辄几十 MB。数据没法一次全搬进来,必须切块轮流搬。
- 核要并行。几十个 AI Core 要同时干活,就得先把总任务切成互不重叠的份。
所以 Tiling 本质是两层切分:
总数据 totalLength
│
├─ 第一层:按 AI Core 切 → 每个核分到 blockLength
│
└─ 第二层:核内按 UB 切 → 每次搬 tileLength,循环 tileNum 次
第一层决定并行度,第二层决定搬运效率。两个都算对,算子才既对又快。
二、第一层:按核算
uint32_t coreNum = GetBlockNum();
uint32_t blockLength = totalLength / coreNum;
// 关键:除不尽时,最后一个核补上余数
if (GetBlockIdx() == coreNum - 1) {
blockLength += totalLength % coreNum;
}
uint32_t offset = GetBlockIdx() * (totalLength / coreNum);
这两行是新手第一大坑。totalLength 一般不是核数的整数倍,直接整除会丢掉尾巴上的数据。判断条件写 blockLength(当前核处理的量),不要写 totalLength(永远是全量)。
三、第二层:核内切块
uint32_t tileNum = blockLength / tileLength; // 整块次数
uint32_t tileTail = blockLength % tileLength; // 尾块长度
循环就是"整块 + 尾块":
for (uint32_t i = 0; i < tileNum; i++) {
CopyIn(i, tileLength);
Compute(tileLength);
CopyOut(i, tileLength);
}
if (tileTail > 0) { // 尾块单独处理,别省
CopyIn(tileNum, tileTail);
Compute(tileTail);
CopyOut(tileNum, tileTail);
}
tileLength 从哪来?两种做法:
- kernel 内直接算(
blockLength / BUFFER_NUM)——样例常用,简单够用 - Host 侧算好,通过 TilingData 传进来——工程规范做法,能按芯片型号动态调优
真实项目里几乎都是第二种。你会在核函数签名里看到 GM_ADDR tiling,那就是 Tiling 参数的入口。
四、tileLength 取多大?
这是 Tiling 最核心的取舍:
| 取值 | 后果 |
|---|---|
| 太小 | 循环次数暴涨,搬运启动开销压过计算 |
| 太大 | UB 装不下,乒乓双缓冲失效,性能反降 |
| 合适 | 2 × tileLength × sizeof(T) ≤ UB 容量 |
注意那个 2 ——它是 BUFFER_NUM=2 的双缓冲。想开双缓冲(强烈建议开),UB 至少要能放下两份 tile。
所以调优顺序通常是:
- 先按 UB 容量倒推出
tileLength上限 - 再往下取整到 32 字节的整数倍
- 实测不同取值,看 profiler 里的搬运/计算时间占比
五、对齐:绕不过去的坎
DataCopy 要求数据量 32 字节对齐。float16 占 2 字节 → 一次至少搬 16 个元素。所以 tileLength 必须是 16 的整数倍(float32 则是 8 的倍数)。
尾块尤其要注意:tileTail 常常不是 16 的倍数,直接把非对齐长度丢给 DataCopy 会报错。两种解法:
- 向上对齐 + mask:多搬一点,计算时用 mask 屏蔽多余元素
- 尾块走 CPU 或标量处理:量大时不合算,量小时最省事
六、五个常见错误
- 最后一个核忘了补余数 → 尾部数据丢失,精度错误
tileLength不是 32B 倍数 →DataCopy直接报错- 为了整除硬凑
tileLength→ 丢掉无法覆盖的数据 - 尾块省掉不写 → 数据对不上,测试必挂
- 判断条件写成
totalLength→ 越界崩溃
七、总结
Tiling 就三件事:
按核算 → blockLength + 余数补齐
按 UB 切 → tileNum + tileTail
按对齐取 → 32B 倍数,且留够双缓冲空间
设计对了,DMA 搬运能和计算重叠起来,性能差距可以是数倍。
还没写过算子的,建议先看:《昇腾 Ascend C 入门:手撕 Add 算子,从核函数到 NPU 跑通》
想先搞懂底层架构,看:《昇腾的 “Hello World” 为什么不打印字符串?》
鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。
更多推荐
所有评论(0)