Repository navigation
Conversation
DSpark verifies gamma + 1 tokens per request at every batch size. Past batch 8 the verify step cost grows with verify tokens faster than the tail draft positions add accepted tokens, so a narrower verify commits more tokens per second. With SGLANG_DSPARK_VERIFY_WIDTHS (e.g. "3,4") the worker builds a target attention backend, decode graphs and verify epilogue for each extra width after the full-width graphs, and swaps them onto the target runner around the verify forward. Each step verifies the first w positions of the proposal, where w maximizes predicted committed tokens per second: the expected accept length from running per-position acceptance, times the SPS table steps per second at bs * w verify tokens. Acceptance counts are read with a fixed lag so every TP rank picks the same width; the full width is taken until every position is measured and periodically after. Grammar and logprob steps keep the full width. Off by default.
…1 graphs Build separate width-3 and bs1 (gamma 7) runtimes with their own captured CUDA graphs so the per-step verify width switches graphs instead of padding to full width. Enable verify widths 2,3 by default for DeepSeek-V4.1 targets with a packaged MI355X table as fallback, resolve seq_lens_cpu on the forward stream, and size the memory pool and graph batches for the bs1 draft length. Co-authored-by: Cursor <cursoragent@cursor.com>
…raph Co-authored-by: Cursor <cursoragent@cursor.com>
…dded-row fill Co-authored-by: Cursor <cursoragent@cursor.com>
…t-scaled split-K main projection Co-authored-by: Cursor <cursoragent@cursor.com>
…-blocking acceptance Co-authored-by: Cursor <cursoragent@cursor.com>
d21e06d to
085e0dc
Compare
The router gate zeroes weights and IDs for rows past num_token_non_padded in the same launch, so top-k post-processing skips the separate pad-mask fill when the gate already did it. Co-authored-by: Cursor <cursoragent@cursor.com>
… coverage mhc_post_combine_norm_hip returns the updated residual, the combined input and its norm in one launch. The HIP SiLU-mul-clamp kernel drops the 1024-wide cap and uses exp2 on the fp8-grid path. Co-authored-by: Cursor <cursoragent@cursor.com>
… outputs and fewer metadata launches Apply RoPE to the query tail while writing the full row into the output buffer in one kernel, reuse preallocated commit/seq-len/out-token buffers in verify, fold candidate page-table zeroing into the length kernel, and let engram gather read strided 2-D indices without a reshape copy. Co-authored-by: Cursor <cursoragent@cursor.com>
|
DeepSeek-V4.1 AMD support is on main now (sgl-project#41308), and #16 and #18 went to sgl-project#41994. We checked whether this PR's AMD-only kernel commits are worth porting to main: FP4 indexer and attention launch fusions, MoE local route-reduce epilogue and padded-row fill, fp8-grid quantization folded into the mHC/norm producers with the split-K main projection, the router-gate pad mask, and the mHC post-combine norm / SiLU-mul-clamp changes. They don't apply to main as they are. Each of these commits conflicts with main, and main already covers part of the same ground: The remaining budget on main is small. MI355X x4, TP4/EP4, Low-Latency cell, main
Upstreaming would also mean a rewrite. The code uses module-level mutable epilogue state, Our guess is that most of the gain measured here comes from the shared-path commits (verify-width runtimes, sharded argmax and non-blocking acceptance, draft RoPE and preallocated verify outputs) and from the AITER patch; we did not measure those separately. They change behavior on other platforms too, e.g. the |
Stacked on #16 (
feat/dspark-verify-width); the first commit here is that PR. Review from the second commit on.DeepSeek-V4.1 DSpark decode on MI355X, TP4/EP4 on 4 GPUs. The changes remove launches and host syncs from the verify/draft step and fuse small memory-bound kernels into their producers. No before/after profile of the baseline exists, so how much each change contributes to the gain has not been measured.
What changed
Verify-width runtimes (second commit; part of the baseline below, not covered by the numbers) (
dspark_worker_v2.py,dspark_config.py,environ.py, scheduler / memory pool)seq_lens_cpuon the forward stream; memory pool and graph batch sizing account for the bs1 draft length.FP4 indexer and attention (
fp4_indexer*.py,fp4_rope_fake_quant.py,fused_qk_norm_rope_store.py, HIP attention backends)MoE (
moe_runner/aiter.py,topk.py,fill_padded_rows.py,deepseek_v2.py)mHC / norm / main projection (
mhc_post_combine_hip.py,mxfp8_dot_scaled_splitk.py,deepseek_v4*.py,fp8_hip.py)dot_scaledsplit-K GEMM with a separate reduce.Router gate (
rocm_router_gate.py,topk.py)mHC post-combine norm and activation (
mhc_post_combine_hip.py,silu_and_mul_clamp_hip.py,deepseek_v2_hip_act.py)Draft and acceptance (
logits_processor.py,dspark_verify.py,dspark_draft.py,dspark_verify_width.py, CUDA graph runner)Results
Baseline is #16 plus the verify-width runtimes (commits 1-2); candidate is the branch head. The numbers therefore cover the FP4, MoE, mHC, router and draft/acceptance changes, not the verify-width runtimes.
Design, and what it does not control:
VARCTX_SCHEDULE_OPT,MOE_SORT_SMALL) in AITER and matching log lines in SGLang. The branch head and the patch below remove them; they fire once per (rank, shape) and are not in the timed path, but the head itself was not re-measured.Throughput gain, median of 6 timed runs per launch; 95% CI and two-sided paired t:
real-bs1 varies most per run because the draft acceptance length differs between server launches; normalized by acceptance length (n=5) it is +7.1% [+2.4%, +11.7%].
Correctness:
torch.equalon selected shapes, for example varctx schedule and small-M MoE sort at M=1/4/24/32/37/128. It was not checked end to end.test/registered/unit/spec/test_dspark_verify_width_policy.py: 6 passed.Reproduction pins
ghcr.io/amdpilot-org/dsv41-pr39857@sha256:fccf7f7268e97eb137ddf2df0dc94cf1af1fd60186aaafef03deb74504ccef20e2e824dc583071ea1bf62f4cebe4dd437d70ff79acf8fdf9307431ece8ee275971c41cb3d1a7020b+ [Bugfix] Drain FlyDSL stage-1 LDS-DMA loads before the tile barrier ROCm/aiter#5561 @610a95851c+ [Perf] Add gfx950 DSV4.1 Flash EP4 a8w4 FMoE tuning ROCm/aiter#5562 @a2506c5bcbdba1be0a40aa45a94ad051997016db3960a90277pr39857-eval-v2(sha2565b2efa85c0d42f9c7dfe0dd08f618bab576b5efbcb75c07602d3f778c6bae8d6), script/opt/amdpilot/contract-pr39857/pr39857_eval.pyin the image.Required AITER change
The small-M MoE sort path, the
fused_moe.pydispatch and the FP4pa_mqa_logitsschedule need this AITER patch (against the AITER base above); without it these fall back to the existing paths. The same change is open as amdpilot-org/aiter#88 (branchperf/dsv41-small-m-moe-sort-varctx, onacf8fdf9).moe_sorting_small.py+fused_moe.pydispatch: a Triton sort for M<=128, 384 experts, top-6, EP mask. Its outputs match the existing path undertorch.equalacross M=1-128 and random/all-local/all-remote/single-expert masks, plus full-layer parity at M=24 and M=128. Microbenchmark: 17.6 to 13.5 µs at M=24, 17.7 to 14.3 µs at M=128.pa_mqa_logits_fp4.py:BLOCK_Sfor the_varctx_cta_info_kernelgrid goes from 256 to 32. 32 was the fastest of 32/64/128, and outputs are unchanged undertorch.equal. CUDA-graph median: 24.9 to 17.8 µs at M=24, 53.0 to 23.2 µs at M=128.aiter patch