外观
第 2 篇:经典算子与 Kernel 实战
假设我们已经能测准一段 GPU 程序的时间,也能从 trace 里找到最慢的 kernel。接下来怎样改它?从这一篇开始,我们拿六种具体算子做实验:先算清输出依赖哪些输入,再决定如何分工,最后用测量检查这个决定。
六章会逐步增加问题的难度。数组加法的每个输出彼此独立;求和需要让线程合作;矩阵乘还要让多个输出复用输入。学会这些基本模式后,我们再把它们组合成 Attention 和 RMSNorm。
怎样阅读这一篇
每章先用一个能手算的小输入解释算法,配有可播放、可单步查看的动画。画面默认静止,先看清当前步骤再前进即可;动画里的时间只用来展示计算关系,不表示真实 GPU 耗时。
理解算法后,在 HIP / Triton 标签页中选择一条实现路线。第一次阅读只学一种语言也可以,页面会在本次浏览中记住选择。两种实现共用前面的数学解释和后面的实验结果,想对照时再切换标签。
如果还不熟悉 thread、block、program 或 tile,可以先读附录 D:HIP 与 Triton 的编程范式。附录用同一个数组加法解释两种写法,第 8 章也会在需要时给出入口。
六章怎样递进
| 章节 | 先回答的问题 | 动画帮助你看清什么 |
|---|---|---|
| 第 8 章:逐元素算子 | 每个输出互不依赖,怎样分配下标? | 线程与元素的对应、访问顺序、向量化尾部、Triton mask |
| 第 9 章:归约算子 | 多个输入怎样合成一个输出? | 求和树的中间值、block partial 与第二次归约 |
| 第 10 章:归一化算子 | 怎样把分数稳定地变成概率? | 减最大值、取指数、求和与归一化 |
| 第 11 章:矩阵乘类算子 | 一份输入怎样为多个输出服务? | 点积、分块加载与累加结果 |
| 第 12 章:融合算子 | 能否边读边算,少存中间矩阵? | Attention 的物化数据流与在线状态更新 |
| 第 13 章:Fused RMSNorm | 怎样把逐元素、归约和融合组合起来? | 平方、求均值、广播尺度与乘权重 |
开始前需要会什么
这一篇会继续使用前几章的方法:
- 第 4 章:启动一个 HIP kernel,并理解全局下标。
- 第 5 章:预热、重复、GPU event 计时,区分 kernel 时间与端到端时间。
- 第 6 章:从 trace 中筛选目标 kernel,读懂工作规模和资源字段。
- 第 7 章:数清算法所需的计算与数据量,用 Roofline 提出下一步实验。
不必一次记住所有工具选项。读代码时先问“谁负责这个输出”,读结果时再问“这次时间包含什么”。
从实现走到实验
每章的源码在 code/part2-kernels/chapterN/。HIP 和 Triton 实现、运行入口、实验记录放在一起;正文摘录关键片段,完整输入生成、校验和计时仍以源文件为准。
| 入口 | 用途 |
|---|---|
run_all.sh | 运行本章实现与正确性检查 |
profile_all.sh | 按实现分别采集 trace;参数以各章说明为准 |
EXPERIMENT.md | 记录环境、命令、源码身份和结果边界 |
evidence/ | 保存正文历史表格所依据的可追溯汇总 |
本书当前环境统一为 Radeon RX 9070 XT(gfx1201)+ ROCm 10.0 + 原生 Ubuntu 24.04。已有性能表保留采集时的 ROCm 版本;每组数字只描述相应输入、源码和测量协议。
做完一次练习,留下输入、参考答案、测量范围和观察就已经很有价值。某个版本变慢同样值得记录:它提醒我们,减少一次写回、增加一个 tile 或少启动一个 kernel,都需要放回完整程序中验证。