[diffusion] Enable breakable CUDA graph (BCG) for diffusion DiTs - #27436
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a model-agnostic Breakable CUDA Graph (BCG) runner for diffusion DiT transformers, moving BCG primitives to a shared package (sglang.srt.breakable_cuda_graph) to be utilized by both LLM and diffusion runtimes. The feedback highlights three important improvements: addressing a critical stream synchronization race condition during graph replay to prevent reading or cloning stale data, adding support for dictionary and ModelOutput types in _clone_output to avoid overwriting static output buffers, and extending _weak_ref_if_tensor to support dictionaries so that intermediate tensors can be properly reclaimed by the shared mempool.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
b9e2bef to
51b03ff
Compare
|
perhaps try ideogram-4? |
6b0e87c to
59ace4f
Compare
There was a problem hiding this comment.
Some high level comments:
- the
diffusion runnershould inherent frombase runnerand implementcapturereplayapi. Thecaptureshould happen during the class init. As for the resolution, maybe we can set a server args to let the user enter it when engine is up instead of runtime capture. - instead of directly injection on the
transformer.forward(), we can implement an eager runner which only doesreplay() -> transformer.forward()with other functions directly pass - maybe we can put the model specific helper functions into their own specific stages file and gather other helper functions in a utils.py
Oasis-Git
left a comment
There was a problem hiding this comment.
The code structure is clean now. However I think the introduction of real ref of captured output will increase the memory usage thus it may be unacceptable.
ff06e95 to
25d2266
Compare
…uda-graph # Conflicts: # test/registered/unit/test_legacy_global_ratchet.py
The srt breakable CUDA graph core returns byte-identical to its state before sgl-project#27436 (back at srt/model_executor/runner_backend_utils/breakable_cuda_graph, dedup support and capture-failure logging restored, top-level srt/breakable_cuda_graph package removed). The diffusion BCG feature is kept and adapted to the pre-PR core: - replay-token machinery moves to multimodal_gen/runtime/breakable_cuda_graph/replay_token.py; the mm runner stamps a token around each graph.replay() - tuple-returning attention forwards (UlyssesAttention, used by Z-Image) are boxed into an attribute object for the break point, since the pre-PR _copy_output copies objects-with-tensor-attrs in place but ignores tuples - layer.py imports repoint accordingly Verified: core files byte-identical to ead1e49; all consumers import; GPU capture/replay of a boxed tuple break point produces fresh outputs on every replay; 16/16 BCG unit tests pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-project#27436) Co-authored-by: BBuf <xiaoyu.zhang@radixark.net> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: BBuf <bbuf@sglang.local>
Summary
Add Breakable CUDA Graph for selected diffusion DiT forward passes: graph-safe spans are captured, while dynamic attention stays eager.
This PR currently enables diffusion BCG only for:
Qwen/Qwen-ImageQwen/Qwen-Image-2512Tongyi-MAI/Z-ImageTongyi-MAI/Z-Image-Turbozai-org/GLM-ImageComfy-Org/Ideogram-4Other diffusion models are intentionally left unchanged from
main.Why it work?
Qwen-Image @512x512
main:
pr:
Torch profiler
B200 profiler evidence for the supported model groups with captured traces. Eager and BCG are shown side by side for each model so the profiler differences are easier to compare. Profiler timings include profiler overhead and are separate from the benchmark table.
Qwen/Qwen-Image @ 512x512
Profiler commands
Eager profiler command:
BCG profiler command:
Tongyi-MAI/Z-Image @ 256x256
Profiler commands
Eager profiler command:
BCG profiler command:
Tongyi-MAI/Z-Image-Turbo @ 512x512
Profiler commands
Eager profiler command:
BCG profiler command:
zai-org/GLM-Image @ 512x512
Profiler commands
Eager profiler command:
BCG profiler command:
Comfy-Org/Ideogram-4 @ 512x512
Profiler commands
Eager profiler command:
BCG profiler command:
Correctness: eager vs BCG serving req2
The BCG images below are generated by serving with
--enable-breakable-cuda-graph: after server warmup, I sent req1 with the short prompt and then req2 with the benchmark long prompt. The eager images use the same serving flow, same req2 prompt, same resolution, andseed=42, with only BCG disabled. Logs were checked to ensure the native SGLang backend was used and no diffusers fallback occurred.Req1 prompt:
A small red cube on a white tableReq2 prompt:
A detailed cinematic scene of a glass observatory above a quiet lake at sunrise, with soft mist, warm reflections, and crisp architectural detailQwen/Qwen-Image @ 512x512
Qwen/Qwen-Image-2512 @ 512x512
Tongyi-MAI/Z-Image @ 256x256
Tongyi-MAI/Z-Image-Turbo @ 512x512
zai-org/GLM-Image @ 512x512
GLM-Image has a sampled AR prior before the denoise span (
do_sample=True). The older serving req2 pair below was generated before this PR fixed AR prior seeding, so the final images can differ because the two runs sampled different prior tokens. The exact AR prior/RNG from that older over-exposed BCG response was not saved, so the same-prior check below replays a newly saved BCG prior through eager. The BCG-covered denoise replay itself was checked separately with the same prior/input and matched eager exactly.Same-prior replay check: BCG serving sampled and saved the GLM AR prior; eager then loaded that exact prior with BCG disabled. The two output images are an exact pixel match (
diff mean=[0,0,0],rms=[0,0,0], all-channel extrema(0,0)).Comfy-Org/Ideogram-4 @ 512x512
Eager and BCG req2 are an exact pixel match (
diff mean=[0,0,0],rms=[0,0,0], all-channel extrema(0,0),diff_bbox=None).Supported Models
B200 benchmarks with the native SGLang backend. Both BCG and eager runs use explicit warmup (
--warmup --warmup-resolutions ...). BCG uses the PR default text buckets64 128 256 512 1024(--bcg-text-bucketsomitted); eager removes only--enable-breakable-cuda-graph. The latency columns report non-profiled runs after warmup. Commands are folded below the table to keep the support matrix readable.Qwen/Qwen-Image @ 512x5125 -> 5 -> 5Qwen/Qwen-Image-2512 @ 512x5125 -> 5 -> 5Tongyi-MAI/Z-Image @ 256x2565 -> 5 -> 5Tongyi-MAI/Z-Image-Turbo @ 512x5125 -> 5 -> 5zai-org/GLM-Image @ 512x5121 -> 1 -> 1Comfy-Org/Ideogram-4 @ 512x5126 -> 6 -> 6GLM-Image total latency is dominated by the sampled AR prior/text path; the measured BCG benefit is on the capture-stable denoise span. This PR also seeds the GLM AR prior from the request seed so eager/BCG correctness comparisons are reproducible.
Benchmark commands
Qwen/Qwen-Image @ 512x512
Eager benchmark command:
BCG benchmark command:
Qwen/Qwen-Image-2512 @ 512x512
Eager benchmark command:
BCG benchmark command:
Tongyi-MAI/Z-Image @ 256x256
Eager benchmark command:
BCG benchmark command:
Tongyi-MAI/Z-Image-Turbo @ 512x512
Eager benchmark command:
BCG benchmark command:
zai-org/GLM-Image @ 512x512
Eager benchmark command:
BCG benchmark command:
Comfy-Org/Ideogram-4 @ 512x512
Eager benchmark command:
BCG benchmark command:
Notes
6signatures,210total graph segments); req1/req2 reused the warmed captures without new captures.Falling back to diffusers backend,Using diffusers backend,Loaded diffusers pipeline,capture failed,Traceback,RuntimeError, orCUDA errorappeared in the benchmark/profile/serving logs.--warmup-resolutionsbecause diffusion graphs are resolution-specific.--enable-torch-compileand Cache-DiT.CI States
Latest PR Test (Base): ⏳ Run #28916751820
Latest PR Test (Extra): ⏳ Run #28916751679