Repository navigation
Fixed Piecewise - #20
Conversation
`cudagraph_mode` requests that need piecewise compilation were silently replaced with NONE when the compilation mode was not VLLM_COMPILE and breakable CUDA graphs were off. A user asking for PIECEWISE therefore got no CUDA graphs at all, and FULL_AND_PIECEWISE lost the FULL decode graphs it also asked for. Downgrade FULL_AND_PIECEWISE to FULL_DECODE_ONLY (its non-piecewise half) and keep NONE only for a bare PIECEWISE request, with a warning that names the two ways to actually get piecewise capture. Signed-off-by: Waldecir Santos <waldecir@gmail.com>
…ture
`dai[:, lora_rank : lora_rank + hc_count]` is a (num_tokens, 4) view whose row
stride is `lora_rank + hc_count + pad_size` (336 for Qwen3.8-Flash-Next).
Inductor specializes on the trace-time stride, so piecewise CUDA graph capture
aborted with
AssertionError: expected size 12==12, stride 4==336 at dim=0
whenever the runtime tensor was contiguous. Materialize the slice through
`_injection_slice()` and make the non-fused split path contiguous as well; the
copy is a few bytes per token per layer.
Signed-off-by: Waldecir Santos <waldecir@gmail.com>
…peculator With FULL_AND_PIECEWISE on ROCm the target model replays its piecewise graphs instead of capturing FULL ones (rocm_full_executes_as_piecewise). Two speculator paths still assumed a FULL graph existed: - draft prefill dispatched to `prefill_cudagraph_manager.run_fullgraph()` and raised "No cudagraph for ... FULL num_tokens=12 num_reqs=4 uniform_token_count=3"; - the draft-decode manager (downgraded to FULL_DECODE_ONLY, so without piecewise captures) also skipped FULL capture, because the skip decision read the global compilation config instead of the manager's own mode, and then raised "No cudagraph for ... FULL num_tokens=3 num_reqs=3 uniform_token_count=1". Route the draft-prefill dispatch through `_prefill()` with the piecewise runtime mode when the redirect applies, and gate the capture skip on `self.cudagraph_mode.has_piecewise_cudagraphs()`. Signed-off-by: Waldecir Santos <waldecir@gmail.com>
…puts
`_get_positions` returned `mrope_positions.gpu[:, :num_tokens]`, a view of a
(3, max_num_tokens + 1) buffer. Inductor specializes on the runtime stride, so
piecewise CUDA graph capture aborted with
AssertionError: expected size 3==3, stride 2048==2049 at dim=0
This was root-caused in the sibling checkout but left unfixed; the V2 model
runner already returns a contiguous copy (`RopeState.get_positions`). Add
packed contiguous workspaces plus `_contiguous_positions()` and use them for
both M-RoPE and XD-RoPE so MRV1 compiled inputs have a stable stride.
Signed-off-by: Waldecir Santos <waldecir@gmail.com>
Add `tools/rdna2/serve_v620_piecewise.sh`: the same serving parameters as serve_v620_baseline.sh with mode 3 + FULL_AND_PIECEWISE, all environment and paths self-contained and overridable, plus the tuning/overlap/import guards. Record the investigation, the five fixes and the measured trade-off in docs/rdna2/V620-PIECEWISE-CUDAGRAPHS.md. On the same box with identical llm-context-bench parameters, prefill is unchanged (1,972.4 / 1,980.5 tok/s vs 1,983.8 / 1,985.3) while decode drops from 63.07 / 70.08 to 25.74 / 33.84 tokens/s: ROCm replays piecewise graphs with eager GDN/attention breaks, so mode-0 FULL_DECODE_ONLY stays the faster decode configuration. Signed-off-by: Waldecir Santos <waldecir@gmail.com>
FULL_AND_PIECEWISE was replaying every ROCm FULL dispatch as piecewise graphs, so decode paid eager GDN and attention breaks. Capture and replay both graphs: uniform decode uses the live FULL CUDA graph, mixed and prefill batches use piecewise graphs. Bring the piecewise capture fixes from #20: do not drop FULL graphs when piecewise compilation is unavailable, make the hyperconnection injection and M-RoPE positions contiguous, and let an MTP draft manager capture its own FULL graphs. Co-authored-by: Cursor Agent <cursoragent@cursor.com>
|
Integrated into Measured on 4× V620 (gfx1030, TP4, W4A16 Flash-Next, 8× concurrency, seed 12345), merged tree:
The V620 PIECEWISE launcher and the measured report from this PR are kept in the merged branch. Thanks — the capture fixes are what make the F&P combination possible. |
|
Correction to the closing note above: the "bare FULL crashes at capture" finding was wrong - it was stale Inductor-cache poisoning in my test harness, not a capture bug. With a fresh Root cause of the earlier crash: |
rocm: get compiled PIECEWISE CUDA graphs working on the V620 stack
Follow-up to #17.
--compilation-config '{"mode":3,"cudagraph_mode":"FULL_AND_PIECEWISE",...}'now boots, captures piecewise graphs and serves correct output on the V620 stack
(Qwen3.8-Flash-Next W4A16 AutoRound, TP4/EP4, MTP2, PLE CPU offload, prefix caching,
4x V620 / gfx1030). Previously PIECEWISE was unreachable.
Why this is not duplicating an existing PR
Checked the open PRs against
rdna_extras(#19, #18, #10, #9, #2) — none touchesCUDA graph capture,
cudagraph_modeor the mRoPE/HC strides. #17 merged theserving stack but kept
mode 0+FULL_DECODE_ONLY, so compiled piecewise capturehad never run on this stack.
What was wrong
cudagraph_mode: PIECEWISEwithmode: 0silently produced no graphs.VllmConfig.__post_init__replaced the mode withNONE(piecewise capture needsInductor splitting or breakable CUDA graphs) and only logged at INFO:
Cudagraph mode PIECEWISE is not compatible with compilation mode 0. Overriding to NONE.The server answered correctly but every step ran eagerly.
dai[:, lora_rank : lora_rank + hc_count]is a(num_tokens, 4)view with row stride 336; Inductor specializes on the trace-timestride and capture aborted with
AssertionError: expected size 12==12, stride 4==336 at dim=0.rocm_full_executes_as_piecewisemakes the target replay piecewise graphs insteadof capturing FULL ones, but draft prefill still called
run_fullgraph()(
No cudagraph for ... FULL num_tokens=12 num_reqs=4 uniform_token_count=3) and thedraft-decode manager wrongly skipped FULL capture because the skip decision read the
global compilation config (
No cudagraph for ... FULL num_tokens=3 num_reqs=3 uniform_token_count=1).(
AssertionError: expected size 3==3, stride 2048==2049 at dim=0) — root-causedearlier but left unfixed; MRV2 already returned a contiguous copy.
Changes
vllm/config/vllm.pywarning_oncenaming the remedy;FULL_AND_PIECEWISEdowngrades toFULL_DECODE_ONLY(keeps its FULL graphs) instead ofNONE.vllm/models/qwen4_exp/amd/hyperconnection.py_injection_slice()returns a contiguous HC injection; the non-fused split path too.vllm/v1/worker/gpu/spec_decode/autoregressive/speculator.py_prefill(cudagraph_runtime_mode=PIECEWISE)when the redirect applies.vllm/v1/worker/gpu/cudagraph_utils.pyself.cudagraph_mode.has_piecewise_cudagraphs().vllm/v1/worker/gpu_model_runner.py_contiguous_positions().tools/rdna2/serve_v620_piecewise.shserve_v620_baseline.sh, mode 3 +FULL_AND_PIECEWISE, all env/paths self-contained and overridable.docs/rdna2/V620-PIECEWISE-CUDAGRAPHS.mdTests run
tests/compile/test_config.py::test_splitting_ops_dynamicfails on this ROCm forkboth before and after the change (the ROCm block appends the TP collective ops to
splitting_ops); verified unchanged by stashing the branch.Two new tests:
test_piecewise_cudagraph_without_compilationandtest_mrv1_contiguous_positions_per_capture_size.Model evaluation (4x V620, 16k / 1024 output, MTP2)
Same box, same harness (
llm-context-bench, the #17 author's protocol), samelauncher parameters; the only difference is
cudagraph_mode. Both tables arevalid (cold protocol:
--profile pr17-rocm10-tuned-cold --suite all --lane performance --sizes 16k --max-retries 0 --input-size-tolerance-percent 11, freshboot, 0.0 % prefix-cache hit rate).
FULL_DECODE_ONLYregular proseFULL_DECODE_ONLYcodingFULL_AND_PIECEWISEregular proseFULL_AND_PIECEWISEcodingPrefill unchanged; mean inter-token latency 15.85 → 38.85 ms (regular) and
14.27 → 29.55 ms (coding). On ROCm the compiled path replays piecewise graphs with
eager GDN/attention breaks, so
mode 0+FULL_DECODE_ONLYremains the fasterdecode configuration. This PR makes PIECEWISE available and correct, not faster;
a capture ladder covering prefill chunks is the follow-up that would change the
trade-off, and it needs a VRAM measurement against the fixed 3.75 GiB KV budget.
Boot evidence:
ROCm FULL decode executes piecewise CUDA graphs+Graph capturing finished in 2-3 secs, took 0.46 GiB; probesParis,1 + 1 = 2,5 + 5 = 10,Berlin; MTP acceptance 80.8 %. A bare"cudagraph_mode":"PIECEWISE"also boots and serves correct output.Known limitations
mode 0+VLLM_USE_BREAKABLE_CUDAGRAPH=1+PIECEWISE)capture but fault on replay (
HSA_STATUS_ERROR_EXCEPTIONinindexSelectSmallIndex<Half,long>); documented, not fixed.[3,6,12]were not measured against the 3.75 GiB KV budget(a 1..256 ladder OOMs during KV allocation).
AI assistance
AI assistance was used to investigate, implement and test this change. The
submitting human reviewed every changed line and ran the commands above.