Prhub

#27959 [Spec] Remove the DFLASH V1 worker path

原始 PR 作者 hnyls2002 合并时间 2026-06-12 05:37 文件变更 6 提交数 1 评论 4 代码增减 +21 / -884

执行摘要

移除 DFLASH V1 worker 路径,统一 V2

根据 PR 描述,V1 worker 路径已不再需要,V2 worker 已稳定且功能完备。通过删除 V1 代码和 SGLANG_ENABLE_SPEC_V2 门控,可以简化架构、消除条件分支、减少维护负担,并使 DFLASH 的行为与 EAGLE 等其他推测解码算法一致。

建议所有 DFLASH 开发者阅读此 PR,理解 V1 路径删除的原因和 V2 worker 的统一行为。特别是环境变量 SGLANG_ENABLE_SPEC_V2 语义的变更需要注意。该 PR 是架构清理的良好范例。

讨论亮点

PR 没有 review 评论,作者自行合并。在 Issue 评论中,作者触发了针对 DFLASH 相关测试的重跑,所有测试均通过。

实现拆解

  1. 清空 dflash_worker.py 中的 V1 专有方法:移除了 _prepare_for_speculative_decoding_append_target_hidden_to_draft_kvforward_batch_generation_to_int32_device_tensor 方法。更新了类文档字符串为 Shared DFLASH infrastructure (draft model, draft KV materialization)。同时清理了不再使用的导入,如 GenerationBatchResultForwardBatchForwardModeDFlashDraftInput 等。
  2. 精简 dflash_info.py:删除了 DFlashDraftInput 数据类(及其 __post_init__get_spec_adjust_token_coefficientfilter_batchmerge_batch 方法),以及函数 _compute_paged_keep_slotsDFlashVerifyInput.verify 方法。现在该文件只保留 DFlashVerifyInput 类及其 V2 所需的方法(如 prepare_for_v2_verifygenerate_attn_arg_prefill)。
  3. 消除 spec_info.py 中的环境变量门控:修改了 supports_spec_v2 方法,使其无条件返回 True(对于 DFLASH)。修改了 create_worker 方法,直接返回 DFlashWorkerV2,不再有条件分支选择 V1 或 V2 worker。
  4. 更新 speculative_hook.py 中的配置逻辑:移除了 V1 相关的警告日志。现在,当 SGLANG_ENABLE_SPEC_V2 为 0 时,仅设置 disable_overlap_schedule=True 以选择同步非重叠路径,不再区分 V1/V2 worker。
  5. 调整 dflash_worker_v2.py 类注释:更新了 DFlashWorkerV2 的文档字符串,说明它同时支持重叠和非重叠调度(与 EAGLE 一致)。
  6. 更新测试文件:在 test_decode_bookkeeping_ownership.py 中移除了对已删除 DFlashVerifyInput.verify 方法的引用(3 行测试数据)。
文件 模块 状态 重要度
python/sglang/srt/speculative/dflash_worker.py 推测解码 modified 8.84
python/sglang/srt/speculative/dflash_info.py 推测解码 modified 8.65
python/sglang/srt/speculative/spec_info.py 推测解码 modified 6.14
python/sglang/srt/arg_groups/speculative_hook.py 配置 modified 6.02
python/sglang/srt/speculative/dflash_worker_v2.py 推测解码 modified 5.07
test/registered/unit/spec/test_decode_bookkeeping_ownership.py 测试 modified 3.35

关键符号

_prepare_for_speculative_decoding _append_target_hidden_to_draft_kv forward_batch_generation _to_int32_device_tensor _compute_paged_keep_slots DFlashDraftInput.__post_init__ DFlashDraftInput.get_spec_adjust_token_coefficient DFlashDraftInput.filter_batch DFlashDraftInput.merge_batch DFlashVerifyInput.verify DFlashWorker._greedy_sample_from_vocab_parallel_head

关键源码片段

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

关键数据结构和函数的重组,删除了 V1 的 `DFlashDraftInput` 和 `verify` 方法

# python/sglang/srt/speculative/dflash_info.py (head) - 仅保留 DFlashVerifyInput@dataclass
class DFlashVerifyInput(SpecInput):
    """Inputs for a target-model verify forward in DFlash.    The verify forward is run with `ForwardMode.TARGET_VERIFY` so that the target
    model returns logits for all tokens in the block, enabling accept-length
    computation.
    """
​
    draft_token: torch.Tensor
    positions: torch.Tensor
    draft_token_num: int
    # Kept for compatibility with attention backends that gate tree metadata by `topk > 1`.
    # DFLASH verify is linear (non-tree), so this is always 1.
    topk: int = 1
    # Custom attention "allow mask" for TARGET_VERIFY in backends that require it.
    # Semantics follow SGLang speculative conventions: True means the (q, k) pair is allowed.
    custom_mask: torch.Tensor | None = None
    capture_hidden_mode: CaptureHiddenMode = CaptureHiddenMode.FULL
​
    # Shape info for padding (e.g., DP attention / CUDA graph).
    num_tokens_per_batch: int = -1
​
    def __post_init__(self):
        super().__init__(spec_input_type=SpecInputType.DFLASH_VERIFY)
        if self.num_tokens_per_batch == -1:
            # 如果没有显式指定,则使用 draft_token_num 作为每批 token 数
            self.num_tokens_per_batch = int(self.draft_token_num)
​
    def get_spec_adjust_token_coefficient(self) -> Tuple[int, int]:
        return self.draft_token_num, self.draft_token_num
​
    def prepare_for_v2_verify(
        self,
        batch: ScheduleBatch,
        target_worker: "TpModelWorker",
    ) -> tuple[ForwardBatch, bool]:
        """Prepare a DFLASH verify forward batch for overlap scheduling.        The caller computes and stores `batch.out_cache_loc` before this
        method is called. This helper only packages the verify forward and
        pre-initializes either CUDA-graph replay metadata or eager attention
        metadata so the actual forward can run with `skip_attn_backend_init=True`.
        """
        batch.input_ids = self.draft_token
        batch.spec_info = self
        batch.forward_mode = (
            ForwardMode.IDLE
            if batch.forward_mode.is_idle()
            else ForwardMode.TARGET_VERIFY
        )
        batch.capture_hidden_mode = self.capture_hidden_mode
        verify_forward_batch = ForwardBatch.init_new(batch, target_worker.model_runner)
​
        can_run_cuda_graph = bool(
            target_worker.model_runner.decode_cuda_graph_runner
            and target_worker.model_runner.decode_cuda_graph_runner.can_run(
                verify_forward_batch
            )
        )
        if can_run_cuda_graph:
            target_worker.model_runner.decode_cuda_graph_runner.replay_prepare(
                verify_forward_batch
            )
        elif not batch.forward_mode.is_idle():
            target_worker.model_runner.attn_backend.init_forward_metadata(
                verify_forward_batch
            )
​
        return verify_forward_batch, can_run_cuda_graph
python/sglang/srt/speculative/spec_info.py dependency-wiring

移除了 DFLASH V1 的环境变量门控,直接返回 V2 worker

# python/sglang/srt/speculative/spec_info.py (head) - create_worker 中 DFLASH 分支简化def create_worker(
    self, server_args: ServerArgs
) -> Optional[Union[Type[BaseSpecWorker], Type[TpModelWorker], Type[NGRAMWorker]]]:
    assert (
        not self.is_none()
    ), "Cannot create worker for NONE speculative algorithm."
​
    if self.is_dflash():
        # V2 worker 同时驱动重叠和非重叠调度(关闭重叠时由调度器同步运行),与 EAGLE 一致
        from sglang.srt.speculative.dflash_worker_v2 import DFlashWorkerV2
        return DFlashWorkerV2
​
    if self.is_frozen_kv_mtp():
        from sglang.srt.speculative.frozen_kv_mtp_worker_v2 import FrozenKVMTPWorkerV2
        return FrozenKVMTPWorkerV2
​
    # EAGLE / EAGLE3 / STANDALONE / MULTI_LAYER 始终使用 V2 worker
    if self.is_eagle() and server_args.enable_multi_layer_eagle:
        from sglang.srt.speculative.multi_layer_eagle_worker_v2 import MultiLayerEagleWorkerV2
        return MultiLayerEagleWorkerV2
    elif self.is_eagle():
        from sglang.srt.speculative.eagle_worker_v2 import EAGLEWorkerV2
        return EAGLEWorkerV2
    # ... 其他分支不变

评论区精华

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

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

风险与影响

该 PR 删除了 884 行代码,V2 路径的行为保持不变,但仍然存在以下风险:

  • 回归风险:任何依赖 V1 worker 或 SGLANG_ENABLE_SPEC_V2=0 原有 V1 行为的部署,在更新后可能会因工作器选择逻辑改变而出现意外行为(尽管 PR 声称 V2 同步模式等效)。
  • 测试覆盖:虽然相关测试通过,但难以保证所有边缘场景(如自定义 draft window size、paged mode 组合)都已被覆盖。
  • 环境变量语义变化SGLANG_ENABLE_SPEC_V2 不再控制 worker 类型,仅控制是否启用重叠调度,文档可能需要同步更新。
  • 用户影响:低。无 API 变化,但环境变量 SGLANG_ENABLE_SPEC_V2 的行为有所调整,用户需注意其含义已变为仅控制重叠调度。
  • 系统影响:中。代码量显著减少,架构更简洁,便于后续维护和演进。DFLASH 的代码路径与 EAGLE 统一,降低了特殊处理的需要。
  • 团队影响:积极。减少技术债务,新开发者更容易理解 DFLASH 的实现。
大量代码删除 环境变量语义变化 回归风险

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论