执行摘要
- 一句话:扩展EAGLE spec v2树推理支持page>1+topk>1
- 推荐动作:建议所有涉及speculative decoding的开发者和reviewer精读。关键设计决策包括:孔状布局理由、前缀复制方案、行宽保护与failure模式选择。值得关注的设计模式:用always-on CPU断言代替难诊断的GPU错误,是防守型编程的良好范例。
功能与动机
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中的正确使用,同时通过行宽调整和不变性检查根除这两种失败模式。
实现拆解
- 布局与复制:在
eagle_info_v2.py中新增duplicate_prefix_tail_to_draft_branches函数,将前缀的部分尾页复制到每个分支(branch b≥1)的第一个页孔中,确保注意力整页读取一致。
- 分配长度计算:在
managers/utils.py的get_alloc_len_per_decode中,为page_size>1+topk>1实现最坏情况分配长度:num_new_pages_per_topk * page_size * topk,替换之前的NotImplementedError。
- 池大小扩宽:在
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的孔状占用。
- 运行时保护:在
eagle_info_v2.py的prepare_for_decode中添加始终开启的CPU端断言,检查本批次最大分配长度不超过行宽,以清晰错误代替静默损坏。
- 路由与后端门控:在
arg_groups/speculative_hook.py中,移除page_size>1强制回退spec v1的限制,改为仅针对mamba状态模型强制回退;同时添加后端白名单(flashinfer、fa3、triton),对不支持的attention backend报错。
- 测试配套:新增
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(模块 推测解码;类别 source;类型 core-logic;符号 duplicate_prefix_tail_to_draft_branches, prepare_for_decode): 核心实现:新增duplicate_prefix_tail_to_draft_branches函数实现前缀尾页复制;在prepare_for_decode中添加行宽不变性检查。
python/sglang/srt/model_executor/model_runner_kv_cache_mixin.py(模块 内存池;类别 source;类型 data-contract): 池初始化:在_init_pools中加宽req_to_token行宽以容纳draft孔状占用,避免KV泄漏和OOB。
test/registered/spec/eagle/test_spec_eagle_topk_page.py(模块 测试;类别 test;类型 test-coverage;符号 TestEagle3Page64Topk8, TestEagleLlama2Page4Topk8): 新增测试用例覆盖EAGLE3(page64+topk8)和Llama-2(page4+topk8),确保功能正确性。
python/sglang/srt/arg_groups/speculative_hook.py(模块 参数配置;类别 source;类型 core-logic;符号 _handle_eagle_family): 路由修改:解除page_size>1对spec v2的限制,添加后端白名单检查。
python/sglang/srt/managers/utils.py(模块 工具函数;类别 source;类型 core-logic;符号 get_alloc_len_per_decode): 分配计算:实现page_size>1+topk>1的get_alloc_len_per_decode,此前为NotImplementedError。
关键符号: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
池初始化:在_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),
)
评论区精华
开发者在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讨论。
风险与影响
关联脉络
- PR #26866 Spec v2 tree drafting (topk>1): 本PR栈在该PR之上,扩展了其page_size>1支持。
- PR #27338 Fix EAGLE draft CUDA-graph
kv_indices under-allocation for topk > 1: 关联修复:flashinfer后端中draft kv_indices缓冲区缺少topk因子导致OOB,与本PR共同解决page>1+topk>1问题。
- PR #27360 Fix fa3 EAGLE draft-decode expand page_table scatter OOB for topk>1 + page_size>1: 关联修复:fa3后端中expand page_table scatter OOB,与本PR一起确保page>1+topk>1在主要后端上正确工作。
参与讨论