Prhub

#30584 Fix diffusion BCG lifetime and add Z-Image-Turbo CI

原始 PR 作者 BBuf 合并时间 2026-07-10 21:20 文件变更 8 提交数 9 评论 12 代码增减 +245 / -12

执行摘要

修复 diffusion BCG 输出生命周期并新增 CI 测试

PR #27436 启用 diffusion BCG 后,Z-Image-Turbo 在禁用 torch.compile/offload 时出现 segfault,根因是 eager break 输出被弱引用,replay 时复制到已释放 CUDA 地址。此 PR 保持强引用修复该问题,并添加 CI 测试预防回归。

建议仔细阅读 breakable_cuda_graph.py 中的 eager_on_graph 实现,理解桥接缓冲区生命周期管理的重要性。新增的 test_eager_output_is_held_strongly_for_replay_bridge 是一个优秀的回归测试示范,值得在其他类似场景中推广。CI 的 standalone 组织方式也值得参考。

讨论亮点

唯一的 review 讨论围绕测试组织:mickqian 建议将 bcg-diffusion 测试加入 STANDALONE_FILES 以避免 fallback-estimate 噪声。BBuf 已采纳并在 commit 684b896ef0 中实现,同时保留了 run_suite.py --suite bcg-diffusion 的兼容性。

实现拆解

  1. 修复桥接缓冲区生命周期:在 breakable_cuda_graph.pyeager_on_graph 装饰器中,将 captured_output 从弱引用 (_weak_ref_if_tensor(output)) 改为强引用 (output),确保 replay 时 bridge buffer 地址有效。
  2. 增强弱引用和复制函数_weak_ref_if_tensor_copy_output 新增对 tuple/list 的递归支持,使这些工具能正确处理更复杂的输出结构。
  3. 修复 Qwen attention mask 元数据:在 qwen_image.py 中添加 _attn_mask_meta_local_pad 辅助函数,正确区分 DynamicVarlenMaskMeta 类型与普通 dict,避免 BCG 场景下 SP padding 计算错误。
  4. 新增回归测试:在 test_breakable_cuda_graph.py 中添加 test_eager_output_is_held_strongly_for_replay_bridge,直接验证 eager 输出被强引用。在 test_diffusion_bcg_padding.py 中添加 test_qwen_dynamic_varlen_meta_is_not_tail_pad_metatest_image_generation_models_are_registered_as_bcg_supported
  5. 端到端 CI 测试:新增 test_diffusion_bcg_zimage_turbo.py,通过 sglang generate 命令真实运行 Z-Image-Turbo BCG 生成,检查 native backend、BCG capture 成功、像素生成成功。并在 GitHub Actions 中添加独立的 bcg-diffusion job(H100),配置为 STANDALONE。
文件 模块 状态 重要度
python/sglang/srt/model_executor/runner_backend_utils/breakable_cuda_graph/breakable_cuda_graph.py 调度器 modified 6.98
python/sglang/multimodal_gen/test/single_test_file/test_diffusion_bcg_zimage_turbo.py 测试 added 6.99
python/sglang/multimodal_gen/runtime/models/dits/qwen_image.py 模型 modified 6.63
python/sglang/multimodal_gen/test/unit/test_diffusion_bcg_padding.py 测试 modified 5.79
test/registered/cuda_graph/breakable/test_breakable_cuda_graph.py 测试 modified 5.75
.github/workflows/pr-test-multimodal-gen.yml 部署 modified 4.73

关键符号

_weak_ref_if_tensor _copy_output eager_on_graph _attn_mask_meta_local_pad test_zimage_turbo_true_bcg_generate test_eager_output_is_held_strongly_for_replay_bridge test_qwen_dynamic_varlen_meta_is_not_tail_pad_meta test_image_generation_models_are_registered_as_bcg_supported

关键源码片段

python/sglang/srt/model_executor/runner_backend_utils/breakable_cuda_graph/breakable_cuda_graph.py core-logic

核心修复文件:调整 eager 输出引用策略(弱→强),增强 _weak_ref_if_tensor 和 _copy_output 对 tuple/list 的支持。

def _weak_ref_if_tensor(x):
    """Return a weak-ref tensor view for tensors; recursively handle tuples/lists."""
    if torch.is_tensor(x):
        from sglang.srt.compilation.weak_ref_tensor import weak_ref_tensors
        return weak_ref_tensors(x)
    # 新增递归支持 tuple/list,确保中间层输出也能正确弱引用
    if isinstance(x, tuple):
        return tuple(_weak_ref_if_tensor(e) for e in x)
    if isinstance(x, list):
        return [_weak_ref_if_tensor(e) for e in x]
    return x
​
​
def eager_on_graph(enable: bool):
    def decorator(inner: Callable):
        def wrapper(*args, **kwargs):
            ...
            output = inner(*args, **kwargs)
            # === 修复关键:将弱引用改为强引用 ===
            # 之前的代码使用 captured_output = _weak_ref_if_tensor(output),
            # 导致 bridge buffer 在 replay 时指向已释放内存。
            # 现在直接保留强引用,确保下一个 segment 的输入地址有效。
            captured_output = output
​
            def replay_fn():
                new_out = captured_inner(*captured_args, **captured_kwargs)
                return _copy_output(captured_output, new_out)
​
            capture.cuda_graph._break_fns.append(replay_fn)
            ...
        return wrapper
    return decorator
python/sglang/multimodal_gen/test/single_test_file/test_diffusion_bcg_zimage_turbo.py test-coverage

新增端到端 BCG 测试文件,验证真实 Z-Image-Turbo 模型在 BCG 模式下完整生成流水线。

class TestDiffusionBCGZImageTurbo(CustomTestCase):
    def test_zimage_turbo_true_bcg_generate(self):
        # 构建完整 sglang generate 命令,启用 BCG 并禁用 torch.compile/offload
        cmd = [
            "sglang", "generate",
            "--backend", "sglang",
            "--model-path", DEFAULT_SMALL_MODEL_NAME_FOR_TEST,
            "--prompt", "...",
            "--width", "512", "--height", "512",
            "--enable-breakable-cuda-graph",
            "--bcg-text-buckets", "128",
            "--enable-torch-compile", "false",
            "--dit-layerwise-offload", "false",
            "--dit-cpu-offload", "false",
            "--perf-dump-path", str(perf_path),
        ]
        result = subprocess.run(cmd, ..., timeout=300)
        # 断言:成功退出、使用 native 后端、BCG capture 成功、像素生成成功
        self.assertEqual(result.returncode, 0)
        self.assertNotIn("Falling back to diffusers backend", result.stdout)
        self.assertNotIn("[Diffusion BCG] capture failed", result.stdout)
        self.assertIn("[Diffusion BCG] captured", result.stdout)
        self.assertIn("Pixel data generated successfully", result.stdout)
        # 验证 perf dump 包含 DenoisingStage
        perf = json.loads(perf_path.read_text())
        self.assertIn("DenoisingStage", {s.get("name") for s in perf.get("steps", [])})

评论区精华

bcg-diffusion 测试组织方式 测试

mickqian 建议将 bcg-diffusion 测试加入 STANDALONE_FILES,而非 parametrized 组。

结论:BBuf 采纳并修改:移动 bcg-diffusion 到 STANDALONE_FILES,保留空 parametrized 入口以维持 run_suite.py 兼容。 · 已解决

风险与影响

  • 强引用内存泄漏风险:保持强引用虽修复了段错误,但若桥接缓冲区在 capture 后不再使用,强引用会阻止其释放。但 bridge buffer 本质是下一个 segment 的输入,在完整 graph replay 完毕前必须存活,因此当前改动合理。
  • 新 CI job 稳定性bcg-diffusion job 运行真实 Z-Image-Turbo 生成,可能因 GPU 资源、环境变量等不稳定,但作为 standalone 测试可控制失败影响。
  • Qwen 辅助函数影响范围_attn_mask_meta_local_pad 替换了内联表达式,仅影响 SP 场景下的 padding 计算,功能等价,风险低。
  • 用户影响:修复了 Z-Image-Turbo 用户启用 BCG 时的崩溃问题;Qwen 模型 BCG 路径也能正确处理掩码元数据。
  • 系统影响:CI 新增 standalone job,增加总测试时长但隔离失败域。
  • 团队影响:未来修改 BCG 相关代码时,必须确保 eager 输出桥接缓冲区的引用策略正确,新增的回归测试提供了防护。
核心路径变更 新 CI 依赖 GPU 环境

关联 Issue

#27436 [diffusion] Enable breakable CUDA graph (BCG) for diffusion DiTs

完整报告

参与讨论