Generating a token from a large language model is slow for a reason that has nothing to do with arithmetic. To produce one token, the GPU must read every weight in the model out of memory. For a 70B model in 16-bit that's roughly 140 GB moved across the memory bus, and the actual multiplication that happens with those weights is trivial by comparison. The hardware spends its time waiting on memory, not computing.
Which leads to a strange and exploitable fact: checking five tokens costs almost exactly what generating one costs. The weights get read once either way. If you already have five candidate tokens in hand, you can score all five in a single forward pass, for very nearly the price of scoring one.
You don't have candidate tokens, of course. That's what the small model is for.
The algorithmDraft, verify, keep what survives
The scheme was published independently by Leviathan et al. and Chen et al. in 2023. You run two models: a draft model that's small and fast, and the target model whose output you actually want. One round goes like this.
- The draft model generates
ktokens the normal slow way. It's small, so this is cheap: call it a tenth the cost of a target pass. - The target model runs one forward pass over the prompt plus all
kdraft tokens, which gives you the target's probability for every drafted position at once. - Walk the draft left to right. Accept token
iwith probabilitymin(1, p(xᵢ) / q(xᵢ)), target probability over draft probability. If the target likes the token at least as much as the draft did, it's kept outright. - At the first rejection, throw away that token and every token after it, and resample one token from the residual distribution
norm(max(0, p − q)). If nothing is rejected, sample one bonus token from the target directly.
So a round yields somewhere between 1 and k+1 tokens, for the price of one target pass plus k cheap draft passes. Here is a real round.
The guaranteeThe output is not an approximation
This is where people expect a catch, so it's worth being precise: the accept-reject rule above is constructed so that the tokens coming out are distributed exactly as if you had sampled from the target model directly. Not close to it. Identically. The proof is a page long and it's the reason the technique matters: the draft model influences speed and nothing else.
The intuition is in the residual. When the draft proposes a token the target dislikes, you don't just drop it. Dropping it alone would bias the distribution toward tokens the draft happens to favour. You resample from max(0, p − q), the probability mass the target wanted that the draft failed to offer. The rejection step pays back exactly the bias the acceptance step introduced.
Two caveats on that guarantee, both real. It holds in exact arithmetic; in practice floating-point differences mean a speculative run won't reproduce a non-speculative run token-for-token, even at temperature 0. And several deployed variants deliberately break it: "lenient" or "typical" acceptance relaxes the test to accept more tokens, buying speed with a distribution that is no longer the target's. If a vendor offers you an aggressive speculative mode, that is the question to ask about it.
Projected gainsTwo numbers predict your speedup
What you get depends on the acceptance rate α (how often the draft guesses what the target would have said) and the draft length k. Expected tokens per round is a geometric series:
Speedup, where c = draft cost / target cost speedup = E[tokens] / (1 + k·c)
The denominator is why longer drafts stop helping. Every extra drafted token costs you whether or not it survives, and the chance it survives decays as αk. There's an interior optimum, and it moves with α.
k to 16 at α = 0.6 and you land at 0.96×. You have made the system slower by speculating.The practical reading: α is a property of your workload and your model pair, and you must measure it rather than assume it. It's high on formulaic text, boilerplate code, and anything that copies from the prompt. It's low on dense reasoning, unusual formatting, and domains your draft model never saw. Measure it on your own traffic, then read k off the curve.
The real riskIt trades compute for latency, and compute isn't always free
Everything above assumes the premise from Fig 1, that verifying extra tokens is nearly free because you're memory-bound. At batch size 1, serving one user, that's true. It stops being true as the batch grows.
Once you're decoding for many sequences at once, the weights you read from memory are already being reused across the whole batch. The GPU is no longer waiting on memory; it's doing real arithmetic, and arithmetic scales with the number of tokens you push through it. Now those k+1 verified positions cost k+1 times the compute, per sequence, and every rejected token is compute you spent on output you threw away.
k dynamically as load rises.This is the single most important thing to understand before enabling it. Speculative decoding buys latency with FLOPs. If you serve one user on a dedicated GPU, FLOPs are sitting idle and the trade is free money. If you run a busy inference service at high batch, those FLOPs have somewhere better to be, and speculation takes capacity from the queue to make individual requests marginally faster.
The rest of the risksSmaller, but they'll find you
Acceptance rate drifts
α is measured, not configured. It falls when traffic shifts to a new domain, a new language, or a new output format, and your speedup falls with it, silently. Monitor it as a production metric, not a one-time benchmark.
The draft model eats your KV cache
It occupies HBM that would otherwise hold key-value cache, which lowers your maximum batch size. On a memory-tight deployment this cost can exceed the benefit before you measure anything.
Reproducibility against a non-speculative baseline
Output distributions match; individual sequences won't, even at temperature 0. If you have golden-output tests pinned to exact tokens, they will break, and the breakage is not a bug.
Tail latency gets lumpier
Rounds return between 1 and k+1 tokens, so inter-token latency becomes bursty rather than steady. Streaming UIs that felt smooth can start to feel uneven even as the average improves.
If you are turning this onCheapest paths first
Before you provision a draft model, try the version that needs no second model at all. Prompt lookup decoding, also called n-gram speculation, drafts by searching the prompt for a matching prefix and copying what followed. Zero extra memory, zero extra compute, and on copy-heavy work like editing a file, summarising a document, or answering from retrieved context, the acceptance rate is enormous because the answer genuinely is mostly in the input. It's usually a config flag in vLLM or TensorRT-LLM.
If that doesn't fit, the ladder is roughly: prompt lookup, then a same-family small model as the draft (a 1B drafting for a 70B tends to work well, since shared tokenizer and training data lift α), then trained approaches like Medusa or EAGLE that graft extra prediction heads onto the target model and avoid a separate draft model entirely.
Then measure three things on your own traffic: acceptance rate, your typical batch size, and end-to-end latency with and without. The first tells you the ceiling, the second tells you whether the ceiling is available to you, and the third is the only number that settles it.
The technique is unusual in being genuinely free on the axis people worry about most. It does not degrade your model. What it does is spend compute to buy time: an excellent trade when you have compute to spare, and a bad one when you don't.