Prhub

#27607 Support spec v2 for Frozen-KV MTP; remove v1 worker

原始 PR 作者 hnyls2002 合并时间 2026-06-10 06:30 文件变更 13 提交数 7 评论 10 代码增减 +750 / -1074

执行摘要

Frozen-KV MTP 迁移到 spec v2 并删除 v1 worker

让 Frozen-KV MTP 能利用 spec v2 的 orchestrator 架构,从而支持 overlap 调度(提升吞吐)并统一与 EAGLE 等其他推测解码算法的代码路径。PR body 明确说明:“Run Frozen-KV MTP on the spec v2 worker — FrozenKVMTPWorkerV2 (orchestrator, subclasses EAGLEWorkerV2) + FrozenKVMTPDraftWorker (draft layer) — supporting both overlap and non-overlap scheduling”。此外,关联 issue #23802 和 #25980 修复了 spec v2 下的 stop 字符串检测边界问题,使 v2 精度能与 v1 匹配。

此 PR 值得精读,特别是学习如何将一种推测解码算法从 v1 迁移到 v2 orchestrator 架构。关键设计决策包括:直接将 FrozenKVMTPDraftWorker 继承 BaseDraftWorker 而非 EAGLEWorkerV2,从而复用 EAGLE 的 verify 契约;通过 AST 移动等价性脚本验证重构正确性。对于代码库维护者,建议关注被删除测试的替代覆盖;对于 spec v2 新用户,可参考此 PR 将其他算法也迁移到 v2。

讨论亮点

Review 中没有实质性讨论,只有一个 approval。Issue 评论中主要是作者触发的 CI 重跑命令(/rerun-test),涉及 test_frozen_kv_mtp.pytest_gemma4_mtp_31b_extra.py 等测试的失败重试,最终全部通过。无未解决争议。

实现拆解

  1. 实现 FrozenKVMTPDraftWorker:在 frozen_kv_mtp_worker_v2.py 中新增该类,继承 BaseDraftWorkerTpModelWorker,将 v1 worker 中大部分方法(如 draft_forwardforward_batch_generation 等)按 spec v2 契约重写,使其可作为 orchestrator 的 draft layer 被调用。
  2. 修改调度路由:在 spec_info.pySpeculativeAlgorithm.create_worker 方法中,将 FROZEN_KV_MTP 的 worker 创建从 v1 的 FrozenKVMTPWorker 改为 FrozenKVMTPWorkerV2(即 v2 orchestrator),并移除原来的 overlap 禁用限制。
  3. 清理依赖与数据类:删除 v1 专属的 frozen_kv_mtp_worker.py 整个文件;从 frozen_kv_mtp_utils.py 中移除不再使用的 select_last_verified_seedcapture_for_decode 函数;从 frozen_kv_mtp_info.py 中删除 FrozenKVMTPVerifyOutput 别名及相关的 _to_frozen_kv_mtp_draft_extend_input 转换逻辑,因为 v2 的 EagleVerifyOutput 可直接使用。
  4. 调整测试配套:删除针对 v1 worker 的单元测试 test_frozen_kv_mtp_all_reqs_finish_in_verify.py;修改 3 个集成测试文件(test_frozen_kv_mtp.pytest_gemma4_mtp_26b_a4b_extra.pytest_gemma4_mtp_31b_extra.py),移除 --disable-overlap-schedule 参数(因为 v2 默认支持 overlap),并添加 --speculative-algorithm FROZEN-KV-MTP 显式指定算法。
  5. 其他配套:在 cuda_graph_runnerscheduler 中做微小调整,适应 v2 worker 的接口变化;在 arg_groups/speculative_hook.py 中更新算法检查,确保 FROZEN_KV_MTP 能在 overlap 开启时正常启动。
文件 模块 状态 重要度
python/sglang/srt/speculative/frozen_kv_mtp_worker_v2.py 推测解码 modified 8.94
python/sglang/srt/speculative/frozen_kv_mtp_worker.py 推测解码 removed 8.89
python/sglang/srt/speculative/spec_info.py 推测解码 modified 6.52
python/sglang/srt/speculative/frozen_kv_mtp_utils.py 推测解码 modified 6.68
test/registered/unit/spec/test_frozen_kv_mtp_all_reqs_finish_in_verify.py 测试 removed 7.24
python/sglang/srt/speculative/frozen_kv_mtp_info.py 推测解码 modified 6.45

关键符号

FrozenKVMTPDraftWorker.__init__ frozen_kv_target_view select_last_verified_seed(已删除) capture_for_decode(已删除) SpeculativeAlgorithm.supports_spec_v2 SpeculativeAlgorithm.create_worker

关键源码片段

python/sglang/srt/speculative/frozen_kv_mtp_worker_v2.py dependency-wiring

核心实现文件:从占位符(raise NotImplementedError)重写为完整的 v2 draft worker(FrozenKVMTPDraftWorker),包含 init、draft forward、verify 等所有关键方法,与 EAGLEWorkerV2 构成 orchestrator 层。

class FrozenKVMTPDraftWorker(BaseDraftWorker, TpModelWorker):
    """Frozen-KV MTP draft worker.    The assistant reads target KV only. It reuses EAGLE's verify input/output
    contract, but owns the seed and recurrent draft loop because there is no
    assistant-side KV extension.
    """
​
    def __init__(
        self,
        server_args: ServerArgs,
        gpu_id: int,
        tp_rank: int,
        dp_rank: Optional[int],
        moe_ep_rank: int,
        attn_cp_rank: int,
        moe_dp_rank: int,
        nccl_port: int,
        target_worker: TpModelWorker,
    ):
        # 保存配置,与 v1 相同的初始化逻辑
        self.server_args = server_args
        self.topk = server_args.speculative_eagle_topk
        self.speculative_num_steps = server_args.speculative_num_steps
        self.speculative_num_draft_tokens = server_args.speculative_num_draft_tokens
        self.gpu_id = gpu_id
        self.device = server_args.device
        self.target_worker = target_worker
        self.page_size = server_args.page_size
        self.speculative_algorithm = SpeculativeAlgorithm.from_string(
            server_args.speculative_algorithm
        )
        assert self.speculative_algorithm.is_frozen_kv_mtp(), (
            "FrozenKVMTPDraftWorker should only be instantiated for "
            "SpeculativeAlgorithm.FROZEN_KV_MTP, got "
            f"{self.speculative_algorithm.name}."
        )
        # 禁用 CUDA graph 自动捕获,由 worker 内部管理
        backup_disable_cuda_graph = server_args.disable_cuda_graph
        server_args.disable_cuda_graph = True
​
        # 共享 target 的 memory pool(只读)
        self.req_to_token_pool, self.token_to_kv_pool_allocator = (
            target_worker.get_memory_pool()
        )
​
        target_cfg = target_worker.model_runner.memory_pool_config
        draft_pool_config = MemoryPoolConfig(
            max_total_num_tokens=64, # 占位值
            max_running_requests=target_cfg.max_running_requests,
        )
​
        self.hot_token_id = None
​
        # 初始化 attention backend
        with (
            empty_context()
        ), speculative_moe_backend_context(), speculative_moe_a2a_backend_context():
            super().__init__(
                server_args=server_args,
                gpu_id=gpu_id,
                tp_rank=tp_rank,
                dp_rank=dp_rank,
                moe_ep_rank=moe_ep_rank,
                attn_cp_rank=attn_cp_rank,
                moe_dp_rank=moe_dp_rank,
                nccl_port=nccl_port,
            )
        server_args.disable_cuda_graph = backup_disable_cuda_graph
​
        self.draft_runner = self.model_runner # 别名
python/sglang/srt/speculative/spec_info.py dependency-wiring

修改调度路由:允许 FROZEN_KV_MTP 算法使用 V2 worker,移除了之前对 overlap 的报错限制。这是启用 v2 的关键开关。

    def supports_spec_v2(self) -> bool:
        # 之前返回 self.is_eagle() and not self.is_frozen_kv_mtp() or self.is_standalone()
        # 现在允许 frozen-KV MTP 也支持 v2
        return self.is_eagle() or self.is_standalone()
​
    def create_worker(
        self,
        ...
    ):
        ...
        if self.is_frozen_kv_mtp():
            # 之前:若 enable_overlap 则 raise ValueError
            # 现在:始终返回 V2 worker(scheduler 根据 overlap 配置决定同步或异步执行)
            from sglang.srt.speculative.frozen_kv_mtp_worker_v2 import (
                FrozenKVMTPWorkerV2,
            )
            return FrozenKVMTPWorkerV2

评论区精华

测试失败与重跑 测试

CI 中 test_gemma4_mtp_31b_extra.py 多次失败,作者通过 /rerun-test 触发重跑,最终全部通过。

结论:测试通过,无需代码修改。 · 已解决

风险与影响

  1. 精度回归风险:v2 架构下 draft 与 verify 的交互模式与 v1 不同,若存在未覆盖的 corner case(如 stop 字符串出现在多 token 接受中间),可能产生精度下降。作者已通过 GSM8K 评测和确定性测试验证 v2 与 v1 精度一致(~0.730),且依赖的 stop 修复 issue #25980 已合入 main,风险较低。
  2. 性能风险:v2 orchestrator 引入额外上下文切换,可能增加延迟;但 overlap 调度可带来吞吐提升。评测显示 top-1 接受长度约 2.83,与 v1 一致,表明无显著性能退化。
  3. 测试覆盖损失:删除了 test_frozen_kv_mtp_all_reqs_finish_in_verify.py,该测试专门验证 v1 的“verify 完成所有请求后安装 idle draft”逻辑。新代码通过 FrozenKVMTPDraftWorker 继承的 EagleDraftInputV2Mixin 来处理类似场景,但缺少针对性单元测试,存在回归隐患。
  4. 兼容性风险:删除了 frozen_kv_mtp_worker.py,任何外部代码若直接引用 FrozenKVMTPWorker 将断裂。仓库内所有引用已通过本 PR 更新,但外部插件或定制代码需同步迁移。
  5. CUDA 图兼容性:v2 worker 使用不同的 draft 输入类型,若 CUDA 图捕获未正确处理,可能导致运行时错误。PR 中已调整 frozen_kv_mtp_cuda_graph_runner.py,风险可控。

影响范围:中等。影响所有使用 --speculative-algorithm FROZEN-KV-MTP 的用户。用户无需修改命令行参数即可获得 v2 架构支持(overlap 默认启用)。删除 v1 后,代码库减少约 800 行,维护负担降低。影响程度:对于开启 overlap 调度的场景,吞吐可能提升(与 EAGLE 一致);对于关闭 overlap 的场景,行为透明。测试和评测均显示精度不变。团队后续可复用 EAGLE v2 的 infrastructure 改进(如 piecewise CUDA graph 支持)于 Frozen-KV MTP。

核心路径变更 测试覆盖变化 算法迁移

关联 Issue

#23802 fix: stop-string check misses early matches during speculative decoding
#25980 Fix spec v2 stop output boundary

完整报告

参与讨论