real speculative decoding on GPT-2 · draft-vs-target verification · where the 2–3× claim actually holds
Speculative decoding is one of the most widely discussed inference optimizations in recent LLM engineering, and frequently one of the most misunderstood. The core proposition sounds ideal: achieving a 2–3× boost in decoding throughput with mathematically identical output distributions—yielding performance gains via a lightweight secondary model.
In practice, speculative decoding functions as a trade-off rather than a guaranteed acceleration: you pay the computational overhead of running a smaller draft model with the expectation that its outputs align sufficiently with the target model to yield a net speedup.
This post details the complete system stack—covering core transformer architecture, memory bandwidth constraints, the draft-then-verify loop, state-of-the-art methodology taxonomies, and empirical benchmark evaluations on GPT-2 weights—to highlight where performance gains originate and where they risk regressing.
---
---

To evaluate speculative decoding, we must analyze the hardware execution costs of a single forward pass. A causal language model generates next-token probability distributions by executing tensor operations across its transformer stack. This pipeline is bounded by two distinct factors:
For small batch sizes ($B=1$) on single-stream inference, memory transfers dominate total step latency: compute units execute quickly and subsequently stall while waiting for weight loading. Speculative decoding specifically targets this architectural bottleneck.
[Target Model (Ground Truth): GPT-2 124M]
d_model=768, depth=12, heads=12, Vocab=50k
▲
│ (pt)
┌───────────────────┐
│ Target Final Head │
└─────────▲─────────┘
│
┌───────────────────┐
│ GPT-2 (12 Layers)│
└─────────▲─────────┘
│
┌───────────────────┐
│ Input Embeddings │
└───────────────────┘
gpt2 (124M parameters, 12 layers). 1. sshleifer/tiny-gpt2 (10M parameters, 2 layers) serving as an independent small LM draft.
2. gpt2 early-exit heads at intermediate layers $m \in \{3, 10\}$ serving as a self-speculative draft.
spec_experiment.py.
Key Takeaway: A draft mechanism provides net throughput benefits only when it is computationally inexpensive (low layer count/parameter footprint) and statistically aligned with the target distribution.
---
Transformer architectures process input sequences in parallel during context encoding (prefill phase), but execute sequentially across step iterations during auto-regressive decoding (decoding phase). Token $t_i$ cannot be evaluated until token $t_{i-1}$ is generated, due to causal self-attention dependencies across historic Key-Value (KV) states.
$$\text{Latency}_{\text{autoregressive}} = N_{\text{tokens}} \times t_{\text{per-token}}$$
Panel A: Autoregressive Bottleneck (Serial)
[Step t] ──(Fetch Weights & KV)──> [Token 1] ──┐
[Step t+1] ──(Fetch Weights & KV)──> [Token 2] ──┼─> High Memory Stall / Idle Compute
[Step t+2] ──(Fetch Weights & KV)──> [Token 3] ──┘
Panel B: Speculative Verification (Parallel)
[Single Forward Pass] ──(Fetch Weights Once)──> [Verify Tokens 1, 2, 3, 4 Simultaneously]
At low batch sizes, single-token generation iterations fail to fully saturate GPU compute pipelines.

Executing a single target forward pass over a sequence of length $L+k$ requires nearly the same memory bandwidth overhead as executing a forward pass over sequence length $L$. Speculative decoding leverages this invariant by amortizing weight transfer costs across $k+1$ candidate tokens simultaneously.
---
The speculative sampling execution pipeline follows a three-step cycle:
This mathematical formulation guarantees that the final output distribution remains provably identical to sampling directly from the target model.

import torch
import torch.nn.functional as F
def spec_sample(seq, k, draft_fn, target_fn, temp=0.8):
"""
Executes speculative decoding with exact target distribution preservation.
"""
draft_tokens, draft_logits = [], []
current_seq = list(seq)
# 1. Draft Step: Generate k candidates sequentially
for _ in range(k):
logits = draft_fn(current_seq)
next_logit = logits[-1]
draft_logits.append(next_logit)
token = torch.multinomial(F.softmax(next_logit / temp, dim=-1), 1).item()
draft_tokens.append(token)
current_seq.append(token)
# 2. Verify Step: Parallel validation over k+1 positions
target_logits = target_fn(seq + draft_tokens)
prefix_offset = len(seq)
accepted_count = 0
# 3. Correction Step: Rejection sampling loop
for i in range(k):
pt = F.softmax(target_logits[prefix_offset - 1 + i] / temp, dim=-1)
pd = F.softmax(draft_logits[i] / temp, dim=-1)
candidate = draft_tokens[i]
# Accept condition
if torch.rand(1).item() < min(1.0, (pt[candidate] / pd[candidate]).item()):
accepted_count += 1
else:
# Reject: Resample candidate from adjusted distribution (pt - pd)+
residual = torch.clamp(pt - pd, min=0.0)
residual /= residual.sum()
resampled_token = torch.multinomial(residual, 1).item()
return seq + draft_tokens[:accepted_count] + [resampled_token], accepted_count
# Bonus token sampling if all k drafted tokens are accepted
bonus_token = torch.multinomial(F.softmax(target_logits[prefix_offset + k - 1] / temp, dim=-1), 1).item()
return seq + draft_tokens + [bonus_token], k
---
While all speculative decoding variants rely on the same fundamental parallel verification framework, they differ in their structural draft generation mechanisms:

| Approach | Draft Generation Architecture | Target Acceleration | Primary Operational Trade-off |
|---|---|---|---|
| Speculative Sampling | Independent auxiliary Small LM | 2.0–3.0× | Requires hosting separate draft model & tokenizer alignment |
| Medusa | Multi-head prediction heads on target | 2.3–3.0× | Requires parameter fine-tuning of prediction heads |
| EAGLE | Feature-level drafting with tree attention | 2.0–3.0× | High draft acceptance rate; higher system complexity |
| Self-Speculative | Target model internal early-exiting | 1.3–1.8× | Zero additional weight hosting; limited by layer alignment |
| Lookahead | Jacobi iteration / N-gram retrieval | 1.8–2.3× | Parameter-free; highly sequence/task dependent |
assisted_generation)from transformers import AutoModelForCausalLM, AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("gpt2")
target_model = AutoModelForCausalLM.from_pretrained("gpt2")
assistant_model = AutoModelForCausalLM.from_pretrained("sshleifer/tiny-gpt2")
inputs = tokenizer("The future of artificial intelligence is", return_tensors="pt")
outputs = target_model.generate(
inputs.input_ids,
assistant_model=assistant_model,
do_sample=True,
temperature=0.8,
max_new_tokens=50
)
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3-8B-Instruct \
--speculative-model meta-llama/Llama-3-1B-Instruct \
--num_speculative_tokens 5 \
--max-model-len 4096
---
Let $c$ represent the ratio of draft model execution cost relative to target model execution cost per token:
$$c = \frac{\text{Cost}_{\text{draft}}}{\text{Cost}_{\text{target}}}$$
Let $\alpha$ represent the average token acceptance rate across speculative steps. The theoretical speedup factor $S$ relative to standard autoregressive execution is modeled as:
$$S \approx \frac{1 + k \cdot \alpha}{1 + k \cdot c}$$
To achieve a net speedup ($S > 1$), the pipeline must satisfy the inequality:
$$\alpha > c$$
If candidate acceptance falls below the cost threshold ($\alpha < c$), the computational overhead of draft generation and verification outpaces the benefits of sequence amortization, leading to increased latency.

Evaluating speculative decoding across unaligned draft configurations demonstrates the real-world operational impact of the $\alpha > c$ constraint:
| Draft Configuration | Relative Cost Fraction ($c$) | Acceptance Rate ($\alpha$) | Speedup ($k=1$) | Speedup ($k=3$) | Net Performance Impact |
|---|---|---|---|---|---|
tiny-gpt2 (10M vs 124M) | ~0.08 | 4.0% | 0.68× | 0.44× | Performance Penalty |
gpt2 Early-Exit (Layer 3/12) | ~0.25 | 12.0% | 0.68× | 0.38× | Performance Penalty |
gpt2 Early-Exit (Layer 10/12) | ~0.83 | 29.0% | 0.38× | 0.35× | Performance Penalty |
Speedup (x Baseline)
1.2x ┼─────────────────────────────────────────────────── (Break-even: 1.0x)
1.0x ┼───────────────────────────────────────────────────
0.8x ┼───── Top Performance (k=1, alpha=4%..12%): ~0.68x
0.6x ┼───────────────────────────────────────────────────
0.4x ┼───────────────── Top Performance (k=3): ~0.35x..0.44x
0.2x ┼───────────────────────────────────────────────────
└───────┬───────────────┬───────────────┬───────────
k=1 k=2 k=3
---
Speculative decoding is a powerful technique for accelerating single-stream LLM inference, but its benefits are fundamentally conditional. Achieving real-world speedups requires strict optimization of draft alignment ($\alpha$) relative to system cost ($c$). When deploying speculative pipelines in production systems, profile target-draft acceptance rates under actual workload distributions before enabling parallel verification logic.
---