【昇腾CANN训练营·番外篇】考古与未来:从TIK (Python DSL)到Ascend C的演进启示
2025年昇腾CANN训练营第二季推出系列课程,助力开发者提升算子开发技能。训练营提供0基础入门、码力全开特辑等专题,完成AscendC算子中级认证可获得证书及抽奖机会。文章对比了昇腾算子开发的两种范式:基于Python的TIK/TBE和基于C++的AscendC,通过VectorAdd算子实例展示其差异。TIK需要手动计算参数,类似"汇编级Python";而AscendC采用
训练营简介
2025年昇腾CANN训练营第二季,基于CANN开源开放全场景,推出0基础入门系列、码力全开特辑、开发者案例等专题课程,助力不同阶段开发者快速提升算子开发技能。获得Ascend C算子中级认证,即可领取精美证书,完成社区任务更有机会赢取华为手机,平板、开发板等大奖。
报名链接:https://www.hiascend.com/developer/activities/cann20252#cann-camp-2502-intro

前言
“不懂历史的人,看不清未来。”
在昇腾算子开发的进化树上,有两个关键节点:
-
TBE (Tensor Boost Engine) / TIK:基于 Python 的 DSL,强调灵活性,是“汇编级的 Python”。
-
Ascend C:基于 C++ 的 DSL,强调工程化与高性能,是“结构化的 C++”。
为什么昇腾要从 Python 转向 C++?仅仅是为了性能吗? 其实,这背后是编程范式的根本转变:从显式指令流构建,转向面向对象的流水线编程。
本期文章,我们将把同一个简单的 VectorAdd 算子,分别用 TIK 和 Ascend C 写一遍。对比之下,你将看到封装之下的真相。
一、 核心图解:语言的蜕变
TIK 像是在搭积木,你需要告诉系统“把积木A放到积木B上面”。 Ascend C 像是在画蓝图,你定义好“车间”和“传送带”,数据会自动流转。

二、 TIK 考古:用 Python 写汇编
TIK 的代码看起来是 Python,但执行逻辑完全不同。
2.1 TIK 代码实战 (Vector Add)
from tbe import tik
def tik_vector_add(shape, dtype):
# 1. 创建 TIK 实例 (Builder)
tik_instance = tik.Tik()
# 2. 定义 Tensor (占位符)
# 必须显式指定 scope (GM, UB)
input_x_gm = tik_instance.Tensor(dtype, shape, name="x_gm", scope=tik.scope_gm)
input_y_gm = tik_instance.Tensor(dtype, shape, name="y_gm", scope=tik.scope_gm)
output_z_gm = tik_instance.Tensor(dtype, shape, name="z_gm", scope=tik.scope_gm)
# 3. 申请 UB 空间
input_x_ub = tik_instance.Tensor(dtype, shape, name="x_ub", scope=tik.scope_ubuf)
input_y_ub = tik_instance.Tensor(dtype, shape, name="y_ub", scope=tik.scope_ubuf)
output_z_ub = tik_instance.Tensor(dtype, shape, name="z_ub", scope=tik.scope_ubuf)
# 4. 手动搬运 (Data Move)
# 所有的参数 burst_len, stride 都需要手动计算,极易出错
tik_instance.data_move(input_x_ub, input_x_gm, 0, 1, shape // 16, 0, 0)
tik_instance.data_move(input_y_ub, input_y_gm, 0, 1, shape // 16, 0, 0)
# 5. 计算 (Vector Add)
# mask, repeat_times, dst, src0, src1... 也就是汇编指令的直接映射
tik_instance.vec_add(128, output_z_ub, input_x_ub, input_y_ub, 1, 8, 8, 8)
# 6. 搬回
tik_instance.data_move(output_z_gm, output_z_ub, 0, 1, shape // 16, 0, 0)
# 7. 编译构建
tik_instance.BuildCCE(...)
TIK 的痛点:
-
无类型检查:Python 是动态类型的,很多错误(如地址偏移算错)要等到运行时才崩。
-
手动调度:你需要自己计算
mask,repeat,stride,心智负担极重。 -
显式同步:如果是双缓冲,你需要手动写
set_flag,wait_flag。
三、 Ascend C 革命:C++ 的工程化胜利
Ascend C 引入了 类 (Class) 和 模板 (Template) 的概念,把脏活累活封装了。
3.1 对比 Ascend C 实现
class KernelAdd {
// ...
__aicore__ inline void Process() {
// Tiling 和 同步被封装在 Queue 和 Loop 中
CopyIn();
Compute();
CopyOut();
}
__aicore__ inline void Compute() {
// 不需要算 mask 和 stride,直接操作 Tensor 对象
// Add(dst, src0, src1, length)
Add(zLocal, xLocal, yLocal, tileLength);
}
};
Ascend C 的优势:
-
类型安全:C++ 编译器会检查
LocalTensor<T>的类型匹配,杜绝了大部分低级错误。 -
流水线自动化:通过
TPipe和TQue,硬件自动处理了大部分的数据依赖和同步。 -
调试友好:支持 CPU 孪生调试(
printf),这在 TIK 时代是难以想象的。
四、 启示:从 TIK 中学到的底层逻辑
虽然我们现在写 Ascend C,但 TIK 揭示的底层原理依然有效,甚至能指导我们写出更高性能的 Ascend C 代码。
4.1 启示一:Mask 的本质
在 TIK 中,vec_add 的第一个参数是 mask。 Ascend C 的 Add 虽然隐藏了 mask,但在处理非对齐尾块时(第 46 期),我们通过 SetVectorMask 显式控制它。
-
TIK 思维:Mask 是指令的一部分。
-
Ascend C 思维:Mask 是一个状态上下文。
4.2 启示二:Stride 的威力
TIK 的 data_move 强迫你手动算 stride。这让你深刻理解了数据是非连续搬运的。 在 Ascend C 中做 Transpose(第 45 期)时,如果你理解 TIK 的 burst_len 和 dst_stride,就能秒懂如何利用 DataCopy 做矩阵转置。
4.3 启示三:流水线的间隙
TIK 允许你在两条 data_move 之间插入任意指令。 Ascend C 的 Process 结构相对固定。 但在做极致优化时(指令级并行,第 43 期),我们有时会打破 Ascend C 的标准范式,像写 TIK 一样手动展开循环,手动控制指令发射顺序,以填充流水线气泡。
五、 总结
编程语言的演进,本质上是抽象层级的提升。
-
TIK 是“手工作坊”,让你看清每一块砖的纹理,适合探索硬件极限。
-
Ascend C 是“现代化工厂”,让你关注架构设计和业务逻辑,适合大规模生产。
作为新时代的开发者,我们不仅要会用 Ascend C 盖楼,最好也要懂一点 TIK 的砌砖原理。这样,当大楼出现裂缝(性能瓶颈)时,你才能一眼看穿地基的问题。
更多推荐




所有评论(0)