dspark: cut per-cycle CUDA overheads (single-draft decode, GPU confidence probe) - #772
Open
vincenzopalazzo wants to merge 2 commits into
Open
dspark: cut per-cycle CUDA overheads (single-draft decode, GPU confidence probe)#772vincenzopalazzo wants to merge 2 commits into
vincenzopalazzo wants to merge 2 commits into
Conversation
A one-token draft gives the batch verifier nothing to check: the only proposed token was already matched against the target argmax, and commit_drafts is unconditionally 1. The cycle still paid a batch-kernel suffix forward (which cannot use the decode-graph fast paths) plus a per-layer frontier snapshot round trip just to commit state that the ordinary single-token decode produces directly. Decode the accepted draft through the plain decode path instead; the resulting state and logits match target-only decode exactly, and multi-token drafts keep the direct verifier-state commit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The proposer's confidence gate read a 28 KB hidden row back to the host and ran a 7424-wide matvec on the CPU for every drafted token, twice per cycle on average -- a serial device-to-host ping-pong right in the middle of the propose loop. Compute the same logit in place instead: one small kernel dequantizes the markov_w1 row for the previous token and dots the confidence projection against [hidden ; markov_state], and the host reads back four bytes. The value only gates proposal length, which the verifier re-checks token by token, so floating-point reduction-order differences from the CPU matvec cannot change committed output. The CPU path remains as the fallback (Apple, non-Q8_0 markov weights, DS4_DSPARK_NO_GPU_CONFIDENCE) and now re-derives markov_state before any CPU markov consumer runs, which also covers the pre-existing reused-first-confidence case. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Summary
Two CUDA-side DSpark cycle-cost reductions, measured on a DGX Spark (GB10):
verify that cannot check anything (
commit_draftsis unconditionally 1 forone drafted token) plus a per-layer frontier snapshot; the single-token
decode path keeps the decode-graph kernels and leaves exactly the state
plain decode would have left
a 4-byte readback instead of a 28 KB device-to-host hidden copy and a
7424-wide host matvec per drafted token, twice per cycle on average
weights,
DS4_DSPARK_NO_GPU_CONFIDENCEas the diagnostic switch), and makeit re-derive
markov_statebefore any CPU markov consumer runs — this alsocovers a pre-existing case where
reuse_first_confidencecould leave theCPU markov fallback reading a stale
markov_stateRoot cause
DS4_DSPARK_STATS=1on GB10 shows the cycle economics: target eval ~60 ms,batch verify ~85 ms, propose ~20 ms. With confidence pruning most proposals
are a single token, and for those the verify pass re-derives state the
ordinary decode produces directly, at batch-kernel prices (no decode graphs,
tiny-batch kernel tiers). On the propose side, an nsys trace shows the
confidence gate doing a blocking 28 KB D2H read plus a host matvec per
drafted token, serialized in the middle of the propose loop.
Performance (main
84cc882vs PR, DGX Spark GB10, CUDA sm_121)DeepSeek-V4-Flash-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8-chat-v2-imatrix.gguf(81 GiB) +
DeepSeek-V4-Flash-DSpark-support.gguf,--dspark --temp 0 --nothink --tokens 128, defaults otherwise, 3 runs, median generation t/s:Isolating the two commits with the diagnostic switch on the PR build
(
DS4_DSPARK_NO_GPU_CONFIDENCE=1leaves only the single-draft change):Propose-stage breakdown (
DS4_DSPARK_STATS=1, whole 128-token prose run):The GPU and CPU probes made identical gating decisions in every run (same
proposed/accepted counters and histograms). The logit differs from the CPU
matvec only by floating-point reduction order, and it only gates proposal
length; the verifier re-checks every proposed token against the target
argmax, so committed output cannot change.
tests/dspark_acceptance_fixture.sh(64-token cases, PR build, defaults):The math case (proposer never fires) was previously a small net loss; the
cheaper probe makes it neutral.
Plain decode is untouched by both commits (
ds4-benchruns without--dspark, so the changed code never executes). README-style sweep(promessi_sposi, 2048..32768, 128 greedy tokens), run in both orders to
control for the box warming up — whichever build runs first wins by the same
fraction of a percent, so the residual deltas are run-ordering noise, not
code:
Generation t/s (main first / PR second, then PR first / main second):
Prefill behaves the same way (798-806 t/s at 2048, 878-896 t/s above,
order-dependent within ~1%).
Test plan
make cuda-sparkclean on DGX Spark (CUDA 13.0, sm_121)make(Metal) clean on macOSmake cuda-regressionon the Sparktests/dspark_acceptance_fixture.sh: 5/5output_match=1, zeroverifier errors, zero replay fallbacks
DS4_DSPARK_NO_GPU_CONFIDENCE=1: identical DSpark statscounters, so the GPU probe changes timing only
ds4-benchCSVs on the same box, same power stateAnalysis and patches developed with Claude (see commit trailers); all
numbers measured on real hardware as described.