跳转到主要内容

Causal Attention:为什么 KV hit 后 Attention 按 1 - h² 缩放

· 约 4 分钟阅读

1. 先把 causal attention 想成一个下三角

这里说的是 causal attention,也就是自回归模型里的注意力约束:第 i 个 token 只能看 1..i 这些历史 token,不能看未来 token。

假设输入有 N 个 token:

token:  1  2  3  4  5  ... N

在 prefill 阶段,模型一次性处理整个 prompt。每个 token 的 attention 工作量大概是:

第 1 个 token 看 1 个
第 2 个 token 看 2 个
第 3 个 token 看 3 个
...
第 N 个 token 看 N 个

总量近似是:

1 + 2 + 3 + ... + N = N(N+1)/2 ≈ N² / 2

所以 prefill attention 的核心成本更接近二次方级别,而不是线性级别。

把它画成矩阵,就是一个下三角区域:

       key/value token
       1 2 3 4 5 6 7 8
query
1      x
2      x x
3      x x x
4      x x x x
5      x x x x x
6      x x x x x x
7      x x x x x x x
8      x x x x x x x x

每个 x 表示一次 query token 对 key/value token 的 attention 交互。整个 prefill attention 大概就是这个下三角面积。

2. KV hit 省掉的是 prefix-prefix 区域

现在假设 KV cache hit 了前 hN 个 prefix。以 N=8、前 4 个 token hit 为例:

prefix hit:  1 2 3 4
miss suffix: 5 6 7 8

prefix 内部这块已经有缓存,不需要重算:

       1 2 3 4 | 5 6 7 8
1      .
2      . .
3      . . .
4      . . . .
-------------------------
5      x x x x | x
6      x x x x | x x
7      x x x x | x x x
8      x x x x | x x x x

关键点是:虽然前 4 个 token hit 了,但后面的 suffix token 5,6,7,8 仍然要 attend 到前面的 prefix token。因为 token 5 的表示依赖 token 1..5,token 8 的表示依赖 token 1..8。

所以 KV hit 省掉的不是“前 hN 个 token 的线性计算量”,而是 prefix-prefix 这块二维三角区域。剩下的 attention 工作量由两部分组成:

  1. suffix 对 prefix 的 attention:矩形区域 M * H
  2. suffix 内部的 causal attention:三角区域 M² / 2

其中:

H = hN          // hit prefix 长度
M = (1-h)N      // miss suffix 长度

3. 公式为什么是 1 - h²

剩余 attention work 约为:

M * H + M² / 2

原始 0-hit 全量 prefill attention work 约为:

N² / 2

两者相除,得到相对工作量:

(M * H + M² / 2) / (N² / 2)
= 2MH/N² + M²/N²
= 2h(1-h) + (1-h)²
= 1 - h²

因此在 KV cache hit ratio 为 h 时,attention bucket 常用的一阶近似是:

T_attn(h) = T_attn0 * (1 - h²)

这个公式表达的是:KV hit 后,attention 省掉的是 prefix-prefix 的二维区域;但 suffix 仍然要看 cached prefix,所以 attention 成本下降慢于 1-h

4. 和 FFN 的线性缩放有什么区别

FFN / MLP / MoE / 大部分 GEMM 更接近逐 token 独立计算。prefix hit 之后,hit 的 token 不需要重新跑 FFN,miss 的 suffix token 需要跑,所以它们更接近线性缩放:

T_ffn(h) = T_ffn0 * (1 - h)

Attention 不同,因为一个 suffix token 的 attention 输入不仅包括 suffix 内部,也包括 cached prefix。它仍然要和 prefix 的 K/V 发生交互。

举个数值例子,h = 0.5,一半 prefix hit:

FFN / GEMM 类线性部分:剩 1 - h = 50%
Attention 部分:剩 1 - h² = 75%

也就是说,命中一半 prefix,并不代表 attention 只剩一半,因为后半段 token 仍然要看前半段 prefix。

5. 放到仿真器里应该怎么用

对 KV cache hit ratio 做 prefill TTFT 修正时,可以先把 0-hit TTFT 拆成几类:

TTFT0 = T_attn0 + T_ffn0 + T_comm0 + T_other0

一个可解释的一阶模型是:

TTFT_compute(h) = T_attn0 * (1 - h²)
                + T_ffn0  * (1 - h)
                + T_comm0
                + T_other0_fixed

含义是:

  • attention bucket 按二维 causal attention 剩余区域缩放。
  • FFN / MoE / GEMM 按 miss token 数线性缩放。
  • communication / dispatch / runtime 这类固定开销保守不缩放。

如果还要模拟 KV cache 从存储池或 Host 加载到 GPU HBM 的成本,还需要再加 transfer term:

T_load(h) = h * ISL * S_kv_per_gpu / B_transfer

TTFT(h) = T_fixed
        + T_load(h)
        + T_compute_var(h)
        - overlap_ratio * min(T_load(h), T_compute_var(h))

这里的 overlap_ratio 用来表示 KV load 和 miss-token compute 能重叠多少:

  • 0:完全不重叠,偏线上复刻口径。
  • 1:充分重叠,偏仿真优化目标口径。

6. 这个近似的边界

1 - h² 不是逐 kernel 精确公式,它适合解释和估算 attention bucket 的主要趋势。使用时要注意几个边界:

  1. 如果 breakdown 能把 QKV projection、O projection 和真正的 FlashAttention 分开,那么 projection 更接近 1-h,不应该强行放进 1-h²
  2. 如果模型使用 sliding window、sparse attention、CSA/HCA、MLA 等机制,attention 的可见区域不再是标准 dense causal 下三角,缩放函数需要按实际可见区域重算。
  3. 如果 kernel 是 memory-bound,延迟不一定严格等于理论交互数量缩放;实际还会受 HBM、cache locality、kernel launch、batch shape 影响。
  4. 如果 KV cache 存在外部存储池,attention compute 下降不代表 TTFT 一定下降;高 hit ratio 下可能转为 KV load 带宽受限。

7. 一句话记忆

Causal attention 的 prefill 成本像一个下三角面积。KV hit 省掉的是 prefix-prefix 的三角区域,剩下的 suffix 仍然要看 prefix,所以 attention 剩余成本近似是 1 - h²,而不是 1 - h

相关页面

修改历史2 次提交