内核与 Triton
对应课程 Lecture 6:Kernels, Triton(Percy)
什么时候需要手写内核?
PyTorch 已经把矩阵乘法做得很高效(调用 cuBLAS),大多数时候你不需要手写内核(kernel)。但有三类场景例外:
- 很多小操作串起来:比如
softmax(x) + x * y会触发多次显存读写,每次都搬一遍中间结果; - 库没有的实现:自定义注意力、新的归一化;
- 省显存:比如注意力里
的中间矩阵,根本不想物化它。
核心武器是算子融合(fusion):把多个操作写进一个内核,中间结果留在寄存器/共享内存里,不回写显存。
Triton:被 Python 包装的 GPU 编程
NVIDIA 的 CUDA C++ 太难写。Triton 是 OpenAI 开源的 DSL(领域专用语言),用 Python 写内核,自动处理线程调度、共享内存、张量核心映射。你只需要想清楚一个块(block)怎么算:
import triton
import triton.language as tl
@triton.jit
def add_kernel(x_ptr, y_ptr, out_ptr, n, BLOCK: tl.constexpr):
pid = tl.program_id(0) # 第几个程序实例
offs = pid * BLOCK + tl.arange(0, BLOCK) # 本块负责的元素下标
mask = offs < n # 越界保护
x = tl.load(x_ptr + offs, mask=mask) # 从显存读一块
y = tl.load(y_ptr + offs, mask=mask)
tl.store(out_ptr + offs, x + y, mask=mask) # 算完写回Triton 的心智模型:内核 = 一个多维网格上的程序,每个程序处理一小块数据。与 CUDA 的区别是你不管单个线程,只管块级逻辑。
例:融合 softmax 内核
普通 PyTorch 写 softmax 会产生多次读写;融合版本一遍完成:
@triton.jit
def softmax_kernel(x_ptr, out_ptr, n, BLOCK: tl.constexpr):
offs = tl.arange(0, BLOCK)
mask = offs < n
x = tl.load(x_ptr + offs, mask=mask, other=-float("inf"))
x = x - tl.max(x, axis=0) # 数值稳定:减最大值
e = tl.exp(x)
e = e / tl.sum(e, axis=0)
tl.store(out_ptr + offs, e, mask=mask)注意减最大值这步:不做的话大数值的 exp 会溢出——数值稳定性是内核编写中与性能同等重要的主题。
FlashAttention:分块的艺术
FlashAttention 是这一讲的高潮。它解决的问题是:标准注意力要物化
难点:softmax 的分母要对整行求和,而分块计算时每次只能看到一部分——怎么办?
第一步:分块算,增量更新
把 Q、K、V 都切成块,只装得下当前 K、V 块对应的注意力结果。对每个 K、V 块,先算局部分数
维护到当前块为止的运行统计量:
:已见分数的最大值; :已见的 之和; :已累积的未归一化输出。
遇到新块时,更新规则:
处理完全部块后,
为什么快?
- 显存从
降到 (不物化注意力矩阵); - 中间结果全程待在 SRAM,显存读写从
次降到 块; - 分块大小按共享内存容量设计,配合张量核心,实测比朴素实现快 2~4 倍,长序列下优势更大。
理解记忆
FlashAttention = tiling(分块)+ online softmax(增量归一化)+ fusion(融合),三招都是为了让“每字节显存读写换到更多 FLOPs”。它的后续版本 FlashAttention-2/3 进一步优化并行度和流水线。
反向传播怎么办?
训练还需要梯度。FlashAttention 的做法是前向只保存
内核优化的通用方法论
- 先剖析(profiling):用 PyTorch profiler / Nsight 找出真正慢的操作,别猜;
- 数一数:这个操作的 roofline 位置在哪?理想时间和实际时间差多少?
- 融合 + 分块:减少显存往返,把数据留在 SRAM;
- 数值稳定:减最大值、防溢出,与性能一起考虑;
- 基准测试要严谨:预热、同步、多次取均值,排除 Python 开销。
小结
- 手写内核的动机是融合与省显存,不是重复造矩阵乘法。
- Triton 让你以“块”为单位写 GPU 程序,自动处理底层调度。
- FlashAttention 用在线 softmax + 分块,把注意力变成显存线性、速度更快、结果完全精确——现代模型的长上下文能力部分归功于它。