从「为什么 PyTorch 里一个算子会慢」讲起:存储层级的不对称、算术强度与机器平衡点,然后沿着 SIMT → 张量核 → 异步搬运三代硬件演进,说明为什么 CUDA、Triton、TileLang 会依次出现。
- 学习目标
- 前置知识
- 1. 问题的由来:为什么 PyTorch 里一个算子会慢
- 2. 概念与模型
tilelang-tutorial.huandu.me
一套面向「会用 PyTorch、但没写过 kernel」的研发人员的完整教材:先建立宏观心智模型, 再按 PyTorch tutorial 的节奏动手写算子、训练并推理一个 7B 模型,最后深入编译期、 NVIDIA 与华为昇腾两条后端。所有结论都指向锁定版本的源码与资料。
第五部分把 DeepSeek 在 2026-09-30 面向昇腾开源的整套基础设施(TileLang 昇腾版、 TileKernels、DeepGEMM-Ascend、FlashMLA、DeepSelect、DeepEP-Ascend)逐层拆开, 以 TileLang 为主线讲透从训练到推理的全流程。
进度只保存在这台设备的浏览器里(localStorage),不上传、不做服务端持久化。换浏览器 或清空站点数据会重置,下方可以手动重置。
从「为什么一个算子会慢」讲到 roofline 与算术强度,先建立可计算、可验证的 性能直觉,再谈写法。每个概念都先回答「它解决什么问题」。
每一条机制都落到锁定版本的具体文件与行号,写清「用户写法 → IR → pass → 目标代码」。全书基于 release tag,而不是每天都在变的 main。
NVIDIA 与华为昇腾并重:既讲通用抽象,也讲 Cube/Vector、UB/L1/L0A-L0C、 MTE 流水与自动调度这些让昇腾真正跑起来的差异。
按目标选一条;每条路径都从第 1 章开始,不必从头读到尾。
第 1–5 章 → 第 7–15 章 → 第 27–28 章
跳过编译器内部细节,先把语言、原语与四类算子实战走一遍,再补上性能方法论。
第一、二部分全部 → 第 16–18 章
在算子实战之后接上 MoE/MLA,然后完成 7B 模型的训练与推理两条端到端链路。
第 1–4 章 → 第 12 章 → 第 20–26 章 → 第 29–31 章
IR 与 pass、布局推导、流水与同步、两条后端,最后落到写 pass 与提交 PR。
第 1–12 章 → 第 19、23、25 章 → 第五部分(第 32–39 章)
先把语言、原语与昇腾后端打通,再逐章拆解 DeepSeek 2026-09-30 开源的整套昇腾基础设施,最后拼成训练与推理的完整链路。
五部分递进:宏观 → 实用 → 深入 → 前沿 → 一次真实开源的完整拆解。 每章都有独立页面、可交互习题与整章自测。
先建立正确的心智模型:TileLang 为什么存在、它处在软件栈的哪一层、与同类框架比强在哪。
从「为什么 PyTorch 里一个算子会慢」讲起:存储层级的不对称、算术强度与机器平衡点,然后沿着 SIMT → 张量核 → 异步搬运三代硬件演进,说明为什么 CUDA、Triton、TileLang 会依次出现。
把「一次训练迭代」和「一次推理请求」拆成算子的清单与账本:每个算子要搬多少字节、算多少次、是 memory-bound 还是 compute-bound,以及 torch.compile 的边界在哪、什么时候必须自己写 kernel。
TileLang 的核心主张:把调度空间(线程绑定、layout、tensorize、流水)从数据流里解耦出来。讲清 tile 的定义、beginner/developer/expert 三层接口,以及可组合(composable)到底是什么意思。
给出一张可随时重画的仓库结构图与一次编译的完整路径:前端 → TIR → pass pipeline → device codegen → host wrapper,并对比 CUDA 与 Ascend 两条管线。
用「用户必须提供的硬件相关决策数量」这把尺子,把 CUDA、Triton、CUTLASS-CuTe、TVM、cuTile、Ascend C、PTO-DSL 放到同一条纵轴上,再用同一个带融合 epilogue 的 GEMM 逐层对照。
用存储层级、执行单元、数据搬运、同步四个维度描述任意加速器,再分别落到 NVIDIA(grid/warp/Tensor Core/TMA/cluster)与昇腾(Cube/Vector/MTE/UB/L1/L0)上,并解释为什么「GPU 是 SIMT、NPU 是 SIMD」已经过时。
按 PyTorch tutorial 的节奏动手:写第一个 kernel、吃透核心原语、完成四类算子实战,最后训练并推理一个 7B 模型。
装好 TileLang 并写出第一个 kernel:安装的三条路径、kernel 的四个部件、如何把生成的 CUDA 源码逐行读回来,以及 JIT、编译缓存与 kernel 复用的行为。
把第 7 章的生成源码往回拆:内核定义与网格、dtype 与内存作用域、T.Parallel/循环/控制流各自对应 IR 里什么、代价是什么,以及 Python 兼容性的边界在哪。
逐个讲透核心原语:T.copy 的搬运与越界语义、T.gemm 如何选指令、reduce/atomic/fill 的用法与坑,以及昇腾专属的 T.dual_copy、T.SimtVF/T.SimdVF 为什么必须存在。
解释「同一个张量为什么会有摆法问题」:从 fragment 与线程的对应、bank conflict、swizzle 到昇腾的 fractal 与 ND→NZ,并给出把布局画出来观察的可复现步骤。
把搬运与计算重叠起来:T.Pipelined 的 stage/order 语义、多缓冲的容量代价、如何判断一份配置还放不放得下,以及 autotuner 的搜索空间怎么定义、结果怎么读。
把 kernel 问题分成生成、正确性、性能、偶发四类,给出对应的工具链:编译期日志与 IR Lower Trace、运行期打印与异常 dump、对拍容差的来源,以及 Nsight Compute / msProf 的读法。
从最不起眼的逐元素算子讲起:用访存账本说明融合的价值,再逐步实现 scale+add、LayerNorm/RMSNorm 与 RoPE,最后合成 RMSNorm+RoPE+量化的融合算子。
GEMM 的性能账本与五级实现阶梯:从朴素分块到 shared memory、软件流水、swizzle 注解与官方 intrinsic 版本,再到 split-K/stream-K/grouped 与低精度变体的选择依据。
从朴素 attention 的三趟访存推导到 online softmax 的一趟实现,逐级加上分块、流水与片上复用,并覆盖 causal、varlen、GQA、decode、paged KV 与 split-KV 等变体。
大模型里最贵的两段结构:MoE 的路由、重排与 grouped GEMM,以及 MLA 的 KV 压缩如何改变访存与 kernel 形态;最后给出一次 decode step 的耗时占比与瓶颈类型总结。
用 HuggingFace 的数据、tokenizer 与权重,把 TileLang 算子接进真实的训练循环:显存与算力两本账、数据管线、混合精度与 checkpoint,以及哪些热点算子值得换成自己的 kernel。
训练完的模型怎么变成服务:prefill 与 decode 的差异、KV cache 的显存账本、paged KV 与 continuous batching,以及 decode attention、量化 GEMM 如何接进 vLLM / TileRT 这类框架。
在昇腾上写第一个算子:环境与 CANN 安装、GM→UB/L1/L0 的数据流、Ascend C 对照实现、msProf 调优、ACLGraph 入图与设备端调试手段。
把 TileLang 从黑盒变成可读的编译器:IR 与 pass、布局推导、流水与同步、NVIDIA 与昇腾两条后端、性能方法论。
把 TileLang 的编译期读成一条可验证的管线:TIR 的基本构件、CUDA 与 Ascend 两条 pass 序列的分组与职责,以及 LayoutInference、LowerTileOp、ThreadSync 这些承重 pass 各自改变了什么。
layout 推导的形式化与实现:约束从哪里来、如何在 tile op 图上传播、冲突时如何权衡转换代价,以及昇腾的 fractal 归一化、ND→NZ 与边界填充为什么是另一类问题。
软件流水的算法层:依赖距离与模调度、缓冲版本数的推导、多缓冲的物化与复用,以及从读写集推导同步点(CUDA 的 ThreadSync 与昇腾的 InsertSync)的原理。
昇腾后端最有技术含量的一步:把 kernel 拆成带资源标签的任务、估计延迟与容量、用 Z3 求解调度与多缓冲,再把解落成真实的 Set/Wait。讲清模型、约束与它失效时的后果。
NVIDIA 后端如何把 tile 程序翻译成 CUDA 与 PTX:指令选择的谓词链、TMA 判定、warp specialization、cluster/DSM,以及 Blackwell 的 tcgen05 与 TMEM 带来的新约束。
昇腾后端的四条路线与它们各自的落点:主干 ascend 方言、适配器的 Ascend C&PTO 与 AscendNPU IR,以及 T.alloc_l1/l0a/l0b/l0c、dual_copy 在代码生成里变成了什么。
从装饰器到可调用 kernel 的完整链路:JIT 生命周期、缓存键与失效、packed ABI 的参数校验、五个 adapter 的分工,以及张量转换、stream 语义与 autograd 的边界。
低精度的数值基础与工程约束:fp16/bf16/fp8/MXFP4/int4 的表示与误差、block scale 的打包与存储布局,以及如何选参考实现对拍、如何区分数值误差与逻辑错误。
把前面的经验上升成方法论:先算(性能模型、roofline)、再定 tile、再定流水,最后才写代码;配一个完整的融合算子案例与一份反模式清单。
把「读编译器」变成「改编译器」:C++ 与 Python pass 的双语结构、写一个新 pass 的完整流程、IR diff 与性能回归怎么测,以及评审时会被问什么。
区分「已发布 / 只在 main / 只在 PR」三种状态,然后给出从 issue 到 PR 的完整贡献路径。
区分「已发布 / 只在 main / 只在未合入 PR」三种状态,逐条核对 TVM 迁移、CuteDSL 后端、Ascend 950 与 PTO、持久化 kernel、AutoSimtVF 等方向的现状,并给出持续跟踪上游的方法。
把改动变成上游的一部分:写算子、改编译器、加后端三条路径各自的门槛与测试要求,issue→RFC→PR→CI 的流程,以及昇腾双仓库的协作方式与自检清单。
DeepSeek 在 2026-09-30 开源了面向昇腾的一整套基础设施。用 TileLang 当主线,把这六个组件从算法、TileLang 写法、后端实现到 128 卡端到端串成一条完整链路。
2026-09-30 DeepSeek 面向昇腾开源了六个组件。本章给出全景地图:每个组件解决什么问题、与 NVIDIA 侧怎么一一对应、在一次训练 step 和一次推理请求里各占哪一段,以及怎么自己核验版本、硬件与可用状态。
TileKernels 是用 TileLang 写的生产算子库,同一份算法提供 CUDA 与 Ascend 两个后端。以 top-k gate 为例逐行对照两种写法,讲清哪些部分平台无关、哪些必须重写,以及量化、RoPE、Rand 这些算子族的接口。
另一类算子:不是矩阵乘,而是访存与小算子为主的结构性算子。以 Engram 论文与 mHC 论文(arXiv:2512.24880)为定义、以 TileKernels 的 mhc/ 与 engram/ 源码为实现,讲透门控融合、Sinkhorn 归一化,以及「除 prenorm GEMM 外都用 TileLang」这条边界。
当 TileLang 不够用(或不该用)时,专家怎么在昇腾上把 GEMM 做到 99% 硬件利用率。讲清 MAD 原语抽象包了什么、JIT 与启发式怎么选配置、MegaMoE 为什么能把通信和两个 grouped GEMM 融进一个 kernel,以及哪一类算子反而适合交给 TileLang。
官方为昇腾稀疏注意力写了一篇技术报告。本章把它讲成一条可跟着走的设计过程:从算存比推出「64 head/AI Core、1C2V、accumulator 放 UB」,再讲 KV 的 ND2NZ 时机、双流水排布、gather2 与 skip-scale,并划清哪些结论能用 TileLang 表达、哪些必须下钻。
算法先行,硬件随后。用阈值扫描 + 随机块序 + RadixSelect 把 TopK 的额外计算量压到 O(k log(N/k)),并给出期望复杂度的证明;再看 CUDA 侧怎么用 mask 与 PTX 把常数压到极致。它同时回答了「什么类型的算子该手写而不是用 TileLang」。
MoE 的专家并行会引入 all-to-all,而通信很容易把算力省下的东西吃回去。本章讲清 EP dispatch/combine 的语义、FP8 dispatch 与延迟 epilogue、HCCL/UBMEM/URMA 的分工、DeepJIT 为什么要在运行时编译,以及 EP8–EP128 的带宽数字怎么读。
收口章:把六个组件拼回一条完整链路,分别走一遍训练的一步与推理的 prefill/decode,给出算力、片内带宽、卡间带宽三类资源的预算账与瓶颈定位清单,并把前面所有「未实现 / 实验性」的结论集中成一份诚实的现状清单。