perf(deepseek4): fix DSpark fused-verify wide-q performance cliff on gfx1151#17
Open
DeanoC wants to merge 2 commits into
Open
perf(deepseek4): fix DSpark fused-verify wide-q performance cliff on gfx1151#17DeanoC wants to merge 2 commits into
DeanoC wants to merge 2 commits into
Conversation
…o-4 pool Cap the fused-verify batch to the next ratio-4 compression boundary so the boundary sits at the batch's LAST token, which the fused-verify graph's fast path assumes (deepseek4_fused_verify.inc). A mid-batch boundary is correct but ~3x slower in compute because the compressor pools mid-batch. Benchmark (gfx1151 Strix Halo, DS4-Flash + DSpark, greedy, DFLASH_DS4_SPEC=1 DFLASH_DS4_FUSED_VERIFY=1, full unified pool, forced-q sweep via /tmp/ds4_spec_q, 127-token decode, DFLASH_DS4_TIMING cum-mean): q=4 verify 311 -> 206 ms. Perf-only: only narrows the width; greedy output unchanged.
The prior default (3) is the measured sm_86 (RTX 3090) MMVQ->MMQ crossover. On RDNA the MMQ / dequant->rocBLAS-GEMM path is ~3x slower than MMVQ at the small batches a spec-decode verify produces, so a q=4 DSpark verify fell off MMVQ into rocBLAS HGEMM at M=4. Default to the MMVQ batch ceiling (MMVQ_MAX_BATCH_SIZE=8) on HIP so dense projections read each weight once for all q columns. Benchmark (same setup as the width fix commit): q=4 verify 206 -> 107 ms on top of the width fix; wide-q now scales ~linearly (q3=89, q4=107 ms). AR / single-token path unaffected (already on MMVQ). Perf-only: kernel selection, not numerics.
2e754f2 to
a619c5c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the DSpark fused-verify wide-q performance cliff on RDNA / gfx1151 for DS4-Flash. Single concern (the cliff), split into two commits:
perf(deepseek4)— boundary-align the fused-verify width (deepseek4_dspark_spec.cpp): cap the verify batch to the next ratio-4 compression boundary so the boundary sits at the batch's LAST token, which the fused-verify graph's fast path assumes (deepseek4_fused_verify.inc). A mid-batch boundary is correct but ~3× slower in compute.perf(cuda-hip)— MMVQ default on HIP (ggml-cuda.cu): defaultLUCE_MMVQ_MAX_NCOLSto the MMVQ batch ceiling (MMVQ_MAX_BATCH_SIZE=8). The prior default (3) is the measured sm_86 crossover; on RDNA the MMQ / dequant→rocBLAS-GEMM path is ~3× slower than MMVQ at small spec-verify batches, so a q=4 batch fell off MMVQ into rocBLAS HGEMM at M=4.The two only remove the cliff together, but each is independently correctness-preserving and non-regressing.
Benchmark — before/after
q=4 verify 311 → 107 ms (2.9×); wide-q now scales ~linearly (q3 = 89 ms, q4 = 107 ms) instead of collapsing. The default adaptive greedy path (q≤3, sweet spot q3 = 18.0 tok/s) and the AR single-token path are unchanged.
Methodology
GGML_CUDA_ENABLE_UNIFIED_MEMORY=1).DFLASH_DS4_SPEC=1 DFLASH_DS4_FUSED_VERIFY=1), greedy (temperature=0), 127-token generation, identical prompt across configs./tmp/ds4_spec_q(adaptive width off) so each config is measured at the same q.DFLASH_DS4_TIMING=1per-phase cumulative means ([ds4-spec-t] TOTAL), one warmup request discarded, single loaded server per sweep (weights identical/warm across configs).rocprofv3 --kernel-trace(see below).Why it was a cliff (rocprofv3 --kernel-trace)
At q=4 the dense MLA attention projections crossed the MMVQ→MMQ threshold and dispatched to rocBLAS FP16 GEMM at M=4 with 128×128 tiles:
Cijk_*HB_MT128x128x32(~34% of decode) +dequantize_block_rocmfp4_fast(13%) dominated the trace. Keeping them on MMVQ (which reads each dense weight once for all q columns) removes both. The width fix additionally keeps the ratio-4 compressor pool at the batch's last token.Correctness
Notes
base/ds4-dspark-435dd26(the DSpark branch commit) so the diff is only these two changes.Follow-up q-sweep — current operating point (improved draft accept, 2026-07-14)
Re-measured on the current DS4 baseline with the improved draft (higher accept rate), gfx1151, forced-q (
/tmp/ds4_spec_q, adaptive off), greedy, 127-token, 2 prompts:q=3 remains the static optimum (~19.2 tok/s), and q=4 is now competitive (~18.5) — no longer the pre-fix cliff — confirming the wide-q path is healthy at the current operating point. q=4 still edges below q=3 on average because its verify cost grows faster than the extra accepted tokens (56 vs 52 ms/tok), though it pays off on individual high-accept steps (18.0 tok/s @accept 1.82 → 19.1 @accept 2.05).
A dynamic confidence-gated width (q=4 only on high-confidence steps) was explored to capture those steps but does not beat static q=3: the DSpark confidence head is currently over-confident and over-selects q=4, and boundary-alignment alone can't offset q=4's higher per-token verify cost. Recommendation: static q=3 pending confidence-head recalibration (a model-side follow-up).