# PR #22128 完整报告

- 仓库：`sgl-project/sglang`
- 标题：Allow piecewise CUDA graph with speculative decoding
- 合并时间：2026-04-17 13:39
- 原文链接：http://prhub.com.cn/sgl-project/sglang/pull/22128

---

# 执行摘要

- 一句话：允许分段 CUDA 图与所有推测解码算法共存，提升推理性能。
- 推荐动作：建议工程师精读 `piecewise_cuda_graph_runner.py` 中的 `can_run` 方法，理解 PCG 与推测解码的路径隔离机制；此 PR 展示了如何通过验证和渐进式修复来移除保守限制，值得学习其设计权衡和测试策略。

# 功能与动机

根据 PR body，PCG 和推测解码操作在独立的前向路径上：PCG 捕获和重放 prefill/extend 图（ForwardMode.EXTEND，spec_info=None），而推测解码的 draft/verify 使用 decode CUDA 图（ForwardMode.TARGET_VERIFY）。原始限制在 #16331 中添加为保守安全措施，但经过 GSM8K 准确率基准测试验证两者兼容，因此移除限制以利用 PCG 的性能优势。

# 实现拆解

1. **移除 server_args 中的推测解码 PCG 禁用**：修改 `python/sglang/srt/server_args.py` 的 `_handle_piecewise_cuda_graph` 方法，删除针对 `self.speculative_algorithm is not None` 的条件（原第 2 个条件），从而允许 PCG 与所有推测解码算法共存。
2. **在 PCG runner 中添加安全保护**：修改 `python/sglang/srt/model_executor/piecewise_cuda_graph_runner.py` 的 `can_run` 方法，新增两个检查：避免 PCG 用于 `ForwardMode.TARGET_VERIFY` 批次（因 spec_info 不同），以及确保批次的 `capture_hidden_mode` 与 runner 的 `capture_hidden_mode` 匹配（防止隐藏状态错误）。
3. **跳过 draft workers 的 PCG 初始化**：修改 `python/sglang/srt/model_executor/model_runner.py` 的 `init_piecewise_cuda_graphs` 方法，添加 `if self.is_draft_worker: return`，因为 draft 模型使用 decode 图而非 PCG。
4. **新增集成测试验证兼容性**：创建 `test/registered/piecewise_cuda_graph/test_pcg_with_speculative_decoding.py`，包含 `TestPCGWithMTP`、`TestPCGWithEAGLE3`、`TestPCGWithSTANDALONE` 等测试类，通过 GSM8K 评估验证准确率和接受长度。
5. **配套调整与 CI 修复**：在提交历史中，多次调整内存设置（如降低 `mem_fraction_static`）和修复 CI 套件名称，确保测试稳定运行。

关键文件：
- `test/registered/piecewise_cuda_graph/test_pcg_with_speculative_decoding.py`（模块 PCG 测试；类别 test；类型 test-coverage；符号 TestPCGWithMTP, TestPCGWithEAGLE3, TestPCGWithSTANDALONE, TestPCGWithNGRAM）: 新增集成测试文件，验证 PCG 与多种推测解码算法（MTP、EAGLE3、STANDALONE）的兼容性，确保准确率和性能。
- `python/sglang/srt/server_args.py`（模块 服务参数；类别 source；类型 core-logic；符号 _handle_piecewise_cuda_graph）: 核心配置入口，移除了对推测解码算法启用 PCG 的全局禁用，是变更的主要开关。
- `python/sglang/srt/model_executor/piecewise_cuda_graph_runner.py`（模块 PCG 运行器；类别 source；类型 data-contract；符号 can_run）: 在 PCG 运行器中添加安全保护逻辑，确保 PCG 仅用于兼容的批次（非 TARGET_VERIFY 模式且 capture_hidden_mode 匹配）。
- `python/sglang/srt/model_executor/model_runner.py`（模块 模型运行器；类别 source；类型 data-contract；符号 init_piecewise_cuda_graphs）: 修改 PCG 初始化逻辑，跳过 draft workers 的 PCG 初始化，因为 draft 模型使用 decode 图而非 PCG。

关键符号：_handle_piecewise_cuda_graph, can_run, init_piecewise_cuda_graphs, TestPCGWithMTP.setUpClass, TestPCGWithMTP.test_gsm8k


## 关键源码片段

### `test/registered/piecewise_cuda_graph/test_pcg_with_speculative_decoding.py`

新增集成测试文件，验证 PCG 与多种推测解码算法（MTP、EAGLE3、STANDALONE）的兼容性，确保准确率和性能。

```python
class TestPCGWithMTP(unittest.TestCase):
    """Test PCG + MTP (NEXTN) on Qwen3.5-35B-A3B with FP8."""

    @classmethod
    def setUpClass(cls):
        cls.model = "Qwen/Qwen3.5-35B-A3B"
        cls.base_url = DEFAULT_URL_FOR_TEST
        other_args = [
            "--tp", "2",
            "--trust-remote-code",
            "--quantization", "fp8",
            "--mamba-scheduler-strategy", "extra_buffer",
            "--enable-piecewise-cuda-graph",          # 启用PCG
            "--speculative-algorithm", "NEXTN",      # 启用推测解码算法NEXTN
            "--reasoning-parser", "qwen3",           # 确保准确率测试配置正确
        ]
        cls.process = popen_launch_server(
            cls.model, cls.base_url,
            timeout=DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH * 3,
            other_args=other_args,
        )

    @classmethod
    def tearDownClass(cls):
        kill_process_tree(cls.process.pid)

    def test_gsm8k(self):
        args = SimpleNamespace(
            base_url=self.base_url,
            model=self.model,
            eval_name="gsm8k",
            max_tokens=8192,
            num_examples=200,
            num_threads=200,
            thinking_mode="qwen3",
        )
        metrics = run_eval(args)                     # 运行GSM8K评估
        print(metrics)
        self.assertGreater(metrics["score"], 0.75)   # 验证准确率阈值
        server_info = requests.get(self.base_url + "/server_info").json()
        avg_spec_accept_length = server_info["internal_states"][0]["avg_spec_accept_length"]
        print(f"{avg_spec_accept_length=}")
        self.assertGreater(avg_spec_accept_length, 1.5)  # 验证推测解码有效性
```

### `python/sglang/srt/server_args.py`

核心配置入口，移除了对推测解码算法启用 PCG 的全局禁用，是变更的主要开关。

```python
def _handle_piecewise_cuda_graph(self):
    # Skip auto-disable when enforce flag is set (for testing)
    if self.enforce_piecewise_cuda_graph:
        self.disable_piecewise_cuda_graph = False
        return

    # Disable piecewise cuda graph with following conditions:
    # 1. Disable Model Arch
    if self.get_model_config().is_piecewise_cuda_graph_disabled_model:
        self.disable_piecewise_cuda_graph = True
    # 2. DP attention  # 原第3个条件，现重新编号
    if self.enable_dp_attention:
        self.disable_piecewise_cuda_graph = True
    # 3. Torch compile  # 后续条件依次重新编号
    if self.enable_torch_compile:
        self.disable_piecewise_cuda_graph = True
    # ... (其他条件保持不变)
    # 注意：原第2个条件“Speculative decoding”已完全移除，不再禁用PCG
```

# 评论区精华

- **代码可维护性建议**：gemini-code-assist[bot] 建议使用集合（如 `{"NEXTN"}`）检查兼容算法以提高可扩展性，但最终未被采纳，因为 PCG 与所有推测解码算法兼容，无需特殊检查。
- **实现简洁性**：Oasis-Git 建议直接移除 `server_args.py` 中的 case 2 并更新后续标签，作者 narutolhy 接受该建议，使代码更简洁。
- **性能验证讨论**：在 Issue 评论中，用户 cs-cat 报告性能下降，但后续确认为其他问题导致；作者强调 PCG 需要充分预热，并最终通过基准测试确认性能提升。

 - 使用集合检查兼容算法以提高可维护性 (design): 未采纳该建议，因为验证表明 PCG 与所有推测解码算法兼容，无需特殊检查，直接移除限制更简洁。
 - 直接移除 case 2 并更新标签 (style): 作者 narutolhy 接受建议，在最终提交中移除了该条件并调整了注释编号。

# 风险与影响

- 风险：- **回归风险**：如果 PCG 图捕获与某些推测解码配置（如不同 hidden mode）交互不当，可能导致输出错误或准确率下降，尤其在边缘情况下。具体在 `piecewise_cuda_graph_runner.py` 的 `can_run` 条件中，依赖 `forward_mode` 和 `capture_hidden_mode` 检查，若遗漏可能引发问题。
- **性能风险**：PCG 图捕获增加 GPU 内存使用，在提交历史中需调整 `mem_fraction_static` 避免 OOM，可能影响高负载下的稳定性。
- **兼容性风险**：draft 模型的 prefill 目前不支持 PCG（如评论中提及），这限制了某些推测解码场景的优化潜力；未来扩展时需额外工作。
- **测试覆盖风险**：新增测试虽覆盖主流算法，但未覆盖所有变体（如 NGRAM 因编译错误未测试），可能存在未发现的不兼容性。
- 影响：- **用户影响**：用户现在可同时启用 PCG 和推测解码，获得叠加性能提升，如 TTFT 减少 42%（从 253ms 到 147ms），且无需手动配置兼容性。
- **系统影响**：提升 GPU 利用率和推理吞吐量，但可能增加内存开销，需监控 PCG 图捕获对内存池的影响。
- **团队影响**：简化了部署配置，减少了因互斥限制导致的调优复杂度，为未来性能优化提供了模板。
- 风险标记：核心路径变更 , 测试覆盖有限 , 内存使用增加

# 关联脉络

- PR #16331 无（需从上下文推断）: 此 PR 添加了原始的 PCG 与推测解码互斥限制，本 PR 移除了该限制，是直接关联的前序变更。
- PR #10062 无（需从上下文推断）: 原始的 PCG 实现 PR，本 PR 基于其设计扩展了兼容性。
- PR #22406 [sgl] improve accuracy of additional page requirement during spec decode: 同为推测解码相关的性能优化 PR，涉及内存调度，可共同参考以理解推测解码的演进。