Conceptual GPU board with fixed dark model blocks and an expanding green KV cache
Same silicon, same weights. The decode rate moved 2.8× because of what ran around the model, not inside it.

§1The five-second version

A language model normally writes one token per forward pass, and on a 27B model every pass is expensive. Speculative decoding puts a much smaller drafter in front: it guesses several tokens cheaply, and the big model checks the whole guess in a single pass. Accepted guesses are nearly free speed. Rejected guesses cost only the check. Under greedy decoding the output is identical to what the big model would have written alone—the drafter proposes, the target disposes.

THE MECHANISM

Speed comes from speculation. Correctness comes from verification. The target model never publishes a token it did not verify itself.

§2Two ways to get a drafter

There are two flavors, and on my runner they are mutually exclusive per model—the model’s own anatomy decides which one you get.

I measured both, because I run two different Qwen3.8-27B community derivatives on this Mac: a vision-capable 4-bit build (17 GB, quantization group 128) that ships its own MTP weights, and an abliterated 4-bit build (14 GB, group 64) with no draft head, which gets DFlash 2. What abliteration is—and why I use it at all—is covered in deep dive 05; this note treats that build purely as an inference target.

§3The bench rules

Every number below was collected the same way, changing one variable at a time:

PROMPT        same shape every run · ~2,150 tokens
STREAMING     on · first-token time (TTFT) recorded
DECODE RATE   completion tokens ÷ (wall − TTFT)
RUNS          three per configuration · mean reported
CONCURRENCY   1 request · one config change per A/B
CONTEXT       128K configured · 262K native

The decode-rate definition is not pedantry. It is the entire difference between §4 and §5 of this note.

§4The numbers

ConfigurationDecode tok/sChangeDraft acceptance
MTP build · speculation off18.5baseline
MTP build · native MTP on22.9+24%~69% (build’s card)
Abliterated build · speculation off16.7baseline
+ DFlash 2 · sampled lane38.7+131%66–82%
+ DFlash 2 · card’s greedy settings35.5+113%65–72%
+ DFlash 2 · reasoning lane, thinking on47.2+183%~68–71%

Each mean is three runs, not one. The +131% row is [35.8, 40.6, 39.7] tok/s against a [16.9, 16.0, 17.2] baseline; the MTP row is [23.8, 25.7, 19.4] against [18.5, 17.9, 19.1]. Cold first-token time on the drafted build also fell on its own, 15.41 s to 11.41 s, before any cache was involved. Separately, a warm prefix cache moved time-to-first-token on the long prompt from 12.34 s to 1.23 s—a 90% cut before the first output token exists.

Three readings from that table:

The failed first pairing

The table above was not the first attempt of the day. Earlier that afternoon I bolted DFlash 2 onto the MTP build—the target that already ships its own draft weights—under a stricter harness: temperature 0, fixed seed, 128-token outputs, exact SHA-256 output-parity checks, on a machine deliberately left in its real multi-service state.

ContextTarget-only tok/sDFlash 2 tok/sChangeAcceptance
4,09616.1514.93−7.5%57.8%
16,3849.3510.20+9.1%62.5%

A quantized-draft variant did better—+1.5% at 4K, +26.1% at 16K—but was slower end to end at both contexts, and the median decode gain sat under the 15% adoption gate I had set in advance. So that configuration shipped disabled, with exact output parity confirmed and the draft files retained. Nothing stopped me bolting an external drafter onto a build that already carries draft weights; the runner allowed it, and the numbers vetoed it. Re-pairing the drafter with the draft-headless build the same evening is what unlocked every gain in the table above. Acceptance barely distinguishes the two experiments—roughly 58–62% on the wrong target, 65–82% on the right one. What changed the outcome was everything around the drafts: the evening run used the quantized draft, generations far longer than the afternoon’s 128-token outputs, and lanes whose text the drafter predicts well. Acceptance alone never tells you the speedup.

CROSS-MACHINE MARKER

The same drafter family on my RTX 5090 measured 139.7 tok/s on this model at 32K context (deep dive 35). Same method, different silicon—the matched Mac-versus-CUDA comparison is its own future note.

§5The number that nearly reverted a working config

My runner logs speculative-decoding throughput like this:

generation complete: 181 tokens, 11.3 tok/s, acceptance=74.0%,
  phases[prefill=10970.8ms draft=232.9ms verify=244.8ms
         replay=24.1ms commit=49.2ms]

11.3 tok/s. My no-drafter baseline decoded at 16.7. For a few minutes on 20 August, the working conclusion was “DFlash 2 is a regression—revert it.”

The logged figure is end-to-end: total tokens divided by total wall time, prefill included. With a ~2,150-token prompt, prefill alone was ~11 of the 16 seconds. The same run measured as a decode rate—completion tokens over the window after the first token—was 35.8 tok/s. On this target, that is the entire difference between “broken” and “a 2.3× speedup.” One phase dominated the denominator, and the log never said so.

THE RULE I KEEP

A throughput number without its denominator definition is not a measurement. Ask what the clock included before believing what it says.

§6Read the derivative’s card, not the base card

The second config error of the session: I applied the base Qwen sampling recipe—temperature 1.0, top-p 0.95, top-k 20—to the abliterated build, because that is what the original model documents. The derivative’s own model card explicitly rejects those values: it wants greedy temperature 0, a repetition penalty of 1.15 it calls essential (without it, greedy decoding loops on imports and boilerplate), no top-p or top-k, and no system prompt. The derivative’s card overrides the base card. Every time.

Two traps stacked on top of that:

And one sanctioned deviation, documented as an owner decision rather than a default: my reasoning lane runs sampling with thinking on and neutral penalties—off the derivative card’s greedy recipe—because the card’s repetition penalty exists as a greedy anti-looping fix and stops applying once sampling is on. That lane is the fastest measured config in §4, and its output was verified loop-free (longest repeated 6-gram across the test outputs: 2).

§7Housekeeping the tuning guides skip

The exact local receipt

SPECULATIVE DECODING RECEIPT · MEASURED 20 AUG 2026

Machine
MacBook Pro · Apple M4 Max · 128 GB unified · 40-core GPU
Runner
oMLX 0.6.3rc1 · local server · loopback only
Targets
Two Qwen3.8-27B community derivatives · 4-bit · 17 GB and 14 GB
Drafter
DFlash 2 · ~2B parameters · Apache-2.0
Context
128K configured of 262K native
Method
Streamed · decode = completion ÷ (wall − TTFT) · three runs per config · concurrency 1
Best decode
47.2 tok/s · +183% over the 16.7 baseline
Decision
Adopt — both lanes, per the field-note contract

This closes the “measurements pending” status from deep dive 33 and updates the current entry in the local model field notes. One honest asterisk: the measured stack is a pair of 4-bit community derivatives, not the stock 8-bit conversion the original receipt named—the download plan changed when the drafter did.

What I am not publishing: the exact repositories of the two modified community builds—deep dive 05 explains that boundary—and my local port and profile layout. The method transfers to any compatible target-and-drafter pair without either.

The compact rule

  • If the model ships draft weights, switch them on; if it does not, add an external drafter. Never both on one model.
  • Define the denominator before trusting any tok/s—end-to-end and decode rate answer different questions.
  • Warm-cache runs are compared end-to-end only; the decode window absorbs cache restore and lies.
  • The derivative’s card overrides the base card, and your profile overrides both. Check all three layers.
  • Predictable text accelerates best: reasoning traces drafted at ~70% acceptance and produced the fastest lane.
  • Cap disk caches yourself—defaults assume a bigger disk than mine.
  • +183% came from configuration, not hardware. The machine never changed.

Primary sources