Skip to content

[Perf] Qwen3.8: Allow breakable prefill CUDA graphs for image batches - #46329

Open
eppaneamd wants to merge 7 commits into
sgl-project:mainfrom
eppaneamd:qwen38-mm-breakable-prefill-graphs
Open

eppaneamd wants to merge 7 commits into
sgl-project:mainfrom
eppaneamd:qwen38-mm-breakable-prefill-graphs

Conversation

@eppaneamd

@eppaneamd eppaneamd commented Oct 24, 2026 •

Copy link
Copy Markdown

Motivation

With --cuda-graph-backend-prefill breakable, the prefill runner refuses graph replay for every Qwen3.8-Flash-Next (Qwen4-Exp) batch that carries multimodal inputs, so image prefills always run eager.

Scope: this enables graph replay for image prefills in deployments without speculative decoding. With speculative decoding enabled (any algorithm, including NGRAM), behavior is unchanged.

This PR keys the refusal on whether speculative decoding is enabled, a launch-time property identical on every rank. Without speculative decoding, image batches become replay-eligible; with any speculative algorithm (including NGRAM), behavior is unchanged. No new flag, environment variable or API.

Modifications

  • python/sglang/srt/model_executor/runner/prefill_cuda_graph_runner.py (+8/-4): replace _qwen_bcg_mtp_draft (no other reader) with _qwen_bcg_multimodal_eager = qwen_bcg and model_runner.spec_algorithm.is_speculative(), and gate multimodal batches on it.
  • test/registered/unit/model_executor/test_prefill_cuda_graph_runner.py: flag derivation for no speculative decoding, EAGLE and NGRAM.
  • test/registered/unit/managers/scheduler_components/test_dp_attn.py: DP vote with one image rank and one text rank, refusal on and off.
  • test/registered/unit/configs/test_multimodal_piecewise_cuda_graph.py: forward-time gate for image and text batches.
  • test/registered/unit/model_executor/test_prefill_cuda_graph_runner_helpers.py: drop fixture lines for the removed flag.

The breakable prefill backend is the default on CUDA, so there this applies by default to Qwen3.8-Flash-Next without speculative decoding. On ROCm it is opt-in.

Accuracy Tests

Mean probability of the gold answer over 805 MMMU validation items, unpatched main vs this commit with breakable prefill graphs, 2 server launches per arm:

  • explicit bucket list: +0.37 pp (95% CI -0.22 to +0.95);
  • default bucket list: -0.10 pp (95% CI -0.66 to +0.47).

A greedy-output probe (16 requests) completed in every server launch; its outputs are not used as an accuracy comparison.

Speed Tests and Profiling

Setup

  • Image: lmsysorg/sglang-rocm:v0.5.22-rocm724-mi35x-20261024, SGLang tree 13b7ef75c5 with this commit applied (prefill_cuda_graph_runner.py is unchanged between that tree and this PR's base), torch 2.11.0+rocm7.2.
  • Hardware: 2x MI355X (gfx950).
  • Model: amd/Qwen3.8-Flash-Next-Quark-MXFP4 (revision b2091c38), MTP off.
  • Server (all arms): TP2 + EP2, --attention-backend aiter --linear-attn-backend triton --context-length 8192 --max-running-requests 128 --disable-radix-cache --mm-enable-dp-encoder.
  • Baseline: unpatched main, no prefill CUDA graph (the ROCm default).
  • Patched, explicit list: this commit plus
    --cuda-graph-backend-prefill breakable
    --cuda-graph-bs-prefill 256 512 768 1024 1280 1408 2560 2752 3840 4096 5376
    
    The bucket list was derived from this workload's prefill sizes.
  • Patched, default list: this commit plus --cuda-graph-backend-prefill breakable.
  • Benchmark: sglang.bench_serving, one 512x512 image per request, 1024 input / 128 output tokens (range ratio 0.8), prompts = 10x concurrency; 2 server launches per arm (order A, explicit, default, default, explicit, A), 2 measured runs per row after a warm-up pass.
Concurrency Output tok/s base Explicit list Change Default list Change TTFT (explicit / default) TPOT (explicit / default)
4 250.0 273.1 +9.2% 273.6 +9.4% -31.6% / -30.8% -6.3% / -6.6%
16 579.5 694.7 +19.9% 693.0 +19.6% -28.0% / -29.4% -15.8% / -15.4%
32 822.2 1008.5 +22.7% 1010.6 +22.9% -23.4% / -28.7% -18.3% / -17.9%
64 1066.0 1280.3 +20.1% 1284.2 +20.5% -18.3% / -25.3% -16.8% / -16.4%
128 1382.9 1570.2 +13.6% 1590.8 +15.0% -10.1% / -20.4% -12.3% / -12.5%

Mean change over concurrency 16-64: +20.9% (explicit), +21.0% (default), SE 0.55 pp. SE: standard error of the change in percentage points, estimated from baseline-vs-baseline (A/A) runs with the same procedure; a change beyond about 2 SE is outside run-to-run noise.

Checklist

  • Format your code: ruff check and ruff format --check clean on the five files.
  • Add unit tests: the four suites above, 65 passed (50 subtests) on CPU (torch 2.14.1+cpu).
  • Update documentation: not needed (no new flag or API).
  • Provide accuracy and speed benchmark results: above, measured on this commit.
  • Follow the SGLang code style guidance.

Notes


CI States

Latest PR Test (Base): ❌ Run #41000722234
Latest PR Test (Extra): ❌ Run #41000722037
Latest PR Test (AMD ROCm 10): ✅ Run #41000722040

The breakable prefill graph runner for Qwen3.8-Flash-Next (Qwen4-Exp:
Qwen4ExpForConditionalGeneration and its MTP draft,
Qwen4ExpForCausalLMMTP) refuses every batch that carries multimodal
inputs, on the target and on the MTP draft alike, so image requests
always run eager prefill even when the breakable prefill backend is
enabled.

Key the refusal on whether speculative decoding is enabled instead of on
the worker's role. Without speculative decoding there is no draft runner
and multimodal batches are replay-eligible: the eager outer forward
builds the merged text and vision embeddings and replay copies them into
the captured body's static input_embeds slot, the same path text batches
already take. With speculative decoding enabled the behavior is
unchanged and the target keeps refusing them. Dropping only the target's
refusal would let DP ranks disagree on replay versus eager for the draft
runner: its verdict is the target's schedule-time vote, which consults
only the target runner, combined with the draft's own rank-local
refusal, so a rank holding images would run its draft eager while the
other ranks replay theirs. That is the disagreement can_replay_locally
exists to prevent. The predicate is the speculative algorithm rather
than the presence of a draft model, so an algorithm without one (NGRAM)
also keeps refusing.

The breakable prefill backend is the default on CUDA and opt-in
elsewhere, so on CUDA this applies by default to Qwen3.8-Flash-Next
without speculative decoding.

The private _qwen_bcg_mtp_draft flag had no other reader and is replaced
by _qwen_bcg_multimodal_eager.

Tests cover the flag across backend, architecture, worker role and
speculative algorithm (including NGRAM), the DP vote with one image rank
and one text rank with the refusal on and off, and the forward-time gate
for image and text batches. Fixtures that set the removed flag are
updated.

Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: Rony Leppänen <rony.leppanen@amd.com>
Keep only the rows that exercise the speculative-algorithm predicate
(none, EAGLE, NGRAM) and drop the rows and the hc_sidechannel assertion
that re-tested unchanged code.

Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: Rony Leppänen <rony.leppanen@amd.com>

@rkarhila-amd rkarhila-amd left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any issues with this code.
Go ahead and open for code owner review.

@eppaneamd
eppaneamd marked this pull request as ready for review October 27, 2026 21:56
@github-actions

This comment has been minimized.

@HaiShaw HaiShaw added run-ci CI: run the baseline test suite on this PR and removed run-ci CI: run the baseline test suite on this PR labels Oct 29, 2026
@eppaneamd

Copy link
Copy Markdown
Author

Required checks failed on ae416e9; this PR cannot be merged until they pass.

Lint (run) Run pre-commit run --all-files locally and push the result. The run log shows any other failing step.

Passing.

@HaiShaw HaiShaw added the run-ci CI: run the baseline test suite on this PR label Oct 30, 2026
@HaiShaw HaiShaw added the run-ci-nv-extra CI: NVIDIA/shared Extra suite (requires run-ci) label Oct 30, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Multi-modal multi-modal language model run-ci CI: run the baseline test suite on this PR run-ci-nv-extra CI: NVIDIA/shared Extra suite (requires run-ci)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants