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 196 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/9
Gradients and BackpropVectors, Matrices & TensorsLinear Algebra for MLAdam, Momentum, SchedulersProbability for Machine LearningBayesian Inference from EvidenceStatistics 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/15
Linear Regression from ScratchLogistic Regression and MetricsDecision Trees, Forests, and BoostingReinforcement Learning BasicsValidation and LeakageStatistical Learning and GeneralizationConformal Prediction and UncertaintyClustering and PCAProbabilistic Generative ModelsCore Retrieval AlgorithmsDecoding AlgorithmsExperiment Design and A/B TestingCausal Inference and CounterfactualsPyTorch 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/25
Dimensionality Reduction for EmbeddingsCoT, ToT & Self-Consistency PromptingFunction Calling & Tool UseMCP & Tool Protocol StandardsContext EngineeringPrompt Injection DefenseResponsible AI GovernancePrivacy-Preserving Machine LearningData 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/17
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 TuningTraining Run OperationsReward 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/31
Inference: TTFT, TPS & KV CacheMulti-Query & Grouped-Query AttentionKV Cache & PagedAttentionPrefix Caching and Prompt CachingFlashAttention & Memory EfficiencyContinuous Batching & SchedulingScaling LLM InferenceModel Parallelism for LLM InferenceAccelerator Architecture Field GuideCorrect GPU Profiling and BenchmarkingGPU Kernel Performance Engineering LabGPU Parallel Primitives LabGPU Compilers and Kernel LanguagesGEMM From Naive CUDA to Tensor CoresLow-Precision GPU ProgrammingGPU Runtime Execution LabAI-Generated Kernel EngineeringModel 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 & AutoscalingGPU Failure OperationsDistributed Inference Data PlaneServing Benchmark EngineeringA/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.

33 min read
Learning path
Step 156 of 196 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.

Consider an on-call alert: inter-token latency has crossed its service-level objective (SLO) while time to first token (TTFT) still looks healthy. Prompt prefill is finished, so the problem sits between streamed tokens. On a low-batch route, the large target rereads weights and KV state for each next-token step. Product wants faster streaming without changing target distribution. Which work can run ahead safely?

The previous chapter treated a small language model as the product: it had to fit the device and do the job itself. Here, a small language model or another cheap proposal path gets a narrower job. It proposes tokens; a larger target model remains the authority.

The draft proposes the next few tokens, then the target scores that known span in one pass. Agreement lets several tokens survive. A disagreement stops the draft at its first mismatch, and the target supplies the correction. That is speculative decoding: fewer target decode calls per emitted token, with the target distribution preserved by modified rejection sampling up to hardware numerical effects.[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 trade-off is concrete. Speculation targets low-batch LLM inference that is memory-bandwidth bound; draft work and verification overhead still have to fit the route's latency and throughput budget.

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.


Why one decode step waits on memory

Start with the dependency. An LLM can't commit token t+1t+1t+1 until token ttt exists, so serial decode exposes one target call for every output token. Prefill has many known positions to process together; decode has one new position per step. Speculation attacks that decode loop, not first-token work.

The arithmetic of billions of parameters isn't the first suspect. Modern GPUs are fast at matrix multiplication, but low-batch decode often leaves compute units waiting for weights and cache state to arrive from memory. Transformer sampling is often limited by memory bandwidth, so time to emit one token tracks parameter traffic more than leftover FLOPs.[3]Reference 3Fast Transformer Decoding: One Write-Head is All You Need.https://arxiv.org/abs/1911.02150[2]Reference 2Accelerating Large Language Model Decoding with Speculative Sampling.https://arxiv.org/abs/2302.01318 Workload and hardware still decide the bottleneck, so profile before choosing an optimization.

Arithmetic intensity

Arithmetic intensity measures work per byte moved from memory. If one weight load lets the GPU verify 100 candidate tokens, intensity is high. If each load verifies one token, compute units spend more time waiting for the next 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

A high ratio says each byte carries more math; a low ratio points back to memory traffic as the likely limiter.

During autoregressive decoding, generating one token with a model of PPP parameters in FP16 or BF16 (2 bytes per parameter) requires, in a weight-only first-order model:

  • Compute: ≈2P\approx 2P≈2P FLOPs (a matrix-vector multiply against the active weights)
  • Memory: ≈2P\approx 2P≈2P bytes loaded (those same weights)

Divide and you get an arithmetic intensity of ~1 FLOP/byte in that weight-only model. Chen et al. use the same first-order picture: sampling time tracks parameter size divided by memory bandwidth.[2]Reference 2Accelerating Large Language Model Decoding with Speculative Sampling.https://arxiv.org/abs/2302.01318 Real decode also pays for KV-cache traffic, activations, kernels, scheduling, and batching, so profile the engine before declaring a bottleneck.

For an illustrative dense Qwen3.6-27B BF16 target, the round 27×109×227 \times 10^9 \times 227×109×2 diagnostic is 54 GB of weights.[4]Reference 4Qwen3.6-27Bhttps://huggingface.co/Qwen/Qwen3.6-27B Published checkpoints can be a bit larger because the true parameter count is not a round 27.0B and extra tensors ship with the card. Plug in deployed accelerator bandwidth before comparing serial decode with verification. The example uses 3,350 GB/s, the H100 SXM HBM3 figure, as a labeled hardware input, not a measured request time.[5]Reference 5H100 GPUhttps://www.nvidia.com/en-us/data-center/h100/

weight-streaming-diagnostic.py
1params_b = 27 2bytes_per_weight = 2 3h100_sxm_hbm3_gbs = 3_350 # NVIDIA H100 SXM spec; replace with the deployed device 4weights_gb = params_b * bytes_per_weight 5weight_stream_ms = weights_gb / h100_sxm_hbm3_gbs * 1_000 6 7print(f"round BF16 weight footprint: {weights_gb} GB") 8print(f"weight-only read time at {h100_sxm_hbm3_gbs} GB/s: {weight_stream_ms:.1f} ms") 9print("This is a diagnostic lower bound, not measured request latency.")
Output
1round BF16 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
Two charts for a five-token span: serial decode pays five target weight reads at about 1 FLOP/byte, while one speculative verification pass pays one weight read and raises weight-only intensity to about 5 FLOP/byte.
Count the target weight reads first. Serial decode pays one read per token. One verification pass scores the drafted chunk after a single read, which is why the weight-only intensity climbs from about 1 FLOP/byte toward K.

That arithmetic gives a hypothesis, not a verdict. Measure target decode across served batch sizes, concurrency, precisions, and context lengths; weight traffic is often dominant 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

Take a five-token draft. Serial decode asks target to move its weights five times. Verification feeds those five known positions to target together, so one target pass can replace several serial calls when its extra query and KV work stays small. The target still scores every proposed position, even if the sampler later keeps only a prefix.

That use of known proposed tokens is teacher forcing: target scores a candidate sequence in parallel rather than choosing each token one by one. The draft pays cheap sequential work up front. The exchange is favorable only when target weight movement saved by verification exceeds draft, sampler, and cache costs.

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

One speculative round from prefix The model: the draft proposes serves, with, and cache. q is draft probability and p is target probability. The target accepts serves and with, rejects cache, and samples latency from the residual as the correction.
Trace left to right from the committed prefix "The model". The draft proposes serves, with, and cache. q is the draft probability and p is the target probability. The target keeps the first two, rejects cache, and samples latency from the residual so the emitted span is "The model serves with latency".

Read the figure as one round you can check by hand. Prefix is "The model"; draft is serves with cache. The target accepts first two tokens, rejects cache, and samples latency as correction. If every draft token survives, the same target pass has logits for the next position, so sampler can emit one extra bonus token. A rejection samples from residual and rewinds KV cache.

Diagram showing Draft K tokens, Target scores the chunk, First reject?, and no.
Draft K tokens, Target scores the chunk, First reject?, and no.

Drafting is sequential on the cheap path. Verification is one target forward pass over the prefix plus proposed tokens. The first rejection stops the round because later draft tokens were generated on a prefix that no longer exists.

Accept/reject criterion

At each proposed position, the target and draft must score the same token under the same prefix: committed history plus any earlier accepted draft tokens. That shared condition makes their probabilities comparable.

If the target assigns at least as much probability as the draft, the verifier always accepts. If the target assigns less, verifier accepts with the ratio of target probability to draft probability; otherwise it rejects and samples a correction.

The running round uses these probabilities:

PositionDraft tokenDraft qqqTarget pppVerdict
1serves0.400.60Accept (target likes it more)
2with0.350.40Accept (target likes it more)
3cache0.600.40Accept with probability 0.40/0.60=2/30.40/0.60 = 2/30.40/0.60=2/3

serves and with are guaranteed accepts because target probability is higher. cache is accepted with probability 2/32/32/3 because target probability is lower. This illustrated draw rejects it, so verifier stops and samples a correction from residual. There is no later draft token after cache in this K=3K=3K=3 example; with one, the same stop rule would discard it.

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

The construction preserves the target distribution exactly in the mathematical sampler. The accepted branch contributes overlap between distributions; the residual sampler contributes missing mass. Together they reconstruct 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 target wanted more than the draft predicted. At the rejected cache position, the draft put 60% on cache and 30% on latency. The target wanted 40% and 50%. That extra 20% on latency is the residual pool. Tokens where the draft was already too generous (cache) get zero residual probability.

Three bar charts at the rejected cache position: draft q puts 60% on cache, 30% on latency, and 10% on batch; target p puts 40%, 50%, and 10%; residual max(0, p-q) is entirely on latency at +20%.
At the rejected cache position, compare draft q, target p, and max(0, p-q). Shared mass can be accepted. The correction sampler draws only from the +20% on latency, so after normalization the residual is a point mass on latency.

This is modified rejection sampling. The accepted branch contributes min⁡(pdraft(t),ptarget(t))\min(p_{\text{draft}}(t), p_{\text{target}}(t))min(pdraft​(t),ptarget​(t)); the residual sampler contributes 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 them 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] 4overlap = [min(p, q) for p, q in zip(target, draft)] 5residual_mass = [max(0.0, p - q) for p, q in zip(target, draft)] 6normalizer = sum(residual_mass) 7residual = [value / normalizer if normalizer else 0.0 for value in residual_mass] 8reconstructed = [left + right for left, right in zip(overlap, residual_mass)] 9 10print(dict(zip(tokens, residual))) 11print(f"positive correction mass: {normalizer:.2f}") 12print(f"overlap plus residual equals target: {reconstructed == target}")
Output
1{'cache': 0.0, 'latency': 1.0, 'batch': 0.0} 2positive correction mass: 0.20 3overlap plus residual equals target: True

A draft token has draft probability 0.60 and target probability 0.40. What is the acceptance probability?

Answer

It's 0.40/0.60=2/30.40/0.60 = 2/30.40/0.60=2/3. The draft over-represented the token, so the verifier keeps it only with probability equal to the ratio. If it's rejected, the residual sampler draws from tokens the target wanted more than the draft did.

Implementation

The loop below mirrors Leviathan's Algorithm 1 with tiny probability tables, so you can run it without a GPU. A production engine still has two model calls per round: the draft proposes KKK tokens autoregressively, then the target scores the prefix plus those tokens in one forward pass. In Hugging Face-style causal LMs, softmax turns each logits vector into probabilities, and position jjj predicts token j+1j+1j+1, so the row at prompt_len - 1 scores the first drafted token.

Before reading code, predict its trace: keep serves and with, reject cache, then draw latency from residual. The sampler must use the same post-processed distributions you serve, not a different temperature or top-p configuration. KV-cache rewind, EOS, batching, and logits processors stay out of this toy.[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.
speculative-round.py
1from random import Random 2 3VOCAB = ("cache", "latency", "batch") 4 5def residual_mass(target, draft): 6 return [max(0.0, p - q) for p, q in zip(target, draft)] 7 8def normalize(mass): 9 total = sum(mass) 10 if total == 0.0: 11 raise ValueError("residual has no positive mass") 12 return [value / total for value in mass] 13 14def sample(probs, rng): 15 draw = rng.random() 16 cumulative = 0.0 17 for token, prob in zip(VOCAB, probs): 18 cumulative += prob 19 if draw < cumulative: 20 return token 21 return VOCAB[-1] 22 23def verify_draft(draft_tokens, token_q, token_p, position_q, position_p, rng): 24 accepted = [] 25 for index, token in enumerate(draft_tokens): 26 accept_p = min(1.0, token_p[index] / token_q[index]) 27 if rng.random() < accept_p: 28 accepted.append(token) 29 continue 30 residual = normalize(residual_mass(position_p, position_q)) 31 correction = sample(residual, rng) 32 return accepted + [correction], "reject" 33 bonus = sample(position_p, rng) 34 return accepted + [bonus], "bonus" 35 36draft_tokens = ["serves", "with", "cache"] 37token_q = [0.40, 0.35, 0.60] 38token_p = [0.60, 0.40, 0.40] 39# Full distributions at the rejected cache position. 40position_q = [0.60, 0.30, 0.10] 41position_p = [0.40, 0.50, 0.10] 42overlap = [min(p, q) for p, q in zip(position_p, position_q)] 43reconstructed = [left + right for left, right in zip(overlap, residual_mass(position_p, position_q))] 44 45emitted, outcome = verify_draft( 46 draft_tokens, token_q, token_p, position_q, position_p, Random(1) 47) 48 49print(f"emitted: {' '.join(emitted)}") 50print(f"outcome: {outcome}") 51print(f"reconstructed target: {reconstructed == position_p}") 52assert emitted == ["serves", "with", "latency"] 53assert outcome == "reject" 54assert reconstructed == position_p
Output
1emitted: serves with latency 2outcome: reject 3reconstructed target: True

Tracing one step

The prefix is already "The model". The draft proposes serves, with, cache. With seed 1, verifier accepts first two because target assigns each at least as much probability as draft, then rejects cache (r=0.76>2/3r = 0.76 > 2/3r=0.76>2/3). Residual at that position is a point mass on latency, so emitted text is "serves with latency". Any later draft tokens would be discarded; K=3K=3K=3 has none.

If every drafted token is accepted, the same target pass has logits for the next position, so sampler emits one bonus token and the round returns K+1K+1K+1 tokens. In a real engine, rejection also rewinds target KV cache to accepted prefix before appending correction. Leaving rejected keys in cache would make later attention read tokens that are not in sequence.

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

Once exactness is clear, ask how much target work one round replaces. The expected tokens per verification step depends on acceptance rate α\alphaα and speculation length KKK. Start with a deliberately simple model: each drafted token is accepted independently with probability α\alphaα. Verification still runs left to right, so each accepted token extends the prefix and first rejection ends the draft run with a target 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 the number of proposals. The sum counts one token for a first rejection or bonus, then one more token for each accepted position. High α\alphaα moves the result toward K+1K+1K+1 tokens per round; low α\alphaα leaves the result near one.

Wall-clock speedup

Tokens per round are not wall-clock speedup. Include draft cost ratio ccc (draft time relative to normal target decode step) and verify cost, which grows as KKK or sequence length grows. The model below is a first prediction, not a 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's 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 such as Medusa and EAGLE replace that chain with parallel heads or a coupled drafter, so proposal cost is not automatically KcKcKc; profile the method you ship.

Worked example

Use explicit model inputs, not measured results. Suppose a candidate draft path costs 10% of a target pass (c=0.1c = 0.1c=0.1), set K=5K = 5K=5, and plug in acceptance rate α=0.8\alpha = 0.8α=0.8.

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, same K=5K = 5K=5 model predicts about 1.6x; at 0.9, about 3.1x. These are model outputs to compare against a benchmark, not promised throughput.

Model inputα\alphaαKKKAssume ccccverifyc_{\text{verify}}cverify​Approx. tokens/roundApprox. speedup
α\alphaα sweep0.650.112.41.6x
α\alphaα sweep0.750.112.92.0x
α\alphaα sweep0.850.113.72.5x
α\alphaα sweep0.950.114.73.1x
Depth sweep0.8580.115.12.8x
Depth sweep0.90100.116.93.4x

Nothing in this model table is a hardware measurement. A measured speedup needs target and draft checkpoints, engine and kernel versions, accelerator and precision, batch and concurrency, prompt/output-length distribution, decoding settings, warmup policy, and a named non-speculative baseline. Pair p50/p99 TTFT and ITL with throughput, cost, acceptance, and an output-distribution or correctness check.

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
Optimistic speedup model with c_verify = 1 and draft cost c = 0.1: a line chart of K = 1, 3, 5, 8, 10 shows 80% and 90% acceptance still rising then flattening, while 60% acceptance peaks near K = 3 and falls; a bar chart at K = 5 shows speedup climbing from 1.3x at 50% acceptance to 3.1x at 90%.
Under the optimistic c_verify = 1 model, raising K helps only while acceptance stays high. At 60% acceptance the curve already peaks near K = 3. Treat these as sketches to beat with a benchmark, not promised fleet speedup.

Pick KKK from measured acceptance, draft cost, and serving behavior. Start a sweep with single-digit depths, then let route-specific benchmarks choose the operating point 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. Continuous batching keeps two ledgers: target positions scored and tokens committed. With target-verification budget BBB and SSS active streams, no speculation schedules about SSS target positions per iteration. At depth KKK, target scores about SKS KSK proposed positions even when rejection happens early.

Committed output and KV growth follow a different ledger, about S(1+aˉK)S(1 + \bar{a}K)S(1+aˉK) for accepted fraction aˉ\bar{a}aˉ. Acceptance reduces committed growth; it doesn't erase verification work. If either target or KV-append capacity fills, streams wait or shrink and multi-tenant inter-token latency rises, even when solo speedup looks good.

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

Choose draft source by asking two questions: how much does each proposal cost, and how often does target accept it? A weak drafter rejects early; an expensive drafter consumes the latency budget. Common serving choices are:

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[6]Reference 6Medusa: 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[7]Reference 7EAGLE: Speculative Sampling Requires Rethinking Feature Uncertainty.https://arxiv.org/abs/2401.15077[8]Reference 8EAGLE-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[9]Reference 9Speculative 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[9]Reference 9Speculative 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[9]Reference 9Speculative Decodinghttps://docs.vllm.ai/en/latest/features/speculative_decoding/Reuse matching suffixes from previous outputsNo extra modelFit depends on reusable prior output patterns

For classic direct-token probabilistic sampling, draft and target need the exact same tokenizer and token-ID mapping. If IDs map to different subwords, target is verifying a different candidate sequence. Some engines expose limited heterogeneous-vocabulary paths, but those can constrain sampling method; decoded strings alone are not a proof of compatibility.[9]Reference 9Speculative Decodinghttps://docs.vllm.ai/en/latest/features/speculative_decoding/

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 a separate draft model. It adds extra prediction heads to the target itself. Each head predicts a different future position from the same hidden state, so the draft is a tree of continuations rather than one chain. Tree attention then scores those candidate paths in one target pass and keeps the longest accepted prefix.

That removes the job of loading and scheduling a second model, at the cost of training the heads and implementing tree verification.[6]Reference 6Medusa: Simple LLM Inference Acceleration Framework with Multiple Decoding Heads.https://arxiv.org/abs/2401.10774

The lossless path still uses the same residual sampler as the rest of this lesson. Medusa also describes typical acceptance, which keeps drafts that look plausible under an entropy threshold instead of reconstructing ppp exactly. Use typical acceptance only when you're willing to leave the exact target distribution. If you need Leviathan-style matching, keep rejection sampling.[6]Reference 6Medusa: Simple LLM Inference Acceleration Framework with Multiple Decoding Heads.https://arxiv.org/abs/2401.10774

EAGLE drafts from target-model internals instead of a separate full assistant. Earlier variants predict feature states, which can raise acceptance because those states carry more information than a plain token-only head.[7]Reference 7EAGLE: Speculative Sampling Requires Rethinking Feature Uncertainty.https://arxiv.org/abs/2401.15077 EAGLE-3 switches to direct token prediction, fuses low, middle, and high target layers, and trains the drafter with a training-time-test loop on its own outputs. Its paper reports up to 6.5x over vanilla autoregressive generation across five tasks, plus roughly 1.4x latency improvement over EAGLE-2 at batch size 1. In an SGLang test on H100 with LLaMA-Instruct 3.1 8B and MT-Bench, using chain length 3 without the tree structure, EAGLE-3 reports 1.38x throughput at batch size 64. Those are study-specific baselines, hardware, workloads, and settings, not portable service guarantees.[8]Reference 8EAGLE-3: Scaling up Inference Acceleration of Large Language Models via Training-Time Testhttps://arxiv.org/abs/2503.01840 Current vLLM docs expose EAGLE-family speculation, but flags and caveats move quickly.[9]Reference 9Speculative 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) skips the neural draft model. It searches the current context window for matching n-grams and reuses them as draft tokens. That helps on 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.[9]Reference 9Speculative Decodinghttps://docs.vllm.ai/en/latest/features/speculative_decoding/

PLD's appeal is that there's no extra model to load, train, or keep in memory. You can stack it with other speculative methods or use it 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

The production decision starts with a bottleneck, not a flag. Speculation spends extra FLOPs on drafting to save target-model memory bandwidth. Modern serving stacks expose several proposer families, so payoff depends on method, route, and load.[9]Reference 9Speculative Decodinghttps://docs.vllm.ai/en/latest/features/speculative_decoding/ On a compute-saturated route, the same trade can lower throughput and raise cost.

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
Four-quadrant fit map for speculation: low QPS memory-bound traffic is a canary candidate; low QPS compute-bound traffic often loses to extra draft FLOPs; high QPS still-latency routes prefer n-gram or suffix first; saturated compute-bound serving should hold speculation.
Read the quadrant before you sweep K. Low-QPS memory-bound decode is the canary. Saturated compute-bound serving should keep a non-speculative fallback. High-QPS routes that still care about latency can start with n-gram or suffix, which add little extra load.

Current vLLM docs describe model-based methods (EAGLE, MTP, draft model, and related proposers) as stronger inter-token-latency options, while n-gram and suffix decoding add less workload during peak QPS (queries per second).[9]Reference 9Speculative Decodinghttps://docs.vllm.ai/en/latest/features/speculative_decoding/ Speculation targets the gap between emitted tokens, not TTFT; draft setup can add first-token work. Feature incompatibilities and hardware numerics sit outside the paper proof, so validate the method on your stack.

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

Answer

It's most likely to help medium-to-low QPS, memory-bound, latency-sensitive decode where the target has idle compute between weight reads. It's less reliable in high-QPS, large-batch serving that is already compute-heavy. Light proposers such as n-gram or suffix are the first things to try when traffic is high but you still want some speculation.

Serving-engine reality

Serving-engine support changes quickly. vLLM's current docs list several speculation families, plus known feature incompatibilities, and they separate theoretical losslessness from what you should expect under real hardware numerics.[9]Reference 9Speculative Decodinghttps://docs.vllm.ai/en/latest/features/speculative_decoding/ Treat framework support as something you validate in your stack, not as a timeless property of the algorithm.

Make the rollout comparison reproducible before tuning KKK. Freeze target and draft checkpoints, engine and kernel versions, accelerator, precision, decoding settings, warmup policy, concurrency, and a prompt/output-length distribution that represents route traffic. Measure baseline TTFT, p50/p99 inter-token latency, and tokens-per-second throughput separately.

Then sweep proposer and depth on those same requests. Break out acceptance, output length, fallback rate, cost, and p50/p99 latency by workload class. Keep a non-speculative fallback for peak-QPS periods or incompatible features, and promote only after output-distribution parity or task correctness passes alongside latency and throughput gates.

The gate example below uses synthetic values to show decision logic, not a benchmark result.

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

Speculation fails in recognizable ways. Diagnose first mismatch before changing KKK.

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.

Replay representative prompt and output-length distributions through baseline and speculative paths with identical sampling settings, warmup, hardware, precision, engine, and concurrency. Check output-distribution parity and KV-cache rewind first. Then compare acceptance, TTFT, p50/p99 inter-token latency, throughput, cost per output token, and fallback rate by workload class. Promote routes that beat the latency target without breaking the throughput floor; 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's 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 ['serves', 'with', 'cache', 'when']. The target accepts the first two tokens and rejects 'cache'. What should happen next?

Correct answer: Discard 'when' and any later draft tokens, sample a correction for 'cache' 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: Don't 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 shouldn't 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

Speculation amortizes one target weight read across a short drafted span. Long prompts make that same pass more expensive, because the KV cache grows with every accepted token and attention work rises with sequence length. The next chapter covers that growth: 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.

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

Fast Transformer Decoding: One Write-Head is All You Need.

Shazeer, N. · 2019 · arXiv preprint

https://arxiv.org/abs/1911.02150

Qwen3.6-27B

Qwen Team · 2026

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

H100 GPU

NVIDIA · 2026

https://www.nvidia.com/en-us/data-center/h100/

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.