Prhub

#26738 [core] Fix crashes on the `gpu_only` spec_v2 path

原始 PR 作者 hnyls2002 合并时间 2026-05-30 10:12 文件变更 5 提交数 9 评论 3 代码增减 +44 / -13

执行摘要

修复 spec_v2 gpu_only 路径的 None 崩溃与索引越界

修补 #26128 合并后暴露的潜在崩溃。在特定 speculative 配置下,draft_extend_attn_backend 可能为 None,导致 decide_needs_cpu_seq_lens 中访问 b.needs_cpu_seq_lens 时抛出 AttributeError;同理,gpu_only 路径下 seq_lens_sum 和 extend_prefix_lens_cpu 为 None 时 Triton backend 的 init_forward_metadata 会因直接操作这些值而崩溃。

建议阅读本文涉及的 None 安全处理模式,尤其是 getattr 默认值和上界预分配(ub-allocate)的方法,可用于未来类似优化。提交历史清晰,可追溯每个修复步骤。

讨论亮点

无 review 讨论。作者自行合并。

实现拆解

  1. overlap_utils.py:在 decide_needs_cpu_seq_lens 中跳过 None 的 backend 条目,并使用 getattr(b, "needs_cpu_seq_lens", True) 确保未声明该标志的后端保持传统路径。
  2. triton_backend.py:在 init_forward_metadata 的 target_verify 分支中,当 seq_lens_sumNone 时以 bs * max_context_len 为上界分配 kv_indices;在 draft-extend 分支中,当 extend_prefix_lens_cpuNone 时使用相同上界,并当 extend_seq_lens_cpuNone 时回退到 GPU 最大值计算 max_extend_len
  3. eagle_info_v2.py:在 prepare_for_extend_to_fill_draft_kvcache 中,当 gpu_only 为真时,为 forward_batch.extend_seq_lens_cpu 提供常量列表 [num_draft_tokens] * bs,避免后端每迭代读取 CPU 同步。
  4. 测试配套调整:修改 test_basic_sanity_eagle3.py 使用更密集的 1/1/2 参数和 --disable-piecewise-cuda-graph 以强化 GPU-only 路径覆盖,并将准确率测试从 Hellaswag 切换为 GSM8K;同时微调 fwd_occupancy_kit.py 中的阈值参数和 prompt。
文件 模块 状态 重要度
python/sglang/srt/layers/attention/triton_backend.py 注意力后端 modified 6.47
python/sglang/srt/speculative/eagle_info_v2.py 推测解码 modified 5.23
python/sglang/srt/managers/overlap_utils.py 调度器 modified 5.19
test/registered/core/test_basic_sanity_eagle3.py 测试 modified 4.76
python/sglang/test/kits/fwd_occupancy_kit.py 测试工具 modified 3.87

关键符号

decide_needs_cpu_seq_lens init_forward_metadata prepare_for_extend_to_fill_draft_kvcache

关键源码片段

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

核心修复:在 target_verify 和 draft-extend 路径中处理 None 的 seq_lens_sum/extend_prefix_lens_cpu/extend_seq_lens_cpu,避免崩溃。

def init_forward_metadata(self, forward_batch: ForwardBatch):
    if forward_batch.forward_mode.is_target_verify():
        # gpu_only 路径 : seq_lens_sum 可能为 None
        seq_lens_sum = forward_batch.seq_lens_sum
        if seq_lens_sum is None:
            # 上界 = bs * max_context_len(安全,后续 ragged write 只使用实际长度)
            seq_lens_sum = bs * self.max_context_len
        kv_indices = torch.empty(seq_lens_sum, dtype=torch.int64, device=self.device)
        ...
    elif forward_batch.forward_mode.is_draft_extend():
        # gpu_only 路径 : extend_prefix_lens_cpu 可能为 None
        if forward_batch.extend_prefix_lens_cpu is not None:
            kv_indices_len = sum(forward_batch.extend_prefix_lens_cpu)
        else:
            kv_indices_len = bs * self.max_context_len
        kv_indices = torch.empty(kv_indices_len, dtype=torch.int64, device=self.device)
        ...
    # 后续 max_extend_len 计算 : 若 extend_seq_lens_cpu 为 None, 从 GPU 取最大值
    if forward_batch.extend_seq_lens_cpu is not None:
        max_extend_len = max(forward_batch.extend_seq_lens_cpu)
    else:
        max_extend_len = int(forward_batch.extend_seq_lens.max())

评论区精华

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

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

风险与影响

风险较低:改动均附带 None 保护,且通过 getattr 默认 True 保持向后兼容。但若其他 attention backend 也需类似保护则可能遗漏。测试已覆盖基本 EAGLE3 路径,但极端配置仍需观察。UB 预分配(over-allocation)在异常大 batch 时可能浪费少量显存,但实际 usage 安全。

影响使用 speculative decoding v2 且 attention backend 为 Triton 的用户(EAGLE3 默认)。修复后 gpu_only 路径不再崩溃,同时保持与 FlashInfer 等其他后端的兼容性。系统稳定性提升,CI 回归概率降低。

None 值处理 gpu_only 路径 向后兼容性

关联 Issue

#26128 [core] Make spec_v2 `seq_lens_cpu` optional via backend `needs_cpu_seq_lens`; Triton opts out

完整报告

参与讨论