Implement and Benchmark Speculative Decoding
SpacexResearch Scientist · Technical ScreenLast updated
Implement autoregressive and speculative decoding algorithms with key-value cache management, along with a benchmarking harness to measure acceptance rates, latency, and throughput.
Problem Statement
In latency-critical inference pipelines, autoregressive decoding on large language models is frequently memory-bandwidth bound. Speculative decoding accelerates generation by using a small draft model to generate gamma candidate tokens sequentially, which the larger target model then verifies in a single parallel forward pass.
Implement greedy speculative decoding and an accompanying benchmarking routine.
Part 1: Speculative Decoding Function
Implement the function speculative_decode using greedy verification (where a draft token is accepted if and only if it matches the target model's greedy argmax prediction):
def speculative_decode(
target_model,
draft_model,
input_ids: list[int],
max_new_tokens: int,
gamma: int,
eos_token_id: int
) -> tuple[list[int], float]:
"""
Returns:
(generated_sequence, acceptance_rate)
"""
In each speculative step:
- The draft model generates
Kcandidate tokens (K <= gamma), stopping early if it produceseos_token_id. - The target model evaluates the candidates in a single batched pass.
- Verify candidates greedily: accept all draft tokens up to the first position
iwheredraft_token[i] != target_token[i]. Append the target model's corrected token at positioni. - If all
Ktokens are accepted, append the target model's additional next-token prediction ifmax_new_tokenshas not been reached. - Roll back the draft and target models' internal KV caches to reflect only accepted tokens plus the correction.
- Stop if
eos_token_idis emitted ormax_new_tokensis reached.
Part 2: Benchmarking Harness
Implement benchmark_decoding to compare speculative decoding against standard autoregressive decoding:
def benchmark_decoding(
target_model,
draft_model,
prompts: list[list[int]],
max_new_tokens: int,
gamma: int,
eos_token_id: int,
warmup_runs: int = 2,
num_runs: int = 5
) -> dict:
The benchmark must include warm-up iterations, synchronize CUDA/accelerator timers where applicable, and return a dictionary containing:
mean_latency_speculativeandmean_latency_autoregressive(in seconds)speedup(autoregressive / speculative)mean_acceptance_ratetokens_per_second_speculativeandtokens_per_second_autoregressive
Constraints
1 <= len(input_ids) <= 20481 <= gamma <= 161 <= max_new_tokens <= 512- Both models expose
forward(input_ids, use_cache=True, past_key_values=...)and a KV-cache truncation methodtruncate_cache(past_key_values, valid_length).
Hints
Hint 1
Remember that the target model generates logits for all candidate positions plus one additional token from the candidate prefix in a single forward evaluation.
Hint 2
When a draft token is rejected at index i, the target model's output at index i provides the ground-truth replacement token, meaning at least one token is always added per verification round.
Hint 3
Ensure cache truncation rolls back KV states to the exact length of the confirmed prefix before starting the next draft round to avoid stale attention states.
Follow-up questions
- How does the verification condition change when extending greedy speculative decoding to stochastic sampling with temperature T > 0 using Leviathan et al.'s rejection sampling scheme?
- Under what draft acceptance rate alpha and cost ratio (latency of draft vs. target model forward pass) does speculative decoding actually slow down wall-clock inference?
- How can you use speculative execution with tree-based drafts (such as Medusa or Speculative Streaming) rather than a linear sequence of gamma tokens?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.