跳到主要内容
TileLang 系统教程

tilelang-tutorial.huandu.me

第 15 章 算子实战 III:Attention 家族

/code/15-operators-attention/

04-block-flash-attention/README.md2325 字节
# 示例 4:本章的参考实现(五个变体逐个对照)

## 目的

这是本章的**参考实现**:把官方 kernel 的主循环(`example_mha_fwd_bhsd.py:40-100`)
逐行搬到 CPU 上,用同一个循环结构覆盖五个变体,每个变体都与一个**独立的、
直接建 mask 的精确 softmax** 对照。正文里的每一处「这一版改了什么」都应该能在这里
找到对应的数值结论。

| 用例 | 对应官方案例 | 检查什么 |
| --- | --- | --- |
| 1 | `example_mha_fwd_bhsd.py` | 五种 `block_m × block_n` 组合都能复现精确结果 |
| 2 | 同上(`past_len`) | causal + KV cache 前缀;`past_len = 0/5/12` |
| 3 | `example_mha_sink_fwd_bhsd.py` | sliding window + sink;**同时验证 `check_inf` 是不是必需** |
| 4 | `example_mha_fwd_varlen.py` | `cu_seqlens` 打包 + 短样本的写回 guard |

## 运行前提

Python 3.9+ 与 numpy。不需要 GPU/NPU、不 import tilelang。

## 运行命令

```bash
python3 block_flash_attention.py
```

## 期望输出(写作机实际输出,退出码 0)

```text
case 1: full attention (no mask)
   block_m x block_n       max abs err
               4 x 4         4.441e-16
               8 x 5         3.331e-16
             20 x 20         4.441e-16
               8 x 8         3.331e-16
              4 x 20         4.441e-16

case 2: causal, with and without a KV-cache prefix (past_len)
past_len=  0 (Sq=24, Skv=24): max abs err = 4.441e-16
past_len=  5 (Sq=24, Skv=29): max abs err = 4.996e-16
past_len= 12 (Sq=24, Skv=36): max abs err = 4.441e-16

case 3: sliding window + attention sink, with and without check_inf
window=8, sink=3.5
  with    check_inf : max abs err = 1.665e-16
  without check_inf : max abs err = nan   (NaN in 48 entries)
  causal + sink, no window, check_inf off: max abs err = 2.220e-16

case 4: varlen packing (cu_seqlens) vs. one kernel per sample
lengths=[7, 19, 4, 13], packed total=43, max_seqlen=19
packed kernel vs per-sample reference: max abs err = 4.441e-16
unwritten padding rows (should be 0): 0
```

## 判据(脚本内部有断言,失败即非 0 退出)

* 用例 1/2/4:误差 `< 1e-12`;
* 用例 3:开 `check_inf` 时误差 `< 1e-12`,关 `check_inf` 时**必须出现 NaN**;
* 用例 3 的第二行是关键对照:**causal + sink 而没有 window 时,`check_inf` 关掉也
  不会出错**——因为 causal 的对角线永远可见,运行最大值从第一块起就是有限的。
  「哪些形状才需要 `check_inf`」由此变成一个可验证的问题,而不是经验之谈。

## 注意事项

* 这里的「精确参考」用的是**直接构造 mask + 完整 softmax**,没有用到 online softmax,
  所以两者的独立性能互相印证第 2.4 节的证明。
* 本示例为了可读性把 `block_m` 之外还加了 `q_offset` 参数(用例 4 需要在同一批数据上
  分块调用)。官方实现是用 grid 的 `bx` 表达同一件事。
* **它不是 kernel**:没有 layout、没有多缓冲、没有 warp 划分,因此**不能**从它推出
  任何性能结论。

## 作者实际做到的验证级别

**CPU 逻辑验证**:在 macOS (Apple Silicon)、Python 3.11 + numpy 1.24.1 上实际运行过,
退出码 0,所有断言通过。**没有运行过任何 TileLang kernel。**

← 浏览本章全部代码