Skip to content

[Feat] [AMD] dsv4: Support interleave prefill CP with breakable CUDA graphs - #43819

Open
AMD-yanfeiwang wants to merge 3 commits into
sgl-project:mainfrom
AMD-yanfeiwang:amd/dsv4-rocm-prefill-cp-bcg
Open

AMD-yanfeiwang wants to merge 3 commits into
sgl-project:mainfrom
AMD-yanfeiwang:amd/dsv4-rocm-prefill-cp-bcg

Conversation

@AMD-yanfeiwang

@AMD-yanfeiwang AMD-yanfeiwang commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

#38603 added interleave prefill context parallelism (CP) with breakable CUDA graphs (BCG) for DeepSeek V4, but only in the CUDA backend. On ROCm, prefill graph capture fails under CP (deepseek_v4_backend_hip_radix.py: assert cp_metadata is not None), so deployments must choose between CP and BCG; the MI355X InferenceX DSv4 recipe, for example, uses BCG at conc 1/4 and CP at conc 16.

#44022 later disabled DSv4 CP + BCG because the capture path did not shard token IDs consistently with hidden states for hash routing. This PR fixes that in the shared CP BCG path and enables DSv4 CP + BCG on ROCm, for DeepSeek-V4-Pro and DeepSeek-V4-Flash. DeepSeek-V4.1-Flash is in #44013.

Modifications

Usage

SGLANG_HACK_FLASHMLA_BACKEND=unified_kv_triton \
python -m sglang.launch_server ... --attention-backend dsv4 \
  --enable-prefill-cp --cp-strategy interleave --enable-cp-decode-attn-tp

On ROCm, BCG is selected automatically for this configuration (log: Enabling breakable prefill CUDA graph for supported prefill CP). Pass --cuda-graph-backend-prefill disabled to run eager CP instead.

Test setup

  • Hardware: 8x MI355X, TP8, DeepSeek-V4-Pro.
  • Image: lmsysorg/sglang-rocm:v0.5.21-rocm724-mi35x-20261011. Stock is main 3b72b70ef4 and this PR is 0ccacc8055, both overlaid on the same image.
  • Server: InferenceX MI355X DSv4 recipe (benchmarks/single_node/srt-slurm-recipes/dsv4/sglang/mi355x-fp4-mtp/agentic.yaml):
    • KV cache: FP8 unified KV in every arm (SGLANG_HACK_FLASHMLA_BACKEND=unified_kv_triton SGLANG_DSV4_UNIFIED_KV_FP8=1 --kv-cache-dtype fp8_e4m3, two-pool FP8 nope + bf16 rope on gfx950). Under CP the packed FP8 KV is all-gathered before the store (_materialize_cp_unified_fp8_kv), and prefill attention runs prefill_fp8_2buff.
    • Other flags: FP4 indexer, --chunked-prefill-size 16384, --mem-fraction-static 0.86.
    • --max-running-requests and --cuda-graph-max-bs-decode are 2x conc, and the server is restarted per conc.
    • SGLANG_DSV4_GROUPED_ASM=1 at conc 16 for all arms.
    • DSpark is off because the base V4-Pro checkpoint has no draft head.
  • Arms:
Arm Code Prefill flags
BCG stock --cuda-graph-backend-prefill breakable
CP stock --enable-prefill-cp --cp-strategy interleave --enable-cp-decode-attn-tp
CP+BCG this PR CP flags + --cuda-graph-backend-prefill breakable
  • Client: InferenceX fixed-seq client (infx.bench_serving.benchmark_serving): random dataset, --random-range-ratio 0.8, num-prompts 10x conc, 2x conc warmups, request rate inf, --ignore-eos.

Accuracy Tests

GSM8K: InferenceX eval (lm-eval gsm8k, 5-shot, 1319 questions) on the conc-16 server.

BCG CP CP+BCG (this PR)
GSM8K strict-match 0.9666 0.9674 0.9674

All three are within one stderr (±0.005). Greedy outputs of CP and CP+BCG agree on prompts from 2 to 30k tokens; the only divergences are at near-tie tokens.

Speed Tests (DeepSeek-V4-Pro, TP8)

TTFT is mean / P50 / P90 in ms. TPOT is the mean in ms. Throughput is output tok/s.

ISL 1024 / OSL 1024

conc BCG TTFT CP TTFT CP+BCG TTFT TPOT (BCG / CP / CP+BCG) output tok/s (BCG / CP / CP+BCG)
1 68 / 71 / 73 210 / 199 / 226 67 / 68 / 71 10.68 / 11.95 / 10.86 93 / 82 / 92
4 111 / 80 / 220 252 / 202 / 341 105 / 75 / 216 11.64 / 12.02 / 11.65 329 / 315 / 329
16 250 / 139 / 762 356 / 250 / 854 194 / 104 / 413 15.46 / 16.87 / 14.70 991 / 907 / 1047

ISL 8192 / OSL 1024

conc BCG TTFT CP TTFT CP+BCG TTFT TPOT (BCG / CP / CP+BCG) output tok/s (BCG / CP / CP+BCG)
1 300 / 321 / 341 248 / 242 / 286 216 / 231 / 243 10.88 / 11.62 / 11.01 89 / 84 / 89
4 417 / 342 / 526 322 / 244 / 419 302 / 240 / 425 12.49 / 12.26 / 12.27 301 / 310 / 310
16 808 / 408 / 1197 851 / 352 / 1183 599 / 303 / 895 18.16 / 17.19 / 16.76 817 / 854 / 891

ISL 8192 / OSL 2 (prefill-bound)

conc BCG TTFT CP TTFT CP+BCG TTFT total tok/s per GPU (BCG / CP / CP+BCG)
1 272 / 298 / 308 226 / 222 / 239 199 / 211 / 220 3360 / 4039 / 4558
4 1051 / 1085 / 1131 720 / 728 / 809 701 / 719 / 754 3344 / 4868 / 4993
16 4113 / 4209 / 4791 2724 / 2787 / 3165 2709 / 2773 / 3142 3412 / 5145 / 5168

Summary

  • CP+BCG matches or beats the better of BCG and CP at every point on mean, P50 and P90 TTFT, except 8k/1k conc 4 P90 (425 vs 419 ms for CP).
    • Short prompts keep BCG's launch-free prefill: 1k/1k conc 1 is 67 ms, vs 210 ms for CP.
    • Long prompts keep CP's prefill split: 8k/1k conc 1 is 216 ms, vs 300 ms for BCG.
    • The largest mean gains come at 8k/1k conc 16 (−26% vs BCG, −30% vs CP) and 8k/1k conc 1 (−13% vs CP).
    • 8k/1k conc 4 is on par with CP: mean 302 vs 322 ms, P50 240 vs 244 ms, P90 425 vs 419 ms.
  • Decode: TPOT and output throughput are within 2% of the best baseline. At conc 16, CP+BCG gives the best output throughput (1047 and 891 tok/s).
  • Tail: the P99 TTFT at 8k/1k conc 16 (BCG 5.4 s, CP 6.5 s, CP+BCG 4.1 s) is queueing of 16 concurrent 8k prompts behind the 16k prefill chunk, not a stall. CP+BCG server logs show no step gap ≥ 1.5 s. The stock arms had 2–5 s step gaps at some points, which raise their means there.
  • First large prefill: with the prefill-tail fix, the first large CP+BCG prefill on a fresh server shows no stall. Before the fix, the first 4x8k batch took 9.0–9.3 s, with RCCL communicator init in the window; after the fix it takes 0.7–1.0 s, with no RCCL init.

AgentX (DeepSeek-V4-Pro-0813, TP8, DSpark gamma 6)

Setup

  • InferenceX AgentX client (srt_agentic.sh, traces semianalysis_cc_traces_weka_062126, 900 s per point, all points submission_valid), using the recipe's own conc 1/4/16 points.
  • Recipe = the InferenceX recipe as-is, on stock: BCG at conc 1/4, CP at conc 16.
  • CP+BCG = this PR at every conc, plus SGLANG_ENABLE_WAR_BARRIER=1. DSpark with BCG needs the WAR barrier for correctness on ROCm; the recipe's BCG arms run without it.

Results (TTFT and ITL: mean / P50 / P90 ms; throughput: tok/s per GPU)

conc arm TTFT ITL output tok/s/GPU DSpark accept
16 Recipe (CP) 1118 / 796 / 2052 10.51 / 10.07 / 15.12 77.8 3.66
16 CP+BCG 804 / 446 / 1521 9.96 / 9.72 / 12.98 77.6 3.70
4 Recipe (BCG, no barrier) 457 / 239 / 1005 5.60 / 5.30 / 7.26 21.3 3.73
4 BCG + barrier 441 / 241 / 986 6.48 / 6.32 / 8.18 19.9 3.63
4 CP+BCG (barrier) 373 / 219 / 819 6.52 / 6.35 / 8.07 21.2 3.80
4 CP+BCG, no barrier 367 / 205 / 943 5.57 / 5.44 / 6.72 21.7 3.56
1 Recipe (BCG, no barrier) 671 / 503 / 1017 5.21 / 5.15 / 6.27 11.4 3.44
1 CP+BCG (barrier) 542 / 435 / 710 6.51 / 6.38 / 8.03 10.6 3.39

Takeaways

  • conc 16: CP+BCG beats the recipe's CP on every TTFT stat (mean −28%, P50 −44%, P90 −26%) and on ITL (mean −5.2%, P90 −14%), with the same throughput.
  • conc 1/4: CP+BCG lowers mean and P90 TTFT by 18–30% vs the recipe's BCG.
    • ITL is about 20% higher, but that comes from the WAR barrier, not from CP+BCG.
    • With the barrier in both arms (conc 4), CP+BCG ITL is within 2% of BCG.
    • Without the barrier in both arms, CP+BCG ITL matches the recipe (5.57 vs 5.60 ms).
    • Making the HIP WAR barrier cheaper is a separate follow-up.
  • No stall: server logs show no busy-time gap ≥ 1.5 s in any CP+BCG point. That covers about 103 large prefill batches at conc 16.

DeepSeek-V4-Flash, TP4

Setup

  • No InferenceX V4-Flash MI355X recipe exists, so this uses the DSv4 MI355X recipe at TP4. Same arms, FP8 KV and client as the V4-Pro tables above; DSpark off.
  • Numbers below are from a full repeat pass on fresh servers with a warm Triton cache.

ISL 8192 / OSL 1024

conc BCG TTFT CP TTFT CP+BCG TTFT TPOT (BCG / CP / CP+BCG) total tok/s/GPU (BCG / CP / CP+BCG)
1 176 / 189 / 202 184 / 176 / 207 146 / 155 / 164 7.47 / 7.45 / 7.52 293 / 293 / 292
4 240 / 199 / 470 237 / 171 / 312 209 / 167 / 407 8.56 / 8.45 / 8.45 994 / 1006 / 1011
16 465 / 247 / 714 416 / 212 / 666 414 / 192 / 734 12.06 / 11.61 / 11.60 2789 / 2901 / 2904

ISL 1024 / OSL 1024

conc BCG TTFT CP TTFT CP+BCG TTFT TPOT (BCG / CP / CP+BCG) output tok/s (BCG / CP / CP+BCG)
1 43 / 44 / 45 156 / 139 / 217 45 / 46 / 49 7.37 / 7.35 / 7.40 135 / 133 / 134
4 74 / 47 / 148 180 / 140 / 265 68 / 49 / 100 8.80 / 8.37 / 8.10 438 / 453 / 474
16 135 / 77 / 289 234 / 156 / 452 131 / 72 / 287 10.17 / 11.25 / 10.20 1513 / 1361 / 1510

ISL 8192 / OSL 2 (prefill-bound)

conc BCG TTFT CP TTFT CP+BCG TTFT total tok/s/GPU (BCG / CP / CP+BCG)
1 158 / 169 / 178 155 / 149 / 175 133 / 142 / 147 11507 / 11801 / 13675
4 536 / 550 / 573 450 / 460 / 481 454 / 469 / 489 13016 / 15483 / 15318
16 2131 / 2158 / 2440 1769 / 1802 / 2032 1767 / 1803 / 2044 13156 / 15769 / 15852

GSM8K (InferenceX eval): BCG 0.9689, CP 0.9659, CP+BCG 0.9697.

Takeaways

  • At 8k/1k, CP+BCG has the best mean TTFT at conc 1 and 4 (−12% to −17% vs the best baseline) and ties CP at conc 16.
  • At 1k/1k, it matches BCG at conc 1/16 and is best at conc 4.
  • On prefill-bound 8k/2 it matches CP at conc 4/16 and is 14% faster at conc 1.
  • TPOT is within 1% of the best baseline everywhere.

BF16 unified KV (DeepSeek-V4-Flash, TP4)

All data above uses FP8 unified KV. This PR's position and KV-gather changes also apply to the BF16 unified layout, so it was checked separately. Settings are the same as the V4-Flash runs, except SGLANG_DSV4_UNIFIED_KV_FP8=0 (server log: unified=True unified_fp8=False). Arms: CP and CP+BCG at conc 4 and 16, each run twice on fresh servers.

  • Correctness
    • GSM8K: CP 0.9682, CP+BCG 0.9682. FP8-KV CP+BCG was 0.9697.
    • Greedy outputs (11 prompts of 2 to 30k tokens): CP vs CP+BCG diverge only at near-ties (top-1/top-2 gap ≤ 0.625 nats). Stock CP compared with itself on two fresh servers diverges just as often, so this is BF16 run-to-run noise.
    • The 12k, 17k and 30k prompts, which go through multi-chunk CP prefill, match for 22–32 of 32 tokens.
  • Performance vs CP (mean / P50 / P90 TTFT in ms, second pass)
ISL/OSL conc CP TTFT CP+BCG TTFT TPOT mean (CP / CP+BCG)
1024/1024 4 169 / 131 / 254 63 / 46 / 65 8.17 / 7.93
1024/1024 16 397 / 190 / 460 127 / 64 / 269 11.27 / 10.05
8192/1024 4 548 / 168 / 332 201 / 158 / 280 8.59 / 8.28
8192/1024 16 408 / 217 / 655 401 / 218 / 640 11.68 / 11.42
8192/2 16 1745 / 1788 / 2026 1746 / 1786 / 2024 –
  • Stock CP had a 2–4 s step gap at 1k/1k conc 16 and 8k/1k conc 4, which raises its mean there.
  • BF16 vs FP8 KV for CP+BCG: no meaningful difference. TTFT is within run-to-run noise, and BF16 TPOT P50 is 1.5–2.2% lower.
  • These BF16 runs shared the node with another TP4 job, so absolute numbers are a bit noisier than the FP8 runs above.

DeepSeek-V4.1-Flash results are in #44013.

Checklist

  • Format your code with pre-commit.
  • Add unit tests: test_dsv4_prefill_cp_bcg.py, test_cp_strategy_unit.py, test_prefill_cuda_graph_runner.py, test_eager_on_graph.py and amd/test_dsv4_hip_bcg_metadata.py (82 passed after the rebase).
  • Provide accuracy and speed benchmark results: DeepSeek-V4-Pro fixed-seq and AgentX, DeepSeek-V4-Flash with FP8 KV, and DeepSeek-V4-Flash with BF16 KV.

CI States

Latest PR Test (Base): ❌ Run #38568557194
Latest PR Test (Extra): ❌ Run #38568556932
Latest PR Test (AMD ROCm 10): ❌ Run #38568557126

…phs on ROCm

Port sgl-project#38603 to the HIP DSv4 attention backend:
- HIP init_forward_metadata_prefill writes the global KV fields with the
  bucket-sized out_cache_loc under prefill graphs (num_write_tokens), passes
  the CP local_index and pads swa_replay_start, matching the CUDA backend.
- CP BCG keeps forward_batch.positions global and feeds the captured body
  CP-local positions and ids through _cp_positions/_cp_input_ids, so the HIP
  unified-KV SWA ring store sees global positions.
- CP BCG sets input_ids_global (rank-major for TP MoE, local for a2a) so the
  DSv4 hash-routed MoE layers route the right tokens (from sgl-project#40333).
… tail

DSV4's body returns (hidden_states, pre_hc_head), and both CP tails (eager
and breakable-graph replay) all-gathered both. The logits processor reads
pre_hc_head only to store hidden states for MTP/EAGLE, and DSpark drops it,
so plain serving and DSpark gathered ~4x the hidden-state bytes for nothing.
On large prefill buckets that gather also exceeds the custom all-gather size
limit and falls back to a lazily initialized RCCL communicator, which showed
up as a one-off multi-second TTFT stall on the first large CP+BCG prefill.
… for DSV4 on ROCm

sgl-project#44022 dropped (interleave, dsv4) from the supported CP BCG combinations
because the capture path did not shard input_ids with the hidden states for
DSV4 hash routing. This branch fills input_ids_global from the CP BCG input
buffers, so add the combination back on ROCm, where it is validated. CUDA
keeps it disabled until the same path is validated on B200.

Restore test_dsv4_prefill_cp_bcg.py with the DSV4 CP BCG checks and make the
supported-configuration check follow the platform.
@AMD-yanfeiwang AMD-yanfeiwang changed the title [AMD][DSV4][CP] Support interleave prefill CP with breakable CUDA graphs on ROCm [Feat] [AMD] dsv4: Support interleave prefill CP with breakable CUDA graphs Oct 14, 2026
@AMD-yanfeiwang
AMD-yanfeiwang force-pushed the amd/dsv4-rocm-prefill-cp-bcg branch from 8bdae57 to 0ccacc8 Compare October 14, 2026 13:02
AMD-yanfeiwang added a commit to AMD-yanfeiwang/sglang that referenced this pull request Oct 15, 2026
…oject#43465 by At1a8)

Applied sgl-project#43465 (head 5c5862f, author @At1a8, open) on top of
the HIP CP+BCG port (sgl-project#43819). Conflict resolution in
deepseek_v4_backend_hip_radix.py: both PRs pad swa_replay_start and pass
cp_metadata.local_index to apply_cp_reindex; kept the single copy from sgl-project#43819,
which also keeps num_write_tokens for breakable prefill graphs.
AMD-yanfeiwang added a commit to AMD-yanfeiwang/sglang that referenced this pull request Oct 15, 2026
…raphs on ROCm

Stacked on the HIP V4.1 prefill CP (sgl-project#43465) and the HIP CP+BCG port (sgl-project#43819):
- HIP low-ratio BCG capture accepts attention CP (the late-layer tail of decoder
  SWA bounded replay is already rejected with prefill graphs by the hook).
- A CP replay builds the low-ratio metadata from the static batch, whose
  out_cache_loc is padded to the bucket. The write fields (raw/c1/c2 out_loc)
  are built from the live prefix, since the eager low-ratio sources write only
  the live rows; init_forward_metadata_in_graph still pins the bucket-sized SWA
  store target read by the captured segments.
- _forward_low_ratio_sources_cp reads the live logical rows; CP BCG sets
  total_seq_lens to the capture bucket.
- Engram hashing in the CP branch runs at an eager break under BCG.
- On HIP, interleave CP BCG capture buckets start at cp_size * 16, the HIP
  minimum CP tokens per rank, so every captured bucket shards.
AMD-yanfeiwang added a commit to AMD-yanfeiwang/sglang that referenced this pull request Oct 15, 2026
…oject#43465 by At1a8)

Applied sgl-project#43465 (head 5c5862f, author At1a8, open) on top of
the HIP CP+BCG port (sgl-project#43819), plus its later test update (07efa4b) that drops
the stale V4.1 HIP CP rejection case. Conflict resolution in
deepseek_v4_backend_hip_radix.py: both PRs pad swa_replay_start and pass
cp_metadata.local_index to apply_cp_reindex; kept the single copy from sgl-project#43819,
which also keeps num_write_tokens for breakable prefill graphs.
AMD-yanfeiwang added a commit to AMD-yanfeiwang/sglang that referenced this pull request Oct 15, 2026
…raphs on ROCm

Stacked on the HIP V4.1 prefill CP (sgl-project#43465) and the HIP CP+BCG port (sgl-project#43819):
- HIP low-ratio BCG capture accepts attention CP (the late-layer tail of decoder
  SWA bounded replay is already rejected with prefill graphs by the hook).
- A CP replay builds the low-ratio metadata from the static batch, whose
  out_cache_loc is padded to the bucket. The write fields (raw/c1/c2 out_loc)
  are built from the live prefix, since the eager low-ratio sources write only
  the live rows; init_forward_metadata_in_graph still pins the bucket-sized SWA
  store target read by the captured segments.
- _forward_low_ratio_sources_cp reads the live logical rows; CP BCG sets
  total_seq_lens to the capture bucket.
- Engram hashing in the CP branch runs at an eager break under BCG.
- On HIP, interleave CP BCG capture buckets start at cp_size * 16, the HIP
  minimum CP tokens per rank, so every captured bucket shards.
- CP decode attention TP also slices the gfx950 MXFP8 weight copies
  (weight_scale_mx_e8m0, weight_scale_inv_mx, weight_bf16) that V4.1's 32x32
  ue8m0 fp8 blocks are served from; slicing only weight and the block scales
  failed the first decode-graph capture of wq_b with mismatched scale shapes.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant