LeetLLM
My PlanLearnGlossaryTracksPracticeBlog
LeetLLM

Your go-to resource for mastering AI & LLM systems.

Product

  • Learn
  • Glossary
  • Tracks
  • Practice
  • Blog
  • RSS

Legal

  • Terms of Service
  • Privacy Policy

© 2026 LeetLLM. All rights reserved.

All Topics
Your Progress
0%

0 of 177 articles completed

🛠️Computing Foundations0/9
Git, Shell, Linux for AIDocker for Reproducible AIPython for AI EngineeringNumPy and Tensor ShapesCUDA for ML TrainingMPS & Metal for ML on MacData Structures for AISQL and Data ModelingAlgorithms for ML Engineers
📊Math & Statistics0/8
Gradients and BackpropVectors, Matrices & TensorsLinear Algebra for MLAdam, Momentum, SchedulersProbability for Machine LearningStatistics and UncertaintyDistributions and SamplingHypothesis Tests, Intervals, and pass@k
📚Preparation & Prerequisites0/13
Neural Networks from ScratchCNNs from ScratchTraining & BackpropagationSoftmax, Cross-Entropy & OptimizationRNNs, LSTMs, GRUs, and Sequence ModelingAutoencoders and VAEsThe Transformer Architecture End-to-EndLanguage Modeling & Next TokensFrom GPT to Modern LLMsPrompt Engineering FundamentalsCalling LLM APIs in ProductionFirst AI App End-to-EndThe LLM Lifecycle
🧮ML Algorithms & Evaluation0/11
Linear Regression from ScratchLogistic Regression and MetricsDecision Trees, Forests, and BoostingReinforcement Learning BasicsValidation and LeakageClustering and PCACore Retrieval AlgorithmsDecoding AlgorithmsExperiment Design and A/B TestingPyTorch Training LoopsDataset Pipelines and Data Quality
📦Production ML Systems0/6
Feature Engineering for Production MLBatch and Streaming Feature PipelinesGradient Boosted Trees in ProductionRanking and Recommendation SystemsForecasting and Anomaly DetectionMonitoring Predictive Models
🧪Core LLM Foundations0/8
The Bitter Lesson & ComputeBPE, WordPiece, and SentencePieceStatic to Contextual EmbeddingsPerplexity & Model EvaluationFile Ingestion for AIChunking StrategiesLLM Benchmarks & LimitationsInstruction Tuning & Chat Templates
🧰Applied LLM Engineering0/24
Dimensionality Reduction for EmbeddingsCoT, ToT & Self-Consistency PromptingFunction Calling & Tool UseMCP & Tool Protocol StandardsContext EngineeringPrompt Injection DefenseResponsible AI GovernanceData Labeling and Human FeedbackEvaluating AI AgentsProduction RAG PipelinesHybrid Search: Dense + SparseReranking and Cross-Encoders for RAGRAG Evaluation for Reliable AnswersLLM-as-a-Judge EvaluationBias & Fairness in LLMsHallucination Detection & MitigationLLM Observability & MonitoringExperiment Tracking with MLflow and W&BPrompt Optimization with DSPyModel Versioning & DeploymentSemantic Caching & Cost OptimizationLLM Cost Engineering & Token EconomicsModel Gateways, Routing, and FallbacksDesign an Automated Support Agent
🎓Portfolio Capstones0/9
Capstone: Delivery ETA PredictionCapstone: Product RankingCapstone: Demand ForecastingCapstone: Image Damage ClassifierCapstone: Production ML PipelineCapstone: Document QACapstone: Eval DashboardCapstone: Fine-Tuned ClassifierCapstone: Reproducible ML Study
🧠Transformer Deep Dives0/8
Sentence Embeddings & Contrastive LossEmbedding Similarity & QuantizationScaled Dot-Product AttentionVision Transformers and Image EncodersPositional Encoding: RoPE & ALiBiLayer Normalization: Pre-LN vs Post-LNMechanistic InterpretabilityDecoding Strategies: Greedy to Nucleus
🧬Advanced Training & Adaptation0/16
Scaling Laws & Compute-Optimal TrainingPre-training Data at ScaleBuild GPT from Scratch LabJAX for PyTorch ResearchersContinued Pretraining for Domain ShiftSynthetic Data PipelinesSupervised Fine-Tuning PipelineMixed Precision TrainingDistributed Training: FSDP & ZeROLoRA & Parameter-Efficient TuningReward Modeling from Preference DataRLHF & DPO AlignmentConstitutional AI & Red TeamingRLVR & Verifiable RewardsKnowledge Distillation for LLMsModel Merging and Weight Interpolation
🤖Advanced Agents & Retrieval0/16
Vector DB Internals: HNSW & IVFAdvanced RAG: HyDE & Self-RAGGraphRAG & Knowledge GraphsRAG Security & Access ControlStructured Output GenerationReAct & Plan-and-ExecuteGuardrails & Safety FiltersCode Generation & SandboxingComputer-Use / GUI / Browser AgentsHuman-in-the-Loop Agent ArchitectureAI Coding Workflow with AgentsAgent Memory & PersistenceAgent Failure & RecoveryRecursive Language Models (RLM)Multi-Agent OrchestrationCapstone: Production Agent
⚡Inference & Production Scale0/19
Inference: TTFT, TPS & KV CacheMulti-Query & Grouped-Query AttentionKV Cache & PagedAttentionPrefix Caching and Prompt CachingFlashAttention & Memory EfficiencyContinuous Batching & SchedulingScaling LLM InferenceModel Parallelism for LLM InferenceModel Quantization: GPTQ, AWQ & GGUFLocal LLM DeploymentSLM Specialization & Edge DeploymentSpeculative DecodingLong Context Window ManagementMixture of Experts ArchitectureMamba & State Space ModelsReasoning & Test-Time ComputeAdvanced MLOps & DevOps for AIGPU Serving & AutoscalingA/B Testing for LLMs
🏗️System Design Capstones0/9
Content Moderation SystemCode Completion SystemMulti-Tenant LLM PlatformLLM-Powered Search EngineVision-Language Models & CLIPMultimodal LLM ArchitectureDiffusion Models: Images & TextReal-Time Voice AI AgentReasoning Agent System Design
🎤AI Lab Interviewing0/4
AI Lab Coding Interview: Python SystemsAI Lab System Design InterviewAI Lab Behavioral InterviewAI Lab Technical Presentation
🔬Project Deep Dives0/17
Deep Dive - vLLMDeep Dive - SkyRLDeep Dive - FlashAttentionDeep Dive - FlashInferDeep Dive - DeepGEMMDeep Dive - NCCLDeep Dive - MegatronDeep Dive - DeepSpeedDeep Dive - RayDeep Dive - MLflowDeep Dive - PyTorchDeep Dive - TransformersDeep Dive - SGLangDeep Dive - slimeDeep Dive - DeepEPDeep Dive - TinkerDeep Dive - Light-PEFT
Back to Topics
LearnInference & Production ScaleSpeculative Decoding
🚀HardInference Optimization

Speculative Decoding

Reduce LLM inter-token latency by pairing cheap drafting with target-model verification. Learn the rejection-sampling proof, speedup model, method choices, and production rollout gates.

32 min read
Learning path
Step 140 of 177 in the full curriculum
SLM Specialization & Edge DeploymentLong Context Window Management

Personalize this lesson

Adapt explanations and teaching visuals to your background and preferred voice.

SLM deployment showed how a small model can fit on constrained hardware. Speculative decoding uses a small or cheap draft path differently: not as the final model, but as a proposal engine that a larger target model verifies.

A large target model writing a deployment summary still emits one token at a time. Speculative decoding adds a smaller draft model that proposes the next few tokens, while the target scores that whole proposed span in one pass. If the draft matches what the target would likely say, several tokens survive. If it diverges, the target repairs the first mismatch and continues. This is the core idea behind speculative decoding: use a fast draft process so a target model can preserve its output distribution in theory while reducing latency when the workload and implementation fit.[1]Reference 1Fast Inference from Transformers via Speculative Decoding.https://arxiv.org/abs/2211.17192[2]Reference 2Accelerating Large Language Model Decoding with Speculative Sampling.https://arxiv.org/abs/2302.01318

The win isn't automatic. The expensive part of low-batch LLM generation is often repeatedly moving model weights and state, not arithmetic alone. Speculative decoding targets low-batch LLM inference that's memory-bandwidth bound. A small draft model proposes several tokens, and the large target model scores that proposed chunk in one verification pass instead of spending one full decode step per token, but only when accepted-token savings outweigh the draft and verification overhead.

What is speculative decoding trying to reduce: model quality, target-model calls, or output length?

Answer

It tries to reduce expensive target-model decode calls per emitted token. A cheap draft proposes several tokens, and the target verifies them in parallel. If the rejection sampler is implemented correctly, quality and the target distribution stay the same in theory.


The inference bottleneck

To understand why speculative decoding works, start with standard autoregressive generation. An LLM emits text one token at a time, and each new token depends on the tokens that came before it. That sequential dependency limits how much work a low-batch decode step can amortize.

It's tempting to blame decode latency only on the arithmetic required by billions of parameters. Modern GPUs are fast at matrix multiplication, but low-batch decode often leaves their compute units waiting for model weights and state to arrive from memory. The bottleneck depends on workload and hardware, so measure it before choosing an optimization.

Arithmetic intensity

Arithmetic intensity measures how much work gets done per byte moved from memory. If one weight load lets the GPU verify 100 candidate tokens, that's high intensity, and the GPU is keeping busy. But if each weight load verifies only 1 token, the compute units are mostly waiting for the next memory transfer.

In GPU terms, this ratio is the number of FLOPs (floating point operations, a measure of computational performance) performed per byte of data loaded from memory:

Arithmetic Intensity=FLOPsBytes Transferred\text{Arithmetic Intensity} = \frac{\text{FLOPs}}{\text{Bytes Transferred}}Arithmetic Intensity=Bytes TransferredFLOPs​

Reading the formula

This ratio helps diagnose whether hardware is spending time computing or waiting for data. Under a weight-only FP16 back-of-the-envelope model, single-token decode performs about 1 FLOP per byte of weights moved.[2]Reference 2Accelerating Large Language Model Decoding with Speculative Sampling.https://arxiv.org/abs/2302.01318

During autoregressive decoding, generating one token with a model of PPP parameters in FP16 (16-bit floating-point) requires:

  • Compute: ≈2P\approx 2P≈2P FLOPs (one matrix-vector multiplication per layer)
  • Memory: ≈2P\approx 2P≈2P bytes loaded (entire model weights in FP16)

This gives an arithmetic intensity of ~1 FLOP/byte in the weight-only model. Real decode also pays for KV-cache traffic, activations, kernels, scheduling, and batching, so profile the actual engine before declaring a bottleneck.[2]Reference 2Accelerating Large Language Model Decoding with Speculative Sampling.https://arxiv.org/abs/2302.01318

For an illustrative Qwen3.6-27B BF16 target, the dense weight footprint alone is about 54 GB.[3]Reference 3Qwen3.6-27Bhttps://huggingface.co/Qwen/Qwen3.6-27B Insert a measured or documented device bandwidth into the simplified model before comparing serial decode with verification:

weight-streaming-diagnostic.py
1params_b = 27 2bytes_per_weight = 2 3example_bandwidth_gbs = 3_350 # example input; use the deployed accelerator specification 4weights_gb = params_b * bytes_per_weight 5weight_stream_ms = weights_gb / example_bandwidth_gbs * 1_000 6 7print(f"BF16 weight footprint: {weights_gb} GB") 8print(f"weight-only read time at {example_bandwidth_gbs} GB/s: {weight_stream_ms:.1f} ms") 9print("This is a diagnostic lower bound, not measured request latency.")
Output
1BF16 weight footprint: 54 GB 2weight-only read time at 3350 GB/s: 16.1 ms 3This is a diagnostic lower bound, not measured request latency.
PhaseSimplified expectationBottleneck to measureIntuition
Prefill (many tokens together)Higher intensityOften compute or mixedLarge matrix work amortizes weight loads
Low-batch decode (1 token)~1 FLOP/byte in FP16 weight-only modelOften memory bandwidthMove active weights to emit one new token
Arithmetic intensity comparison: serial decode reloads weights once per token at about 1 FLOP/byte, while speculative verification amortizes one weight read across a drafted chunk for higher intensity.
Speculation helps when the target pass is memory-bound: the same weight read can verify several drafted positions, while ordinary decode pays the target pass once per emitted token.

Don't assume low-batch decode is compute-bound without measuring it. Weight traffic is often a dominant cost when the target emits one token at a time.[2]Reference 2Accelerating Large Language Model Decoding with Speculative Sampling.https://arxiv.org/abs/2302.01318

Why does speculative decoding help more in low-batch decode than in a large prefill?

Answer

Low-batch decode has poor arithmetic intensity because it rereads weights to produce one token. Prefill already processes many known tokens together, so it naturally amortizes weight movement through larger matrix work.

Why verification can win

Speculative decoding is useful because the bottleneck in low-batch decode often isn't the softmax over one token; it's repeatedly moving weights and cache state for each separate decode step. If the target can verify 5 proposed tokens in one pass and reject only the first bad one, the system emits accepted tokens with fewer target-model calls, even though each speculative round does slightly more work.

That's exactly what happens here: because weight movement dominates, verifying a short candidate chunk can be much closer to one target-model pass than to KKK separate decode passes. We use a cheap draft model to propose a sequence of candidate tokens. The target model then uses teacher forcing, meaning it scores a known candidate sequence in parallel instead of generating those tokens one by one. The weight-loading cost is paid once for the whole drafted chunk, which raises arithmetic intensity and improves throughput when the draft is accurate enough.

What does teacher forcing mean in the verification step?

Answer

The target model is given the proposed draft tokens as known inputs and scores all draft positions in one forward pass. It isn't autoregressively choosing each of those tokens one at a time during verification.


The algorithm

Speculative decoding draft-verify cycle: draft proposes model serves with cache, target accepts the first three tokens, rejects cache, and samples latency as residual correction.
The draft model (small, fast) proposes a chain of candidate tokens. The target model (large, slow) scores every position in one forward pass, keeps the accepted prefix, and samples a correction at the first rejection.

The figure is small enough to trace by hand. The draft model proposes "model serves with cache." The target accepts the first three tokens, rejects "cache," and samples a correction from the residual target distribution.

Accept/reject criterion

For each proposed token, the target model asks: "How much probability did I assign to this proposal?" If the target model likes it even more than the draft model did, instant approval. If the target model likes it less, it might still keep it (proportional to how close the preferences are), or reject it and sample a correction instead.

A concrete example shows the verification step. Suppose the prefix so far is "The model" and the draft model proposes three tokens:

PositionDraft tokenDraft prob.Target prob.Verdict
1serves0.400.60Accept (target likes it more)
2with0.300.15Roll: accept with probability 0.50
3cache0.200.25Accept (target likes it more)

For token 1, the target probability (0.60) is higher than the draft probability (0.40), so the verifier always accepts. For token 2, the target probability (0.15) is lower than the draft (0.30). The verifier flips a weighted coin: it accepts with probability 0.15 / 0.30 = 0.50. If the coin comes up reject, the verifier stops checking further tokens and samples a correction from the residual distribution. Token 3 only matters if token 2 survived.

acceptance-probability.py
1draft_probability = 0.30 2target_probability = 0.15 3accept_probability = min(1.0, target_probability / draft_probability) 4 5print(f"accept probability for 'with': {accept_probability:.2f}") 6print("Later drafted positions are discarded after a rejection.")
Output
1accept probability for 'with': 0.50 2Later drafted positions are discarded after a rejection.

This construction preserves the target distribution exactly. The accepted branch contributes the overlap between the two distributions, and the residual sampler contributes the missing mass. Added together, they reconstruct the target probability for every token.[1]Reference 1Fast Inference from Transformers via Speculative Decoding.https://arxiv.org/abs/2211.17192

Why does the verifier stop at the first rejected draft token instead of checking later draft tokens?

Answer

Later draft tokens were generated conditioned on the rejected token. Once that token is replaced, the prefix changes, so later draft probabilities are no longer conditioned on the sequence that will actually continue.

Mathematically, for each draft token tit_iti​, both models assign a probability to that token conditioned on the same prefix. We compare the target model's probability ptarget(ti)p_{\text{target}}(t_i)ptarget​(ti​) with the draft model's probability pdraft(ti)p_{\text{draft}}(t_i)pdraft​(ti​):

P(accept ti)=min⁡(1,ptarget(ti)pdraft(ti))P(\text{accept } t_i) = \min\left(1, \frac{p_{\text{target}}(t_i)}{p_{\text{draft}}(t_i)}\right)P(accept ti​)=min(1,pdraft​(ti​)ptarget​(ti​)​)

Reading the formula

Compute the ratio of the big model's probability to the draft model's probability for this token. If the big model likes it more (ratio >= 1), always accept. If the big model likes it less, accept randomly with probability equal to the ratio. The bigger the disagreement, the more likely rejection.

When a token is rejected at position iii, we sample a correction token from the residual distribution:

presidual(t)=max⁡(0,  ptarget(t)−pdraft(t))Zp_{\text{residual}}(t) = \frac{\max(0, \; p_{\text{target}}(t) - p_{\text{draft}}(t))}{Z}presidual​(t)=Zmax(0,ptarget​(t)−pdraft​(t))​

where Z=∑tmax⁡(0,  ptarget(t)−pdraft(t))Z = \sum_t \max(0, \; p_{\text{target}}(t) - p_{\text{draft}}(t))Z=∑t​max(0,ptarget​(t)−pdraft​(t)) is the normalizing constant.

In plain terms

The correction picks from tokens that the big model wanted more than the draft model predicted. If the draft said "the" had 20% probability but the big model wanted 35%, that extra 15% enters the residual pool. Tokens where the draft was already too generous (draft > target) get zero residual probability, since they were over-represented, not under-represented.

Modified rejection sampling bars comparing draft q and target p for tokens A through D, with residual max(0, p-q) highlighting target-only correction mass for A and C.
The accept/reject test keeps the overlap between the draft and target distributions. When a token is rejected, the correction sampler draws only from probability mass the target wanted more than the draft did.

This accept/reject scheme is a modified rejection-sampling algorithm. The accepted branch contributes min⁡(pdraft(t),ptarget(t))\min(p_{\text{draft}}(t), p_{\text{target}}(t))min(pdraft​(t),ptarget​(t)), and the residual sampler contributes the missing mass max⁡(0,ptarget(t)−pdraft(t))\max(0, p_{\text{target}}(t) - p_{\text{draft}}(t))max(0,ptarget​(t)−pdraft​(t)). Add those two terms together and you recover ptarget(t)p_{\text{target}}(t)ptarget​(t) exactly.[1]Reference 1Fast Inference from Transformers via Speculative Decoding.https://arxiv.org/abs/2211.17192

residual-correction.py
1tokens = ["cache", "latency", "batch"] 2draft = [0.60, 0.30, 0.10] 3target = [0.40, 0.50, 0.10] 4residual_mass = [max(0.0, p - q) for p, q in zip(target, draft)] 5normalizer = sum(residual_mass) 6residual = [value / normalizer if normalizer else 0.0 for value in residual_mass] 7 8print(dict(zip(tokens, residual))) 9print(f"positive correction mass: {normalizer:.2f}")
Output
1{'cache': 0.0, 'latency': 1.0, 'batch': 0.0} 2positive correction mass: 0.20

A draft token has draft probability 0.20 and target probability 0.35. What is the acceptance probability?

Answer

It's 1.0 because the target assigns higher probability than the draft. The token is underrepresented by the draft, so accepting it can't over-sample it relative to the target distribution.

Implementation

This PyTorch version keeps speculative decoding small enough to inspect. It requires two components: a pre-trained target model and a smaller, computationally efficient draft model. The algorithm generates KKK draft tokens autoregressively with the small model, concatenates them with the current context, and then validates the entire sequence in a single forward pass through the target model.

This version is intentionally pedagogical: it handles the accept/reject loop and residual sampling, but leaves out production concerns like KV-cache reuse, EOS handling, batching, and logits processors. It assumes Hugging Face-style causal-LM logits, with softmax turning each score vector into probabilities and position jjj predicting token j+1j + 1j+1. In a production sampler, the acceptance test has to use the same post-processed distributions you serve, not a different temperature or top-p configuration.[1]Reference 1Fast Inference from Transformers via Speculative Decoding.https://arxiv.org/abs/2211.17192[2]Reference 2Accelerating Large Language Model Decoding with Speculative Sampling.https://arxiv.org/abs/2302.01318

Why must the acceptance test use the same temperature, top-p, top-k, and logits processors as serving?

Answer

The proof is about the final served distribution. If verification compares raw probabilities while serving samples from a post-processed distribution, the accept/reject math reconstructs the wrong target distribution.

served-distribution-parity.py
1def top_k_normalize(probabilities, k): 2 kept = sorted(range(len(probabilities)), key=probabilities.__getitem__, reverse=True)[:k] 3 total = sum(probabilities[index] for index in kept) 4 return [probabilities[index] / total if index in kept else 0.0 for index in range(len(probabilities))] 5 6raw_target = [0.55, 0.30, 0.15] 7raw_draft = [0.40, 0.35, 0.25] 8served_target = top_k_normalize(raw_target, k=2) 9served_draft = top_k_normalize(raw_draft, k=2) 10token_id = 1 11 12raw_accept = min(1.0, raw_target[token_id] / raw_draft[token_id]) 13served_accept = min(1.0, served_target[token_id] / served_draft[token_id]) 14print(f"raw acceptance: {raw_accept:.3f}") 15print(f"served top-k acceptance: {served_accept:.3f}") 16print("Verification must use served probabilities.")
Output
1raw acceptance: 0.857 2served top-k acceptance: 0.756 3Verification must use served probabilities.
implementation.py
1import torch 2import torch.nn.functional as F 3 4def speculative_decode( 5 target_model: torch.nn.Module, 6 draft_model: torch.nn.Module, 7 input_ids: torch.Tensor, # (1, seq_len) 8 K: int = 5, 9 max_new_tokens: int = 100, 10) -> torch.Tensor: 11 """Pedagogical speculative decoding loop.""" 12 generated = input_ids.clone() 13 14 tokens_generated = 0 15 while tokens_generated < max_new_tokens: 16 step_k = min(K, max_new_tokens - tokens_generated) 17 prompt_len = generated.shape[1] 18 19 # Step 1: Draft step_k tokens autoregressively with the small model. 20 draft_tokens: list[int] = [] 21 draft_probs: list[torch.Tensor] = [] 22 draft_input = generated.clone() 23 24 for _ in range(step_k): 25 with torch.no_grad(): 26 logits = draft_model(draft_input).logits[:, -1, :] # (1, vocab) 27 probs = F.softmax(logits, dim=-1) 28 token = torch.multinomial(probs, num_samples=1) 29 30 draft_tokens.append(token.item()) 31 draft_probs.append(probs.squeeze(0)) 32 draft_input = torch.cat([draft_input, token], dim=-1) 33 34 verify_suffix = torch.tensor( 35 [draft_tokens], 36 device=generated.device, 37 dtype=generated.dtype, 38 ) 39 verify_input = torch.cat([generated, verify_suffix], dim=-1) 40 41 # Step 2: One target pass scores every drafted position at once. 42 with torch.no_grad(): 43 target_logits = target_model(verify_input).logits 44 45 # In Hugging Face causal LMs, position prompt_len - 1 predicts 46 # the first drafted token. 47 for i in range(step_k): 48 pos = prompt_len - 1 + i 49 target_p = F.softmax(target_logits[:, pos, :], dim=-1).squeeze(0) 50 draft_p = draft_probs[i] 51 token_id = draft_tokens[i] 52 53 ratio = (target_p[token_id] / draft_p[token_id]).item() 54 if torch.rand(1).item() < min(1.0, ratio): 55 continue 56 57 residual = torch.clamp(target_p - draft_p, min=0) 58 residual = residual / residual.sum() 59 correction = torch.multinomial(residual, num_samples=1) 60 61 accepted_prefix = torch.tensor( 62 [draft_tokens[:i]], 63 device=generated.device, 64 dtype=generated.dtype, 65 ) 66 generated = torch.cat( 67 [generated, accepted_prefix, correction.unsqueeze(0)], 68 dim=-1, 69 ) 70 tokens_generated += i + 1 71 break 72 else: 73 generated = torch.cat([generated, verify_suffix], dim=-1) 74 tokens_generated += step_k 75 76 # The last logit also predicts one bonus token beyond the draft. 77 if tokens_generated < max_new_tokens: 78 bonus_pos = prompt_len - 1 + step_k 79 bonus_probs = F.softmax(target_logits[:, bonus_pos, :], dim=-1) 80 bonus = torch.multinomial(bonus_probs, num_samples=1) 81 82 generated = torch.cat([generated, bonus], dim=-1) 83 tokens_generated += 1 84 85 return generated

Tracing one step

Suppose input_ids currently contains the tokens for "Explain KV cache". The loop sets step_k = 5 and the draft model autoregressively generates ["reduces", "decode", "latency", "when", "batched"]. The target model then scores all five draft positions in a single forward pass on the concatenated sequence.

If the verifier accepts the first three tokens but rejects the fourth, the code appends ["reduces", "decode", "latency"] plus a correction token sampled from the residual distribution. The loop then resumes from the new prefix, generating another batch of five draft tokens. If all five tokens are accepted, the code appends all five and also samples a bonus token from the last target logit, yielding six new tokens for one target pass.

Why can a speculative round emit K+1 tokens when all K draft tokens are accepted?

Answer

The target pass over the prefix plus K draft tokens also produces logits for the token after the draft. If every drafted token is accepted, the sampler can use that final target logit to sample one bonus token.


Speedup analysis

The expected number of tokens generated per verification step depends on the acceptance rate α\alphaα and the speculation length KKK. A useful back-of-the-envelope model assumes each drafted token is accepted independently with probability α\alphaα. Verification proceeds left to right: every accepted draft token extends the prefix, while the first rejection ends that draft run and inserts a target-model correction.

Under that approximation, the expected tokens per verification round is given by the geometric series (with the convention E=K+1\mathbb{E}=K+1E=K+1 when α=1\alpha=1α=1):

E[tokens per step]=1−αK+11−α\mathbb{E}[\text{tokens per step}] = \frac{1 - \alpha^{K+1}}{1 - \alpha}E[tokens per step]=1−α1−αK+1​

Expected tokens per round

α\alphaα is the per-token acceptance probability and KKK is how many tokens the draft model proposes. When α\alphaα is high, you get close to K+1K+1K+1 tokens per round because many drafted tokens survive and you often collect the bonus token too. When α\alphaα is low, most drafts get rejected early and you fall back toward 1 token per round.

Wall-clock speedup

Wall-clock speedup must also account for the draft cost ratio ccc (draft time relative to a normal target decode step) and for the fact that verification isn't free as KKK or sequence length grows. The textbook form below is a useful first model, not an exact production forecast:

Speedup≈E[tokens per step]cverify(K,s)+K⋅c\text{Speedup} \approx \frac{\mathbb{E}[\text{tokens per step}]}{c_{\text{verify}}(K,s) + K \cdot c}Speedup≈cverify​(K,s)+K⋅cE[tokens per step]​

The classic Leviathan-style simplification sets cverify≈1c_{\text{verify}} \approx 1cverify​≈1 (one target pass ≈ one decode step) and ccc as draft cost relative to that step. That is optimistic when:

  1. Verification attends over KKK query positions on a long prefix, so attention and KV traffic scale with KKK and sequence length sss. Prefer a profiled cverify(K,s)≥1c_{\text{verify}}(K,s) \ge 1cverify​(K,s)≥1, often closer to 1+εK1+\varepsilon K1+εK than to a flat 1.
  2. Continuous batching shares a token budget across streams. Each accepted draft multiplies tokens scheduled that iteration; high KKK can steal slots from other users and worsen multi-tenant ITL even when single-stream speedup looks good.
  3. Real systems also pay for cache growth, kernel launches, sampler parity checks, and tree-attention variants (Medusa/EAGLE), which aren't the classic residual chain alone.

For the first table below we still use cverify=1c_{\text{verify}}=1cverify​=1 so the arithmetic stays readable. Treat those numbers as an optimistic upper-bound sketch to compare against a benchmark, not as promised fleet speedup. The second table and lab re-open cverify=1+εKc_{\text{verify}}=1+\varepsilon Kcverify​=1+εK so you can see how verify cost collapses the win as KKK grows.

Classic serial two-model drafting pays roughly K⋅cK \cdot cK⋅c for KKK sequential draft steps (each step ~ccc target-equivalents). Tree methods (Medusa/EAGLE) replace that chain with parallel heads or a coupled drafter, so proposal cost isn't automatically KcKcKc; profile the method you ship.

Worked example

Use illustrative measured inputs. Suppose a candidate draft path costs 10% of a target pass (c=0.1c = 0.1c=0.1). You set K=5K = 5K=5 and observe an acceptance rate of α=0.8\alpha = 0.8α=0.8 in a benchmark.

Expected tokens per round = (1−0.86)/(1−0.8)=(1−0.262)/0.2≈3.69(1 - 0.8^6) / (1 - 0.8) = (1 - 0.262) / 0.2 \approx 3.69(1−0.86)/(1−0.8)=(1−0.262)/0.2≈3.69 tokens.

Optimistic cost denominator (cverify=1c_{\text{verify}}=1cverify​=1) = 1+5×0.1=1.51 + 5 \times 0.1 = 1.51+5×0.1=1.5 target-equivalent passes.

Optimistic speedup = 3.69/1.5≈2.463.69 / 1.5 \approx 2.463.69/1.5≈2.46x.

The model predicts about 2.5x for those inputs under flat verification. If acceptance changes to 0.6, the same K=5K = 5K=5 model predicts about 1.6x; at 0.9, it predicts about 3.1x. These are model outputs to compare against a benchmark, not promised throughput.

Candidate setupα\alphaαKKKAssume ccccverifyc_{\text{verify}}cverify​Approx. tokens/roundApprox. speedup
Measured path A0.650.112.41.6x
Measured path A0.750.112.92.0x
Measured path A0.850.113.72.5x
Measured path A0.950.114.73.1x
Measured path B0.8580.115.12.8x
Measured path C0.90100.116.93.4x

Now keep α=0.8\alpha = 0.8α=0.8 and c=0.1c = 0.1c=0.1, but set cverify=1+0.05Kc_{\text{verify}} = 1 + 0.05Kcverify​=1+0.05K so verification cost grows with draft depth:

KKKTokens/roundcverify+Kcc_{\text{verify}}+Kccverify​+KcSpeedup vs flat cverify=1c_{\text{verify}}=1cverify​=1
11.801.151.57x (vs 1.64x)
53.691.752.11x (vs 2.46x)
104.572.501.83x (vs 2.29x)

Even with strong acceptance, larger KKK can lose after verify cost is counted. Production sweep tools therefore need cverify(K,s)c_{\text{verify}}(K,s)cverify​(K,s) profiles alongside α\alphaα and ccc.

modeled-speculation-speedup.py
1def expected_tokens(acceptance: float, depth: int) -> float: 2 return sum(acceptance**step for step in range(depth + 1)) 3 4def modeled_speedup( 5 acceptance: float, 6 depth: int, 7 draft_cost: float, 8 verify_cost: float = 1.0, 9) -> float: 10 return expected_tokens(acceptance, depth) / (verify_cost + depth * draft_cost) 11 12for acceptance in (0.6, 0.8, 0.9): 13 estimate = modeled_speedup(acceptance, depth=5, draft_cost=0.1, verify_cost=1.0) 14 print(f"acceptance={acceptance:.1f}: optimistic speedup={estimate:.2f}x") 15 16print("--- growing verify cost, alpha=0.8 ---") 17for depth in (1, 5, 10): 18 verify = 1.0 + 0.05 * depth 19 estimate = modeled_speedup(0.8, depth, draft_cost=0.1, verify_cost=verify) 20 print(f"K={depth}: c_verify={verify:.2f}, modeled speedup={estimate:.2f}x")
Output
1acceptance=0.6: optimistic speedup=1.59x 2acceptance=0.8: optimistic speedup=2.46x 3acceptance=0.9: optimistic speedup=3.12x 4--- growing verify cost, alpha=0.8 --- 5K=1: c_verify=1.05, modeled speedup=1.57x 6K=5: c_verify=1.25, modeled speedup=2.11x 7K=10: c_verify=1.50, modeled speedup=1.83x
Speculative decoding speedup chart showing speculation depth helping only when acceptance stays high, plus a bar chart showing higher acceptance rate producing larger speedup at the same draft cost.
The speedup curve isn't monotonic in practice. Larger speculation depth helps when acceptance is high, but it wastes draft work when the draft is weak or misconfigured.

The useful KKK depends on measured acceptance, draft cost, and serving behavior. A small sweep can begin with single-digit depths, but select from route-specific benchmarks rather than a universal default.

choose-speculation-depth.py
1def modeled_speedup(acceptance, depth, draft_cost, verify_cost=1.0): 2 expected = sum(acceptance**step for step in range(depth + 1)) 3 return expected / (verify_cost + depth * draft_cost) 4 5measurements = {"acceptance": 0.72, "draft_cost": 0.12} 6candidates = { 7 depth: modeled_speedup( 8 measurements["acceptance"], 9 depth, 10 measurements["draft_cost"], 11 verify_cost=1.0 + 0.05 * depth, 12 ) 13 for depth in (1, 3, 5, 8) 14} 15best_depth = max(candidates, key=candidates.get) 16print({depth: round(value, 3) for depth, value in candidates.items()}) 17print(f"model-selected K to benchmark: {best_depth}")
Output
1{1: 1.47, 3: 1.73, 5: 1.662, 8: 1.435} 2model-selected K to benchmark: 3

With growing cverifyc_{\text{verify}}cverify​, the same acceptance and draft cost prefer a smaller KKK than the flat-cverify=1c_{\text{verify}}=1cverify​=1 sketch (which selected K=5K=5K=5). Always re-rank depths under the profiled verify curve.

Continuous batching token budget

Single-stream speedup can still hurt multi-tenant ITL. Suppose a continuous-batching engine has target-verification budget BBB per iteration and SSS active streams. Without speculation, each stream schedules one target position, so target work is about SSS. At speculation depth KKK, the target verifies all KKK proposed positions in parallel even if rejection happens early. Target-verification work is therefore about SKS KSK, while expected committed output and KV growth is about S(1+aˉK)S(1 + \bar{a}K)S(1+aˉK) for accepted fraction aˉ\bar{a}aˉ. Keep those budgets separate: acceptance changes committed growth, but it doesn't erase target verification of proposed positions. If either target work or KV-append capacity is exceeded, some streams wait or shrink and multi-tenant inter-token latency rises even while the speculative route's solo benchmark looks fast.

multi-tenant-token-budget.py
1target_token_budget_b = 64 2kv_append_budget_b = 64 3streams = 16 4accepted_fraction = 0.6 # mean fraction of K that survives before reject 5for depth in (0, 2, 4, 8): 6 target_work = streams * depth if depth else streams 7 expected_commits = streams * (1 + accepted_fraction * depth) if depth else streams 8 fit = target_work <= target_token_budget_b and expected_commits <= kv_append_budget_b 9 print( 10 f"K={depth}: target={target_work:.0f}/{target_token_budget_b} " 11 f"commits≈{expected_commits:.0f}/{kv_append_budget_b} " 12 f"({'fits' if fit else 'OVER budget: multi-tenant ITL risk'})" 13 )
Output
1K=0: target=16/64 commits≈16/64 (fits) 2K=2: target=32/64 commits≈35/64 (fits) 3K=4: target=64/64 commits≈54/64 (fits) 4K=8: target=128/64 commits≈93/64 (OVER budget: multi-tenant ITL risk)

If acceptance drops from 0.8 to 0.5, should you usually increase K first?

Answer

No. Low acceptance means drafts are rejected early, so a larger K mostly adds draft work that never survives. First check tokenizer, sampler, prompt distribution, and draft-model alignment.


Draft model choices

The draft mechanism determines your acceptance rate and your overall speedup. It requires a balance: if the draft is too weak, it gets rejected constantly. If it's too expensive, it erases the latency gains from verification. In practice, most systems use one of these families:

ApproachDraft sourceMain advantageMain trade-off
Smaller same-family modelSeparate assistant model with the same tokenizerSimple exact speculative-decoding setupExtra model to load and schedule
Medusa heads[4]Reference 4Medusa: Simple LLM Inference Acceleration Framework with Multiple Decoding Heads.https://arxiv.org/abs/2401.10774Extra heads attached to the target modelNo separate model at inference timeNeeds extra training and tree verification
EAGLE / EAGLE-3[5]Reference 5EAGLE: Speculative Sampling Requires Rethinking Feature Uncertainty.https://arxiv.org/abs/2401.15077[6]Reference 6EAGLE-3: Scaling up Inference Acceleration of Large Language Models via Training-Time Testhttps://arxiv.org/abs/2503.01840Target-coupled speculator over hidden states or direct token headsStrong proposals without a full second modelMore integration complexity
MTP heads[7]Reference 7Speculative Decodinghttps://docs.vllm.ai/en/latest/features/speculative_decoding/Checkpoint-native multi-token prediction modulesNo separate assistant when supportedRequires checkpoint and engine support
Prompt Lookup[7]Reference 7Speculative Decodinghttps://docs.vllm.ai/en/latest/features/speculative_decoding/Reuse repeated n-grams from contextNo extra model or trainingOnly helps when the context repeats itself
Suffix decoding[7]Reference 7Speculative Decodinghttps://docs.vllm.ai/en/latest/features/speculative_decoding/Reuse matching suffixes from previous outputsNo extra modelFit depends on reusable prior output patterns
Comparison table of six speculative decoding methods: separate draft model, Medusa, EAGLE, MTP, prompt lookup, and suffix decoding, with each draft source and operational trade-off.
Speculative methods differ in where proposals come from and what they cost to deploy. Target verification remains the final authority, while proposal shape and engine integration vary.

In the classic direct-token draft setup, require the separate draft model to share the exact same tokenizer as the target model. If token IDs map to different subwords, the target isn't verifying the intended candidate sequence; reject that pairing or use a method that explicitly supports different tokenizers.

tokenizer-compatibility-gate.py
1draft_vocab = {"return": 14, " label": 88, " expires": 103} 2target_vocab = {"return": 14, " label": 88, " expires": 104} 3required_pieces = ["return", " label", " expires"] 4 5mismatches = [ 6 piece for piece in required_pieces 7 if draft_vocab.get(piece) != target_vocab.get(piece) 8] 9print(f"token-id mismatches: {mismatches}") 10print(f"direct draft path allowed: {not mismatches}")
Output
1token-id mismatches: [' expires'] 2direct draft path allowed: False

What is the core draft-model trade-off?

Answer

The draft must be cheap enough that K draft passes cost far less than one target pass, but accurate enough that many proposed tokens are accepted. Too weak lowers acceptance; too large erases the saved latency.

Medusa: multi-head speculative decoding

Medusa avoids the need for a separate draft model entirely. Instead, it adds extra prediction heads to the target model itself:

Each Medusa head predicts a different future position from the same hidden state. That changes the draft shape from one sequence to a tree of possible continuations. Since each head can propose multiple candidates, the candidates form a tree structure rather than a single chain. A specialized tree attention mechanism then evaluates all these candidate paths simultaneously in a single forward pass, filters out incorrect branches, and keeps the longest accepted path. The result avoids the overhead and complexity of loading and orchestrating a separate draft model.

The method table identifies future-token heads as Medusa's draft source. Those heads fan out from a shared hidden state into a candidate tree that the target verifies in parallel.

EAGLE drafts from target-model internals instead of using a separate full assistant. Earlier EAGLE variants predict feature states, which can raise acceptance because those states carry more information than a plain token-only head.[5]Reference 5EAGLE: Speculative Sampling Requires Rethinking Feature Uncertainty.https://arxiv.org/abs/2401.15077 EAGLE-3 moves further in that direction: the paper switches to direct token prediction, fuses low, middle, and high target-model layers, and trains the drafter with a training-time-test loop on its own outputs. The paper reports up to 6.5x speedup in its evaluation setup, but exact gains still depend on engine support, batch shape, and prompt mix.[6]Reference 6EAGLE-3: Scaling up Inference Acceleration of Large Language Models via Training-Time Testhttps://arxiv.org/abs/2503.01840 Current serving docs such as vLLM expose EAGLE-family speculation, but the exact options and caveats change quickly.[7]Reference 7Speculative Decodinghttps://docs.vllm.ai/en/latest/features/speculative_decoding/

Why do Medusa-style heads remove one major deployment burden of classic speculative decoding?

Answer

They avoid loading and scheduling a separate draft model. The target model gains extra heads that propose future tokens from its own hidden state, then tree verification decides which path survives.

Prompt lookup decoding

Prompt Lookup Decoding (PLD) takes a completely different approach. Instead of using any neural model for drafting, it searches the current context window for matching n-grams and reuses them as draft tokens. This non-neural method works surprisingly well for tasks with repeated text or pattern matching.

AspectHow it works
Draft sourceMatch n-grams from the prompt/context window
Candidate workloadsCode completion, summarization, repetitive text
Memory overheadNo extra model weights
Main failure modeLittle benefit when the context has little repetition

The algorithm scans the context window for n-grams (typically 3-5 tokens) that match the end of the currently generated sequence. When it finds a match, it looks at what token followed that n-gram earlier in the context window and uses that as the next draft token. For example, if the model has generated "timeout error" and the context contains "timeout error repeats Friday," PLD proposes "repeats" as the next draft token.

PLD is a candidate for code generation and other repetitive tasks because variable names, function calls, and boilerplate often reappear within the context window.[7]Reference 7Speculative Decodinghttps://docs.vllm.ai/en/latest/features/speculative_decoding/

The key advantage is simplicity: there's no model to load, no training required, and no extra model weights. PLD can be combined with other speculative methods or used as a fallback when no neural draft model is available.

prompt-lookup-candidate.py
1context = "timeout error repeats Friday. auth callback needs review. timeout error" 2tokens = context.split() 3suffix = ["timeout", "error"] 4 5proposal = None 6for index in range(len(tokens) - len(suffix)): 7 if tokens[index:index + len(suffix)] == suffix: 8 proposal = tokens[index + len(suffix)] 9 break 10 11print(f"matched suffix: {' '.join(suffix)}") 12print(f"lookup proposal: {proposal}")
Output
1matched suffix: timeout error 2lookup proposal: repeats

Why is Prompt Lookup Decoding often strong for code completion but weak for open-ended creative writing?

Answer

Code often repeats identifiers, imports, call patterns, and boilerplate already present in context. Creative writing is less likely to contain exact reusable n-gram continuations, so lookup drafts have fewer high-quality proposals.


Production deployment

Adding speculative decoding to a production system isn't an automatic win. On paper, the idea looks clean; in production, workload characteristics decide. For the classic two-model draft-and-verify setup, the core trade-off is simple: you spend extra FLOPs on the draft path to save target-model memory bandwidth. Modern serving stacks now expose several speculation families, so the payoff is method-specific rather than a blanket yes-or-no.[7]Reference 7Speculative Decodinghttps://docs.vllm.ai/en/latest/features/speculative_decoding/ Deploy blindly and you may decrease overall throughput and increase costs.

When the classic draft-model setup helps (and when it doesn't)

Before rolling out a separate draft model, check whether your workload benefits from the draft-then-verify cycle. The technique is a trade-off: it burns additional compute (FLOPs) to save memory bandwidth. If your system is already compute-bound, this trade-off will usually backfire and reduce overall throughput.

ScenarioHypothesis before benchmarkWhy test it
Single-user, low-batch inferenceStrong candidateTarget decode may be memory-bandwidth bound
Throughput-maximized batchingMeasure carefullyExtra draft work can compete with saturated compute
Long outputsCandidateMore decode steps can amortize setup
Very short outputsWeak candidateSetup and drafting may dominate
Repetitive outputs (code, templates)CandidateDraft or lookup acceptance may be higher
Diverse outputsMeasure carefullyAcceptance may vary with sampling and prompt mix
Production speculative-decoding rollout flow from baseline through method-and-depth sweep and route canary to rollout, paired with a traffic-fit table for low-QPS, long-output, repetitive, short-output, and high-QPS workloads.
Use traffic fit to choose canary candidates, then measure the baseline, sweep method and depth, and promote only routes whose latency and cost gates pass.

Current vLLM docs frame speculation as an inter-token-latency optimization for medium-to-low QPS, memory-bound workloads. This targets the gap between emitted tokens, not TTFT; drafting setup can even add a little first-token work. The same guide separates draft-model, EAGLE, MTP, n-gram, suffix, and other proposer paths, with method-specific latency-versus-throughput trade-offs.[7]Reference 7Speculative Decodinghttps://docs.vllm.ai/en/latest/features/speculative_decoding/

When is classic draft-model speculation most likely to help production serving?

Answer

It is most likely to help medium-to-low QPS, memory-bound, latency-sensitive decode where the target has idle compute between weight reads. It is less reliable in high-QPS, large-batch serving that is already compute-heavy.

Serving-engine reality

Serving-engine support changes quickly. For example, vLLM's current docs list several speculation families, but they also call out known feature incompatibilities and separate theoretical losslessness from what you can expect under real hardware numerics.[7]Reference 7Speculative Decodinghttps://docs.vllm.ai/en/latest/features/speculative_decoding/ Treat framework support as an operational detail you must validate in your own stack, not as a timeless property of the algorithm.

The practical rollout loop is usually straightforward:

  1. Measure baseline TTFT, inter-token latency, and tokens-per-second throughput separately.
  2. Sweep the draft mechanism and speculation depth on real prompts, not toy strings.
  3. Track acceptance rate, output length, and p95 latency by workload class.
  4. Keep a non-speculative fallback path for peak-QPS periods or incompatible features.
speculation-canary-gate.py
1baseline = {"p95_itl_ms": 46.0, "throughput_tps": 380, "sampler_parity": True} 2canary = {"p95_itl_ms": 29.0, "throughput_tps": 372, "sampler_parity": True} 3minimum_throughput_ratio = 0.95 4 5promote = ( 6 canary["sampler_parity"] 7 and canary["p95_itl_ms"] < baseline["p95_itl_ms"] 8 and canary["throughput_tps"] >= baseline["throughput_tps"] * minimum_throughput_ratio 9) 10print(f"inter-token latency improved: {canary['p95_itl_ms'] < baseline['p95_itl_ms']}") 11print(f"canary promoted: {promote}")
Output
1inter-token latency improved: True 2canary promoted: True

Which metrics should you split by workload class before deciding speculation is working?

Answer

Track acceptance rate, inter-token latency, TTFT, output length, throughput, cost per output token, and fallback rate by workload class. A global average can hide that code improves while creative chat regresses.

When speculation backfires

Speculative decoding isn't a universal speedup button. These three failure modes are common enough to check explicitly.

SymptomLikely causeFix
Speedup is near 1x or negativeDraft model is too slow or too inaccurate (low acceptance rate)Benchmark a smaller or better-aligned draft, or switch to Prompt Lookup for repetitive tasks
Correctness checks failAcceptance test uses different temperature or top-p than the served modelEnsure the verifier and the sampler share the exact same post-processed distribution
Memory usage spikes unexpectedlyKV cache wasn't truncated after a rejected tokenImplement cache rewind so rejected draft tokens don't persist in the cache

What target-model state must be restored after a speculative rejection?

Answer

The target KV cache must be rewound to the accepted prefix before appending the correction token. Keeping KV entries for rejected draft tokens corrupts subsequent attention.


Gate speculation on a real traffic slice

Replay representative prompt and output lengths through baseline and speculative paths with identical sampling settings. Verify output-distribution parity and KV-cache rewind first, then compare acceptance rate, TTFT, inter-token latency, throughput, and cost by workload class. Canary only routes that improve their latency target without breaking the throughput floor, and keep a non-speculative fallback for rejection-heavy or incompatible traffic.

Complete the lesson

Mastery Check

Answer every question, then check your score. Score 75% or higher to mark this lesson complete.

1.A low-batch target decode is dominated by streaming the target weights for each new token. A cheap draft path proposes five tokens and the target verifies the chunk in one pass. What condition makes speculative decoding a latency win without changing the theoretical output distribution?

Correct answer: The accepted-token savings from fewer target decode calls must outweigh draft and verification overhead, while the accept/reject sampler reconstructs the target distribution.

Speculation trades extra cheap draft work for fewer expensive target decode passes per emitted token. It is only useful when verification amortizes target weight movement across accepted positions, and it remains distributionally exact only when the modified rejection sampler is implemented against the served target distribution.

2.Using the optimistic back-of-the-envelope model with acceptance rate 0.8, speculation depth K = 5, draft cost c = 0.1, and c_verify = 1, what speedup is predicted? If instead c_verify = 1 + 0.05K, what happens?

Correct answer: About 2.46x with c_verify = 1; with c_verify = 1.25 the same alpha and K fall to about 2.11x, so growing verify cost can erase depth gains.

Expected tokens per round are (1 - 0.8^6) / (1 - 0.8) ≈ 3.69. With c_verify = 1 the cost is 1 + 50.1 = 1.5, so 3.69/1.5 ≈ 2.46x. With c_verify = 1 + 0.055 = 1.25 the cost is 1.75, so 3.69/1.75 ≈ 2.11x. Flat c_verify = 1 is an optimistic sketch.

3.What does teacher forcing mean during speculative verification?

Correct answer: The target model is given the proposed draft tokens as known inputs and scores all draft positions in one forward pass instead of choosing those tokens one at a time.

In verification, the target isn't generating the drafted tokens autoregressively. It conditions on the prefix plus the proposed draft sequence and computes the target probabilities for each drafted position in parallel, which is what lets one target pass amortize work across the chunk.

4.A round drafts ['ships', 'from', 'Seattle', 'on', 'Friday']. The target accepts the first three tokens and rejects 'on'. What should happen next?

Correct answer: Discard 'Friday' and any later draft tokens, sample a correction for 'on' from the residual distribution, and rewind the target KV cache to the accepted prefix before appending the correction.

Later draft tokens were generated conditioned on the rejected token. Once that token is replaced, the prefix changes, so the later draft tokens and any KV-cache entries derived from them no longer describe the sequence that will continue.

5.At one verification position, the draft distribution q and target distribution p over [cache, latency, batch] are q = [0.60, 0.30, 0.10] and p = [0.40, 0.50, 0.10]. The drafted token is 'cache'. What happens under the accept/reject rule?

Correct answer: Accept 'cache' with probability 2/3; if it's rejected, the residual sampler chooses 'latency' with probability 1.

The acceptance probability is min(1, p(cache) / q(cache)) = min(1, 0.40 / 0.60) = 2/3. The residual uses max(0, p - q), which is [0, 0.20, 0], so after normalization all correction mass goes to 'latency'.

6.Why must the acceptance test use the same temperature, top-p, top-k, and logits processors as serving?

Correct answer: The proof is about the final served distribution. If verification compares raw probabilities while serving samples from a post-processed distribution, the accept/reject math reconstructs the wrong target distribution.

Modified rejection sampling reconstructs whichever distributions are used in the ratio and residual. Temperature, top-p, top-k, and logits processors can change probabilities, support, and normalization, so using raw probabilities while serving processed probabilities preserves the wrong distribution.

7.A deployment is evaluating three speculative paths: a repetitive code-completion route with no extra model budget, a neural proposer path that can add trained target-coupled components but can't load a second assistant, and a classic direct-token assistant path. Which plan matches those constraints?

Correct answer: Use Prompt Lookup for repetitive code, Medusa or EAGLE-style target-coupled speculation for the no-assistant neural route, and require exact tokenizer IDs for the classic assistant path.

Prompt Lookup uses repeated n-grams from the current context and adds no model weights, which fits repetitive code with no extra model budget. Medusa and EAGLE-style approaches avoid a separate full assistant by using target-attached heads or target-coupled internals, but they require training or integration. Classic direct-token draft-model speculation requires the exact same tokenizer IDs, because otherwise the target verifies a different candidate sequence.

8.A speculation canary has sampler parity, lowers p95 inter-token latency from 46 ms to 29 ms, but drops throughput from 380 to 340 tokens/s. The rollout gate requires at least 95% of baseline throughput. What should happen?

Correct answer: Do not promote it yet, because 340 tokens/s is below the 361 tokens/s throughput floor despite the latency improvement.

The throughput floor is 0.95 * 380 = 361 tokens/s. The canary improves inter-token latency and has sampler parity, but 340 tokens/s misses the configured throughput gate, so it should not be promoted without further tuning or route-specific gating.

9.A classic assistant path shows 0.5 acceptance at K = 3 and only 1.1x measured speedup. Which next step is justified?

Correct answer: Check tokenizer and prompt-format alignment, sampler parity, acceptance by route, and draft cost before changing K.

At low acceptance, the useful accepted prefix is short, so increasing K mostly adds proposals that are discarded after an early rejection. A larger drafter may improve alignment but also increases c, which can erase the gain. First rule out tokenizer, prompt-format, and sampler mismatches, then use route-specific acceptance and draft-cost measurements to choose a method or K.

9 questions remaining.

Next Step
Continue to Long Context Window Management

Speculative decoding exploits the memory wall in single-token decode; long contexts push that wall even harder because the KV cache grows with every token and attention costs rise. The next chapter covers that growth directly: KV-cache math, <span data-glossary="prefill">prefill</span>-vs-decode trade-offs, <span data-glossary="rope">RoPE</span> scaling, and when to use long-context inference versus retrieval augmentation.

PreviousSLM Specialization & Edge Deployment
Share this article
XFacebookLinkedInBlueskyRedditHacker NewsEmail
References

Fast Inference from Transformers via Speculative Decoding.

Leviathan, Y., Kalman, M., & Matias, Y. · 2023 · ICML 2023

https://arxiv.org/abs/2211.17192

Accelerating Large Language Model Decoding with Speculative Sampling.

Charlie Chen, Sebastian Borgeaud, Geoffrey Irving, Jean-Baptiste Lespiau, Laurent Sifre, John Jumper · 2023

https://arxiv.org/abs/2302.01318

Qwen3.6-27B

Qwen Team · 2026

https://huggingface.co/Qwen/Qwen3.6-27B

Medusa: Simple LLM Inference Acceleration Framework with Multiple Decoding Heads.

Cai, T., et al. · 2024 · ICML 2024

https://arxiv.org/abs/2401.10774

EAGLE: Speculative Sampling Requires Rethinking Feature Uncertainty.

Li, Y., et al. · 2024 · ICML 2024

https://arxiv.org/abs/2401.15077

EAGLE-3: Scaling up Inference Acceleration of Large Language Models via Training-Time Test

Li, Y., Wei, F., Zhang, C., & Zhang, H. · 2025

https://arxiv.org/abs/2503.01840

Speculative Decoding

vLLM Team · 2026 · vLLM Documentation

https://docs.vllm.ai/en/latest/features/speculative_decoding/

Discussion

Questions and insights from fellow learners.

Discussion loads when you reach this section.