Prhub

#27460 Fix MLA EAGLE draft CUDA-graph `kv_indices` under-allocation for `topk > 1`

原始 PR 作者 hnyls2002 合并时间 2026-06-07 07:28 文件变更 2 提交数 4 评论 3 代码增减 +28 / -1

执行摘要

修复 MLA EAGLE draft CUDA-graph kv_indices 欠分配

关联 Issue #27338 暴露了非 MLA EAGLE draft 中类似问题,本次是 MLA 后端的对等修复。PR body 明确指出:generate_draft_decode_kv_indices 为每个分支打包了 topk 条序列,每行需要 topk * seq_len 个条目,但 CUDA-graph 缓冲区忽略了 topk 因子(而 eager 路径已正确处理)。该 bug 目前是潜伏的(构造器对 topk>1 报错),一旦 topk>1 支持落地就会触发内存损坏。

建议合并并安排 review。此 PR 是一个防御性修复,代码简洁清晰,风险极低,值得快速合入以在未来 topk>1 支持落地前消除一个已知的静默损坏点。

讨论亮点

该 PR review 中没有产生讨论或争议。作者在 PR body 中清晰说明了该 bug 的潜伏性质和对修复必要性的论证,设计决策(添加运行时断言、注册 revert toggle)直接沿用了 #27338 的模式。

实现拆解

  1. 补齐 topk 因子:在 flashinfer_mla_backend.pyinit_cuda_graph_state 方法中,将缓冲区尺寸从 (self.speculative_num_steps, max_bs * self.max_context_len) 改为 (self.speculative_num_steps, max_bs * self.topk * self.max_context_len),与 eager 路径 init_forward_metadata 保持一致。
  2. 添加运行时大小不变性断言:在 common_template 方法中,在调用 kernel 之前插入断言,检查 seq_lens_sum * topk + bs * speculative_num_steps <= kv_indices_buffer.shape[1]。若缓冲区过小,会打印清晰的错误信息并直接失败,避免静默越界写入。该断言在 topk=1 路径上不会误触(与 #27338 相同公式)。
  3. 注册 revert toggle:在 pr_fix_toggle.py 中添加了针对本 PR 的 revert YAML 配置,可通过环境变量 SGLANG_DEBUG_REVERT_PR 快速回退此修复,便于调试。
文件 模块 状态 重要度
python/sglang/srt/layers/attention/flashinfer_mla_backend.py 注意力后端 modified 5.52
python/sglang/srt/debug_utils/pr_fix_toggle.py 调试工具 modified 5.0

关键符号

common_template init_cuda_graph_state

关键源码片段

python/sglang/srt/layers/attention/flashinfer_mla_backend.py core-logic

核心修复文件:在 `init_cuda_graph_state` 中补齐 `topk` 因子,在 `common_template` 中添加运行时断言。

    def common_template(
        self,
        forward_batch: ForwardBatch,
        kv_indices_buffer: torch.Tensor,
        call_fn: Callable,
    ):
        num_seqs = forward_batch.batch_size
        bs = self.topk * num_seqs
        seq_lens_sum = forward_batch.seq_lens_sum
​
        # 计算 kernel 实际需要的 kv_indices 行长度:
        # 每个分支 topk 条序列,每条序列贡献 seq_lens_sum 个索引,
        # 再加上每个 step 的 batch_size 个索引。
        required_kv_indices_len = (
            seq_lens_sum * self.topk + bs * self.speculative_num_steps
        )
        # 运行时断言:若缓冲区过小则直接失败,避免静默内存破坏
        assert required_kv_indices_len <= kv_indices_buffer.shape[1], (
            f"EAGLE draft kv_indices row too small: need {required_kv_indices_len} "
            f"but row width is {kv_indices_buffer.shape[1]} (topk={self.topk}, "
            f"num_seqs={num_seqs}, seq_lens_sum={seq_lens_sum}, "
            f"num_steps={self.speculative_num_steps}); the buffer must be sized "
            f"max_bs * topk * max_context_len."
        )
​
        # ... 后续 kernel launch 和 forward 循环保持不变 ...
​
    def init_cuda_graph_state(self, max_bs: int, max_num_tokens: int):
        # 注意:行需容纳 topk 倍数量的序列(因 generate_draft_decode_kv_indices
        # 为每个分支打包 topk 个序列),故缓冲区宽度须包含 topk 因子,
        # 与 eager 路径的 init_forward_metadata 保持一致。
        self.cuda_graph_kv_indices = torch.zeros(
            (self.speculative_num_steps, max_bs * self.topk * self.max_context_len),
            dtype=torch.int32,
            device="cuda",
        )
​
        for i in range(self.speculative_num_steps - 1):
            self.attn_backends[i].init_cuda_graph_state(
                max_bs, max_num_tokens, kv_indices_buf=self.cuda_graph_kv_indices[i]
            )

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

  1. 回归风险低:此修复仅改变 CUDA-graph 路径下缓冲区大小并添加断言,不影响 eager 路径或非 MLA 后端。断言在 topk=1 时不会触发;缓冲区变大不会影响已有 kernel 逻辑。
  2. 潜在性能影响:缓冲区尺寸增加 topk 倍,对于大 topk 值可能增加显存占用。但考虑到 topk 通常较小(常见值 4-8),且该缓冲区生命周期与 CUDA graph 一致,影响可忽略。
  3. 缺少测试配套:本次没有新增测试用例,仅依赖断言语义。建议未来在 topk>1 支持落地时补充覆盖。
  1. 对用户的影响:当前无影响(topk=1 路径行为不变);为未来 topk>1 支持提供了正确性保障。
  2. 对系统的影响:仅修改 MLA 后端的 CUDA-graph 初始化路径,不影响其他后端或 eager 执行。
  3. 对团队的影响:作为 #27338 的对等修复,降低了跨后端一致性的心智负担,且 revert toggle 便于调试。
缺少测试覆盖 显存占用小幅增加

关联 Issue

#27338 [Bug] Fix EAGLE draft CUDA-graph `kv_indices` under-allocation for `topk > 1`

完整报告

参与讨论