标准 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 收益越大。