Theme
Reflections · July 2026

You Can Just Download More
Tokens/Sec

$ git log --oneline -1 dspark-v9 # latest DSpark commit

DeepSeek's DSpark is a faster, lossless, speculative decoding technique. 50-80% faster per-user generation than the previous production MTP-2 DSV4F baseline I ran locally.

In the last post, we were attempting to figure out the best version of GLM-5.2 to run and ended up going with DeepSeek V4 Flash.

Since then, the RTX PRO 6K Discord community has been iterating on DSpark, a new variant of speculative decoding from DeepSeek. DeepSeek has open-sourced the DeepSpec training repository (including Eagle3, DFlash, and DSpark) and released the trained DSpark checkpoints for DeepSeek-V4-Flash.

What is DSpark?

Short version: DSpark is DeepSeek's speculative decoding setup for V4, and it's the first one that doesn't make you pick between a draft model that's fast or a draft model that's right. It gets to be both, and it pays attention to how busy the server is before deciding how much work to bother verifying.

The training code is public in DeepSpec (which also has Eagle3 and DFlash if you want to compare). Here's the idea.

The problem: generation is memory-bound

LLMs generate one token per forward pass, and every pass starts by streaming the full model weights out of memory. For one token that's barely any math at all, so when you're generating for a single request, the compute sits nearly idle and each token costs you a full memory round-trip. That's the wall, and no amount of kernel tuning gets you around it: the only way to speed up one stream is to get more tokens out of each pass.

Serving many users at once can soften this, since batched requests share the weight reads.

Speculative decoding is the standard way around this next token full weight problem. A small draft model guesses the next several tokens, then the big target model checks the whole guess in one forward pass. Causal masking makes this work, the target model scores positions 1 thru 7 in parallel.

normal:      N × (load weights + compute 1 token)
spec decode: (N / accepted_per_round) ×
             (draft_time + load weights + compute N_tokens)

Verification uses rejection sampling, so the output distribution is exactly the target model's, making this lossless, not an approximation. The only thing that changes is how fast you get there. How much faster depends entirely on how good the draft is.

The tradeoff everyone was stuck with

Until now, draft models came in two flavors, and both with downsides:

And there was a second, quieter problem on the serving side: everyone verified the full block, every round, for every request. Fine when the GPU is idle. Under load, every token you verify that was doomed to be rejected is a batch slot stolen from another user.

the bottleneck: every token costs a full trip through memory load weights compute 1 token repeat for every single token weights: hundreds of GB read per pass math: almost none, only one token's worth GPU compute sits idle. you're waiting on HBM, not FLOPs. the fix: guess cheap, verify once small draft model guesses tokens t1..t7 big target model checks all 7 in ONE pass keep the longest correct run output identical to normal decoding one weight-load now produces several tokens instead of one. the catch: drafting is its own tradeoff draft one-by-one (Eagle3, MTP) + each token sees the last → accurate − serial loop again → blocks stay short draft all-at-once (DFlash, Medusa) + whole block in one pass → fast − blind to neighbors → tail falls apart
why generation is memory-bound, how speculative decoding routes around it, and where prior drafters got stuck

How DSpark gets both

DSpark's answer is semi-autoregressive drafting: run the two stages back to back and take the best half of each.

The result is a draft that has the parallel model's length with most of the autoregressive model's coherence. In the paper's offline benchmarks (Qwen3 4B/8B/14B targets), average accepted length improves ~27–31% over Eagle3 and ~16–18% over DFlash.

The second trick: stop verifying tokens you already know are doomed

The other half of DSpark is the part I find more interesting, because it's a systems idea rather than a modeling one. Alongside each draft token, a little confidence head outputs a probability: "given that everything before me was accepted, how likely am I to survive verification?" Chain those down the block and you get a survival probability per position, calibrated after training (they use sequential temperature scaling) so the numbers actually mean what they say.

Then the hardware-aware prefix scheduler decides, per request, how much of the block is worth verifying at all. The trade it's making: each extra token you verify costs a batch slot, and what a batch slot is worth depends on how loaded the engine is right now. So it weighs expected accepted tokens against the engine's profiled throughput curve and picks the cutoff that maximizes total throughput and it stops early rather than peeking at later tokens, which is what keeps the whole thing provably lossless (the paper has a nice counterexample in the appendix showing how a naive "look at everything, then decide" scheduler quietly skews the output distribution).

In practice it behaves like this: GPU idle? Verify all 7 tokens, rejected guesses are nearly free. Server on fire? Verify only the confident prefix, don't burn batch slots on a tail you were going to throw away. DeepSeek reports this is what lets them hold strict latency SLAs (120 tok/s on Flash, 50 on Pro) under load where the old MTP baseline falls off a cliff. Per-user speedups of 60–85% at matched throughput, which is where the number in the lead comes from.

one DSpark round, end to end 1 · PARALLEL BACKBONE: all 7 positions drafted in one pass (target hidden states injected) t1 t2 t3 t4 t5 t6 t7 fast, but tokens can't see each other yet 2 · SEQUENTIAL HEAD: walks left→right, patches the guesses; emits a confidence per token backbone draft: "of" "problem" patched: "of" "course" t1 t2 t3 t4 t5 t6 t7 .97 .94 .93 .88 .81 .42 .21 confidence head (calibrated) 3 · HARDWARE-AWARE SCHEDULER: picks the cutoff using confidence × current engine load server idle verify all 7, rejects are cheap server loaded (this round) verify t1–t5, skip the doomed tail t1 t2 t3 t4 t5 t6 t7 ← tail never runs 4 · TARGET VERIFIES THE PREFIX IN ONE PASS t5′ 4 in, t5 fixed
the decoding cycle: parallel draft → sequential patch + confidence scores → load-aware cutoff → one verification pass. skipped tokens never touch the GPU.

Nothing here is a moonshot, each weakness in the spec-decode pipeline had a known fix, and DSpark's contribution is stacking all of them (better draft, calibrated confidence, load-aware scheduling) into one system and actually shipping it. The open checkpoints and the DeepSpec training code mean you don't have to take the paper's word for it, which is exactly what the rest of this post is about.

Overall Rankings

First, the headline numbers. I've added DSpark as a seventh entrant to the ranking table:

$ ./bench --rank --include dspark # model rankings with DSpark
$ cat tok-per-sec.txt # per-stream tok/sec at various concurrency levels (32K context)
PER-STREAM 1 concurrent 2 concurrent 4 concurrent 8 concurrent
GLM-5.2 IQ4_NL (llama.cpp, PP) 40 t/s 30 t/s 20 t/s 11 t/s
DeepSeek V4 Flash (vLLM, tp=4) 185 t/s 102 t/s 162 t/s 155 t/s
DeepSeek V4 Flash DSpark (vLLM, tp=2) 330 t/s 225 t/s 175 t/s 148 t/s
AGGREGATE ×1 ×2 ×4 ×8
GLM-5.2 IQ4_NL (llama.cpp, PP) 40 t/s 60 t/s 80 t/s 88 t/s
DeepSeek V4 Flash (vLLM, tp=4) 185 t/s 204 t/s 648 t/s 1,240 t/s
DeepSeek V4 Flash DSpark (vLLM, tp=2) 330 t/s 451 t/s 701 t/s 1,185 t/s

DSpark at tp=2 nearly matches standard MTP=2 at tp=4 (1,185 vs 1,240 tok/s at 8 concurrent), despite using half the GPU parallelism. I saw 1500+ at DSpark TP=4 but was having riser issues, hence dropping down to tp=2 for this writeup.

DSpark also outperforms stock vLLM by 9 percentage points, jumping from 56% to 65%. That puts it ahead of the GLM 5.2 FP8 API baseline. And that makes, honestly, no sense. I am going to have to re-do the GLM 5.2 FP8 test with a different provider. I have been thinking of benching multiple OpenRouter providers to see how different they are despite the same reported precision. I think there are likely huge variances there.

Regardless of the GLM 5.2 FP8 score, it's also potentially stronger than running standard MTP, which is also interesting on its own. Faster? Lossless over native and better than standard MTP? Sign me up.

This is not just a throughput improvement from custom kernels. The scores are higher because DSpark's better scheduling and speculative decoding let the model spend more compute cycles on hard problems before hitting the timeout, and because KV cache on MLA is dramatically more faithful than quantizing dense K/V directly.

Performance by Category

$ ./bench --heatmap --by-category --include dspark # category breakdown with DSpark

DSpark improves or matches stock vLLM in every single category. The biggest jumps:

Strengths & Weaknesses

Per-model profile cards showing each model's strongest and weakest categories:

$ ./bench --profile --strengths-weaknesses # per-model profile

Token Efficiency

Token usage statistics: average output tokens per task, split by pass/fail:

$ ./bench --tokens --efficiency # token efficiency

Detailed Category Breakdown

The full pass/fail matrix, organized by category, showing every task and every model:

$ ./bench --group --by-category --include dspark # per-category detail