Prhub

#26972 Spec v2 tree drafting (topk>1) with page_size>1

原始 PR 作者 hnyls2002 合并时间 2026-06-07 03:00 文件变更 8 提交数 3 评论 8 代码增减 +228 / -39

执行摘要

扩展 EAGLE spec v2 树推理支持 page>1+topk>1

PR body指出,当page_size>1且topk>1时,draft预留的页对齐足迹(2 * get_alloc_len_per_decode)远超默认的4+num_draft_tokens头空间,导致free侧KV泄漏(pool memory leak detected)和access侧OOB(CUDA gather断言)。本PR旨在解锁page>1+topk>1组合在spec v2中的正确使用,同时通过行宽调整和不变性检查根除这两种失败模式。

建议所有涉及speculative decoding的开发者和reviewer精读。关键设计决策包括:孔状布局理由、前缀复制方案、行宽保护与failure模式选择。值得关注的设计模式:用always-on CPU断言代替难诊断的GPU错误,是防守型编程的良好范例。

讨论亮点

开发者在PR body中详细描述了bug的两个表现(free-side leak和access-side OOB),并提供了两个repro gist(page512 multi-turn和page256 long prompt)以及用SGLANG_DEBUG_REVERT_PR=26972验证修复的A-B测试。后续关联PR #27338(flashinfer cuda-graph kv_indices)和#27360(fa3 expand page_table)继续修复其他backends上的同类问题。无额外review讨论。

实现拆解

  1. 布局与复制:在eagle_info_v2.py中新增duplicate_prefix_tail_to_draft_branches函数,将前缀的部分尾页复制到每个分支(branch b≥1)的第一个页孔中,确保注意力整页读取一致。
  2. 分配长度计算:在managers/utils.pyget_alloc_len_per_decode中,为page_size>1+topk>1实现最坏情况分配长度:num_new_pages_per_topk * page_size * topk,替换之前的NotImplementedError
  3. 池大小扩宽:在model_runner_kv_cache_mixin.py_init_pools中,当满足speculative_algorithm非None且page_size>1且topk>1时,将extra_max_context_len提升为max(4+num_draft_tokens, 2*get_alloc_len_per_decode),从而加宽req_to_token行,容纳draft的孔状占用。
  4. 运行时保护:在eagle_info_v2.pyprepare_for_decode中添加始终开启的CPU端断言,检查本批次最大分配长度不超过行宽,以清晰错误代替静默损坏。
  5. 路由与后端门控:在arg_groups/speculative_hook.py中,移除page_size>1强制回退spec v1的限制,改为仅针对mamba状态模型强制回退;同时添加后端白名单(flashinfer、fa3、triton),对不支持的attention backend报错。
  6. 测试配套:新增test/registered/spec/eagle/test_spec_eagle_topk_page.py,包含TestEagle3Page64Topk8(spec v2)和TestEagleLlama2Page4Topk8(spec v1)两个测试用例。修改test_spec_eagle_page.py移除重复符号。
文件 模块 状态 重要度
python/sglang/srt/speculative/eagle_info_v2.py 推测解码 modified 7.76
python/sglang/srt/model_executor/model_runner_kv_cache_mixin.py 内存池 modified 6.71
test/registered/spec/eagle/test_spec_eagle_topk_page.py 测试 added 6.4
python/sglang/srt/arg_groups/speculative_hook.py 参数配置 modified 5.75
python/sglang/srt/managers/utils.py 工具函数 modified 5.71

关键符号

duplicate_prefix_tail_to_draft_branches get_alloc_len_per_decode _init_pools _handle_eagle_family prepare_for_decode

关键源码片段

python/sglang/srt/model_executor/model_runner_kv_cache_mixin.py data-contract

池初始化:在 _init_pools 中加宽 req_to_token 行宽以容纳 draft 孔状占用,避免 KV 泄漏和 OOB。

# Inside _init_pools, after the base extra_max_context_len based on num_draft_tokens:# page>1 + topk>1 reserves a holey draft footprint (2 * get_alloc_len_per_decode
# = topk * num_new_pages * page) far beyond the default num_draft_tokens
# headroom; widen the row to hold it, else free leaks KV and the holey gather OOBs.
if (
    self.server_args.speculative_algorithm is not None
    and self.server_args.page_size > 1
    and (self.server_args.speculative_eagle_topk or 1) > 1
):
    from sglang.srt.managers.utils import get_alloc_len_per_decode
​
    extra_max_context_len = max(
        extra_max_context_len,
        2 * get_alloc_len_per_decode(self.server_args),
    )

评论区精华

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

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

风险与影响

  1. 内存占用增加_init_pools中加宽行宽可能增加req_to_token池内存,尤其在高page_sizetopk下;但由于仅在启用page>1+topk>1时生效,风险可控。
  2. 后端兼容性:仅flashinfer、fa3、triton后端支持page>1+topk>1,若用户尝试其他后端(如flashmla、trtllm_mla)会报错,需确保文档清晰。
  3. GPU kernel调用duplicate_prefix_tail_to_draft_branches调用token_to_kv_pool.move_kv_cache,这是一个GPU操作,可能增加延迟,但仅每轮decode一次,影响有限。
  4. 不变性断言性能prepare_for_decode中的断言涉及CPU-GPU同步?实际使用CPU max和shape,开销低,但需注意不会引入阻塞。

用户影响:使用EAGLE推测解码且page_size>1的用户现在可以启用topk>1(tree drafting),获得更高推测接受率;但必须使用支持的后端。之前该组合要么回退到spec v1(实际上之前代码导致prepare_for_decode报NotImplementedError),要么崩溃。现在正常运作。
系统影响req_to_token池增大可能略微增加显存占用,但仅在开启时。
团队影响:规范解码的维护复杂度增加,需要确保其他backend(如flashmla)也能支持;新增的pr_fix_toggle注册方便调试和回滚。

内存占用增加 后端兼容限制 GPU 同步开销

关联 Issue

#27338 [Bug] Fix EAGLE draft CUDA-graph `kv_indices` under-allocation for `topk > 1`
#27360 [Spec] Fix fa3 EAGLE draft-decode expand page_table scatter OOB for topk>1 + page_size>1

完整报告

参与讨论