2026-06-11

从 Triton 到 Ascend NPU — 跨后端 Kernel 开发路线图

学了 9 个 Triton exercise 之后,你写的 kernel 跑在 NVIDIA GPU 上。但 Triton 的定位不只是"CUDA 的 Python 替代品"——它是跨后端的 kernel 语言。这篇文章梳理 Triton 的编译架构、Ascend NPU 的编程模型、以及"给 Ascend 适配 Triton kernel"到底意味着什么。

Triton 的编译器架构

Triton kernel 不直接编译成 GPU 指令。中间有一层 Triton IR

Triton Python (.py)
       │ @triton.jit
       ▼
Triton IR (中间表示)
       │ triton.compile()
       ▼
后端代码生成
   ├── NVIDIA:  Triton IR → TritonGPU IR → LLVM IR → PTX → SASS
   ├── AMD:     Triton IR → TritonGPU IR → LLVM IR → AMDGCN
   ├── Intel:   Triton IR → TritonGPU IR → SPIR-V → GPU binary
   └── Ascend:  Triton IR → ? → AscendC → DaVinci binary

关键点:Triton IR 是 硬件无关的tl.loadtl.dottl.sum 这些操作在你写的时候不关心目标硬件是 NVIDIA、AMD 还是 Ascend。硬件相关的部分在下层——代码生成器负责把 Triton IR 映射到特定硬件的指令集。

Triton 3.0 的 Backend Plugin

Triton 3.0 引入了 backend plugin 机制,让第三方可以注册自己的后端,而不需要修改 Triton 核心代码。

# triton/backends/ascend/  (your work!)
#   driver.py:     内存分配、数据搬运、同步
#   compiler.py:   Triton IR → AscendC → 可执行文件
#   target_info.py: warp size, shared memory size, max threads...

# 然后用户只需:
@triton.jit
def my_kernel(...):
    ...

# 指定后端
my_kernel[grid](a, b, c, backend="ascend")

华为的 torch_npu 团队已经在做这件事。你的实习大概率是参与 compiler.py 的编写——把 Triton IR 里的操作翻译成 AscendC (华为 NPU 的 C-like kernel 语言)。

Ascend NPU 的硬件模型

跟 NVIDIA GPU 的对应关系:

概念 NVIDIA GPU Ascend NPU
计算单元 SM (Streaming Multiprocessor) AI Core (DaVinci)
线程模型 CUDA thread / warp (32 threads) AscendC block / core
内存层级 Global → L2 → Shared → Reg DDR/HBM → L2 → L1 → UB → Reg
矩阵乘法 Tensor Core (mma.sync) Cube Unit (矩阵计算加速器)
向量计算 CUDA Core Vector Unit
标量计算 CUDA Core Scalar Unit
编程语言 CUDA C++ / PTX AscendC (类 C++)

关键区别:

1. 异构计算单元(Cube + Vector + Scalar)

Ascend AI Core 内部有三类运算器:
  Cube Unit:   矩阵乘法(类似 Tensor Core)
  Vector Unit: 向量加减乘除、激活函数
  Scalar Unit: 标量计算、控制流

三者可以同时执行!比如 Cube 做 matmul 时,Vector 同时做 GELU,
Scalar 同时做地址计算。这叫 "融合算子" 的真正含义。

2. 片上内存 UB(Unified Buffer)

NVIDIA: Shared Memory (程序员显式管理) + L1 Cache (硬件自动)
Ascend: UB (Unified Buffer) — 更大的片上 SRAM,程序员控制
        UB 大小约 192KB-256KB (比 NVIDIA 的 128KB Shared Mem 大)

3. 没有 warp 概念

NVIDIA: 32 线程同步执行 (warp),warp shuffle 通信
Ascend: 单指令多数据 (SIMD) 风格,没有 warp-level primitive
        → tl.dot 的实现方式完全不同

实际要做什么:从 Triton IR 到 AscendC

假设你写了一个 Triton matmul kernel。编译器看到的 Triton IR 大概是:

%a = tt.load %a_ptr[%rm, %rk]     // 从 global 加载 tile
%b = tt.load %b_ptr[%rk, %rn]
%c = tt.dot %a, %b                 // 矩阵乘法
tt.store %c_ptr[%rm, %rn], %c     // 存回 global

你的 compiler.py 需要把这些操作翻译成 AscendC:

// AscendC equivalent:
LocalTensor<float16> aTile = inA[rm][rk];     // 从 GM 搬到 UB
LocalTensor<float16> bTile = inB[rk][rn];
LocalTensor<float>  cTile;
Matmul<cubePara>()(aTile, bTile, cTile);      // Cube Unit 做 matmul
outC[rm][rn] = cTile;                          // 写回 GM

核心挑战:

1. 内存搬家的效率 Triton 的 tl.load 有 mask 和边界处理。在 Ascend 上需要映射到高效的 DMA 拷贝(DataCopy 指令)。

2. tl.dot 的映射 NVIDIA 上 tl.dot 编译成 mma.sync(Tensor Core 指令)。Ascend 上映射成 Cube Unit 指令,但 tile 形状、数据布局、精度都有不同约束。

3. 流水线 NVIDIA 有 num_stages(pipeline stages)做 latency hiding。Ascend 有类似的流水机制,但实现方式不同——你需要在 AscendC 里显式地用 Pipedouble buffer

4. 控制流 Triton 的 for 循环、if 分支在有 warp 概念的 GPU 上开销小。在 Ascend 上需要更小心地处理分支发散。

你的学习路线

已掌握 (Ex00-09):              下一步 (实习准备):
  Triton kernel 编写 ✓           AscendC 语法和编程模型
  FlashAttention ✓              Cube Unit 的 tile 约束
  Matmul tiling ✓               UB 内存管理
  Operator fusion ✓             DataCopy / Pipe 流水
  Autotuning ✓                  Ascend 的 profiler (msprof)
  GQA ✓                         torch_npu 的运行环境

推荐的 Ascend 热身步骤

  1. 读 AscendC 官方文档AddMatmul 两个例子
  2. 理解 Cube Unit 的约束 — 为什么输入必须是 16×16 的倍数?怎么处理不规则 shape?
  3. 对比 Triton FA kernel 和 AscendC FA kernel — 同一个算法在不同硬件上的实现差异
  4. 搭建环境 — Atlas 推理卡 + CANN 软件栈 + torch_npu

跟 NVIDIA 学到的知识的可迁移性

好消息:你学的 90% Triton 知识直接可迁移。

知识 NVIDIA 专用 跨后端通用
Tiling 策略
在线 softmax 算法
Operator fusion 模式
Roofline 分析
Autotune 思想
Shared memory 使用 warp 相关 大部分
tl.dot 行为 Tensor Core 相关 需适配

所以 9 个 exercise 没有白做——算法和系统层面的理解完全可迁移。需要重新学的只是代码生成那一层:如何把你的 kernel 意图忠实地翻译成 Ascend 的指令。

总结

Triton 之于 Ascend,就像 LLVM 之于新 CPU 架构。你不需要为 Ascend 重写所有 kernel——你只需要写一个 back-end plugin,让 Triton 编译器知道"怎么把 Triton IR 翻译成 Ascend 能跑的东西"。

这是一个编译器工程师 + kernel 工程师的交叉角色。你已有的 Triton kernel 编写经验让你理解"上层想表达什么",现在需要补充"下层怎么执行"。