标准 Attention 的可拆分性
标准 attention 对一个 query token q 的计算是:
o = \text{softmax}\left(\frac{q K^T}{\sqrt{d}}\right) V
关键性质:如果 KV 可以分成两段不相交的子集 K = [K_A; K_B],则:
\text{LSE}_A = \log \sum_{j \in A} \exp\left(\frac{q k_j}{\sqrt{d}}\right), \quad o_A = \text{softmax}(q K_A^T) V_A
\text{LSE}_{total} = \log\left(e^{\text{LSE}_A} + e^{\text{LSE}_B}\right)
o = e^{\text{LSE}_A - \text{LSE}_{total}} \cdot o_A + e^{\text{LSE}_B - \text{LSE}_{total}} \cdot o_B
这就是 merge_state_v2
做的事,两段分开算再合并,数学上完全等价于一次完整
attention。
Topk = 1 时没有问题
设前缀 KV 长度为 L,draft tokens 长度为 S:
Request i: [prefix_KV (L tokens)] + [draft_KV (S tokens)]
一次 flash_attn_with_kvcache
搞定,cache_seqlens = L + S。
Topk > 1 引入了什么问题
以 topk=4、S=3 为例,Target
Verify 阶段:
Request i 有 4 条候选树,每条 3 个 draft tokens:
Branch 0: [d_00, d_01, d_02] 各自需要 attend: prefix_KV_i + 本分支祖先
Branch 1: [d_10, d_11, d_12] 各自需要 attend: prefix_KV_i + 本分支祖先
Branch 2: [d_20, d_21, d_22] ──────────────────────────────────────────
Branch 3: [d_30, d_31, d_32] 共享同一段 prefix_KV_i !!
朴素展开(Naive Expand)方案:将 batch
展成 B × topk 个虚拟请求:
| 虚拟请求 (i, k) | Q | KV |
|---|---|---|
| (i, 0) | 3 个 query | prefix_KV_i (L) + branch0_KV (3) |
| (i, 1) | 3 个 query | prefix_KV_i (L) + branch1_KV (3) |
| (i, 2) | 3 个 query | prefix_KV_i (L) + branch2_KV (3) |
| (i, 3) | 3 个 query | prefix_KV_i (L) + branch3_KV (3) |
prefix_KV_i 被重复读取 topk 次,总读 KV 量:
\text{Naive} = B \times topk \times (L + S) = B \times 4 \times (L + 3)
当 L = 1000 时,主要开销都在反复读那 1000 token 的 prefix。
Cascade 方案的数学结构
将 KV 拆成两段,利用 prefix 被所有分支共享:
第一段 attention(共享
prefix,metadata.page_table):
B \text{ 个请求,每个请求的 Q} = \text{所有 topk 分支的全部 query tokens} \text{KV} = \text{prefix\_KV}_i \quad (\text{每个请求只读一次!})
输出:o^{(A)},\text{LSE}^{(A)}
第二段 attention(各自独立的 draft
KV,forward_metadata_spec_decode_expand.page_table):
B \times topk \text{ 个虚拟请求,每个请求的 Q} = \text{本分支 query tokens} \text{KV} = \text{各自分支的 draft tokens (很短,只有 } S \text{ 个)}
输出:o^{(B)},\text{LSE}^{(B)}
合并:merge_state_v2(o_A, LSE_A, o_B, LSE_B)
→ 精确等价于原始完整 attention。
总读 KV 量:
\text{Cascade} = \underbrace{B \times L}_{\text{prefix 只读一次}} + \underbrace{B \times topk \times S}_{\text{draft 各自读}}
多条候选 token(topk)影响的核心
| 方案 | prefix KV 读取次数 | draft KV 读取次数 |
|---|---|---|
| Naive Expand | B \times topk \times L | B \times topk \times S |
| Cascade | B \times L | B \times topk \times S |
| 节省 | topk 倍 | 无节省 |
当 topk = 4,L = 2000,S = 5: - Naive 每请求读 8020 个 KV token - Cascade 每请求读 2020 个 KV token - 节省约 4 倍,且 L 越长、topk 越大,优势越明显
这就是为什么 use_cascade_attn 的触发条件是
topk > 1——topk = 1 时没有共享 prefix
可利用,cascade 无意义;topk 越大,prefix KV
的重复读取浪费越严重,cascade 收益越大。