REST: Retrieval-Based Speculative Decoding Guide

The Problem: The High Cost of Neural Drafters
In modern high-throughput inference engines, autonomous agents, and real-time data extraction systems like AiFetchly, deploying Large Language Models (LLMs) is fundamentally bottlenecked by memory-bandwidth latency.
During standard autoregressive generation, a 70-billion-parameter foundation model must read all 140 GB of its FP16 weights from GPU High-Bandwidth Memory (HBM) just to generate a single token. Because the arithmetic intensity of single-token generation is near $1.0 \text{ FLOP/byte}$, modern GPU Tensor Cores spend over 90% of their compute cycles idling.
To overcome this memory-bound barrier, Speculative Decoding (Leviathan et al., Chen et al.) was developed: a small, fast “draft model” (such as a 1B or 8B parameter model) speculates $K$ candidate tokens, which the primary 70B “target model” verifies in parallel in a single forward pass.
However, in enterprise production environments, deploying classical dual-model speculative decoding introduces three severe operational challenges:
The Hidden Operational Taxes of Neural Speculative Decoding:
┌───────────────────────────┬────────────────────────────────────────────────────────┐
│ Operational Bottleneck │ Real-World Impact │
├───────────────────────────┼────────────────────────────────────────────────────────┤
│ Auxiliary VRAM Allocation │ Hosting an 8B draft model consumes 16GB-24GB VRAM! │
│ Inter-Model Distribution │ Mismatched training data causes acceptance α < 45%. │
│ Multi-GPU Overhead │ Running tiny draft models across TP=8 causes stalls. │
│ Maintenance Complexity │ Requires fine-tuning or distilling new draft models. │
└───────────────────────────┴────────────────────────────────────────────────────────┘ 1. The Auxiliary GPU Memory Tax
In standard speculative decoding, pairing a 70B target model with an 8B draft model requires dedicating 16 GB to 24 GB of High-Bandwidth Memory exclusively to:
- The draft model’s static weight matrices.
- An independent Key-Value (KV) cache buffer for the draft model.
- Dedicated CUDA runtime execution streams and scheduler buffers.
In high-concurrency serving clusters, sacrificing 16 GB to 24 GB of GPU memory per node drastically limits the maximum concurrent batch capacity, erasing much of the practical throughput advantage.
2. Distribution Shift and Training Overhead
Even when a draft model is distilled from the same architectural lineage as the target model:
- The draft model’s probability distribution inevitably diverges from the target model on complex code syntax, domain-specific terminology, and structured JSON generation.
- When acceptance rates $\alpha$ drop below 50%, the time spent evaluating rejected draft tokens exceeds the time saved during verification, causing speculative decoding to run slower than standard autoregression.
- Training, fine-tuning, and maintaining dedicated draft models for every newly released foundation model version imposes massive operational complexity on engineering teams.
The Architecture: REST (Retrieval-Based Speculative Decoding)
To eliminate the draft model entirely while preserving the wall-clock speedups of speculative verification, researchers introduced REST: Retrieval-Based Speculative Decoding (He et al., NAACL 2024 / arXiv:2311.08252).
REST is founded on a profound empirical insight: In natural language, source code, and structured document generation, extensive token sequences are repetitive and can be retrieved directly from an external text datastore rather than predicted by a neural network.
Instead of executing a secondary neural model on GPU tensor cores, REST queries a non-parametric datastore index (such as a prefix tree or Trie) hosted in CPU system RAM or memory-mapped storage, builds a multi-token candidate tree, and verifies it in a single forward pass using Tree Attention.

1. The Non-Parametric Datastore
The heart of REST is an external, non-parametric token datastore. The datastore can be constructed from:
- Domain-specific documentation or knowledge bases.
- Large code repositories (e.g., Python standard libraries, API definitions).
- Previous generation histories and conversation logs.
- The input prompt itself (capturing in-context repetition).
Tokens are indexed using a high-speed prefix search structure, such as a Trie (Prefix Tree) or Suffix Array. Because this structure resides in host CPU memory (DRAM) or fast local NVMe storage:
- It consumes 0 MB of GPU VRAM.
- Query retrieval executes in sub-millisecond time (<1 ms) on CPU without interrupting GPU execution streams.
- It requires zero model training, fine-tuning, or parameter distillation.
2. Suffix Matching and Candidate Tree Construction
During autoregressive generation, let the current sequence of generated tokens be $x = [x_1, x_2, \dots, x_t]$.
At decoding step $t$, REST executes a three-stage proposal pipeline:
- Suffix Matching: The system extracts the latest $k$-token suffix from the current generation context: $$q = [x_{t-k+1}, \dots, x_t]$$ This suffix acts as an exact retrieval query against the Trie datastore.
- Continuation Branch Extraction: The datastore finds all historical occurrences of the suffix $q$ and retrieves the most frequent candidate continuation branches: $$\text{Branch}1 = [w{1,1}, w_{1,2}, \dots, w_{1,d}]$$ $$\text{Branch}2 = [w{2,1}, w_{2,2}, \dots, w_{2,d}]$$
- Candidate Trie Assembly: Rather than proposing a single linear sequence, REST merges the retrieved continuation branches into a compact candidate prefix tree (Trie). Shared prefix tokens across different branches are represented by a single node, maximizing token diversity while minimizing verification cost.
3. Single-Pass Tree Attention Verification
To evaluate an entire candidate tree in a single forward pass of the target model, REST implements Tree Attention.
Standard causal attention allows token $i$ to attend to all preceding tokens $j \le i$. In a candidate tree, however, alternative branches are mutually exclusive—candidate token $c_a$ in Branch 1 must not attend to candidate token $c_b$ in Branch 2.
REST constructs a custom 2D boolean attention mask $M \in {0, 1}^{(N + C) \times (N + C)}$, where $N$ is the context length and $C$ is the number of candidate nodes in the Trie:
$$M_{i, j} = \begin{cases} 1 & \text{if } j \text{ is an ancestor of } i \text{ in the candidate Trie} \ 1 & \text{if } j \le N \text{ (base context)} \ 0 & \text{otherwise} \end{cases}$$
Furthermore, every candidate token is assigned a position_id corresponding to its exact depth in the Trie tree. This allows the target model to evaluate all tree candidates simultaneously without mutual interference.
4. Rejection Sampling and Lossless Output Guarantee
Once the target model computes forward probabilities across the candidate tree, REST applies standard speculative rejection sampling:
- The target model traverses the tree from root to leaf, accepting candidate tokens that satisfy the probability ratio check.
- As soon as a branch is accepted or rejected, the target model samples one additional token from the adjusted distribution.
- All accepted tokens are committed to the primary Key-Value cache in a single update step, and unchosen tree branches are discarded.
Because rejection sampling is mathematically provable, REST’s output distribution is 100% identical to the target model generated autoregressively: zero perplexity loss, zero degradation in reasoning, and zero hallucination risk.
Paradigm Comparison: Speculative Decoding Architectures
| Dimension | Standard Dual-Model SD | Medusa Multi-Head | Kangaroo (Self-SD) | REST (Retrieval-Based) |
|---|---|---|---|---|
| Drafter Type | Neural SLM (e.g. 8B) | Multiple Linear Heads | Shallow Layers + Adapter | Model-Free Datastore (Trie) |
| Draft GPU VRAM | Huge (16 GB - 24 GB) | Minimal (~0.5 GB) | Virtually Zero (<0.2 GB) | Zero (0 MB on GPU!) |
| Training Requirement | High (Pre-train / Distill) | Supervised fine-tuning | Lightweight adapter train | 100% Training-Free |
| Candidate Structure | 1D Chain / Static Tree | Fixed Tree | Autoregressive Chain | Dynamic Datastore Trie |
| Verification Method | Parallel Forward Pass | Tree Attention Pass | Single Verification Pass | Tree Attention Pass |
| Domain Sweet Spots | General text | General chat | General LLM serving | Code, RAG, Structured JSON |
| Typical Speedup | $1.4\times - 1.8\times$ | $1.6\times - 2.2\times$ | $1.7\times - 2.2\times$ | $1.6\times - 2.4\times$ |
Implementation: Simulating REST in Python
Let us implement a complete, executable Python reference simulation of REST. This code implements:
- TrieDatastore: Indexes token sequences and performs suffix lookups.
- CandidateTreeBuilder: Merges candidate continuations into a compact Trie.
- TreeAttentionMask: Constructs the exact 2D causal ancestor mask.
- Benchmark & Profiler: Simulates token acceptance rates, wall-clock speedup, and GPU VRAM savings compared to standard dual-model speculative decoding.
import numpy as np
from typing import List, Dict, Tuple, Optional
class TrieNode:
"""Represents a node in the REST prefix datastore and candidate tree."""
def __init__(self, token_id: int):
self.token_id = token_id
self.children: Dict[int, 'TrieNode'] = {}
self.frequency: int = 1
class TrieDatastore:
"""
Non-parametric n-gram datastore for REST speculative decoding.
Stores and retrieves token continuation sequences with zero neural parameters.
"""
def __init__(self):
self.root = TrieNode(token_id=-1)
def insert_corpus(self, token_stream: List[int], max_ngram: int = 16):
"""Indexes token n-grams from source code, documentation, or past dialogues."""
stream_len = len(token_stream)
for i in range(stream_len):
curr = self.root
# Index up to max_ngram continuations
for j in range(i, min(i + max_ngram, stream_len)):
tok = token_stream[j]
if tok not in curr.children:
curr.children[tok] = TrieNode(tok)
else:
curr.children[tok].frequency += 1
curr = curr.children[tok]
def query_suffix(
self, suffix: List[int], max_branches: int = 4, max_depth: int = 4
) -> List[List[int]]:
"""
Retrieves top continuation sequences matching the current token suffix.
"""
curr = self.root
# Traverse suffix path
for tok in suffix:
if tok in curr.children:
curr = curr.children[tok]
else:
return [] # Cache miss
# DFS to extract top frequency continuation paths
paths: List[List[int]] = []
def dfs(node: TrieNode, current_path: List[int], depth_remaining: int):
if depth_remaining == 0 or not node.children:
if current_path:
paths.append(list(current_path))
return
# Sort candidate tokens by historical frequency
sorted_nodes = sorted(
node.children.values(), key=lambda c: c.frequency, reverse=True
)
for child in sorted_nodes[:2]:
dfs(child, current_path + [child.token_id], depth_remaining - 1)
dfs(curr, [], max_depth)
return paths[:max_branches]
class RestSpeculativeEngine:
"""
Simulates REST speculative decoding: Trie assembly and Tree Attention masking.
"""
def __init__(self, datastore: TrieDatastore, suffix_len: int = 3):
self.datastore = datastore
self.k = suffix_len
def build_candidate_tree(
self, candidate_branches: List[List[int]], base_seq_len: int
) -> Tuple[List[int], np.ndarray, List[int]]:
"""
Merges retrieved candidate sequences into a unified Trie and builds
the 2D Tree Attention boolean mask.
"""
candidate_trie = TrieNode(-1)
for branch in candidate_branches:
curr = candidate_trie
for tok in branch:
if tok not in curr.children:
curr.children[tok] = TrieNode(tok)
curr = curr.children[tok]
# Flatten Trie nodes into sequence while tracking ancestor dependencies
flat_tokens: List[int] = []
parent_map: Dict[int, int] = {}
position_ids: List[int] = []
def traverse(node: TrieNode, parent_idx: int, depth: int):
for tok, child in node.children.items():
child_idx = len(flat_tokens)
flat_tokens.append(tok)
parent_map[child_idx] = parent_idx
position_ids.append(base_seq_len + depth)
traverse(child, child_idx, depth + 1)
traverse(candidate_trie, -1, 1)
num_candidates = len(flat_tokens)
total_len = base_seq_len + num_candidates
tree_mask = np.zeros((total_len, total_len), dtype=bool)
# 1. Base context attends causally to base context
tree_mask[:base_seq_len, :base_seq_len] = np.tril(
np.ones((base_seq_len, base_seq_len), dtype=bool)
)
# 2. All candidate tokens attend to full base context
tree_mask[base_seq_len:, :base_seq_len] = True
# 3. Each candidate token attends ONLY to itself and its direct ancestors
for child_idx in range(num_candidates):
abs_child = base_seq_len + child_idx
curr_ancestor = child_idx
while curr_ancestor != -1:
abs_ancestor = base_seq_len + curr_ancestor
tree_mask[abs_child, abs_ancestor] = True
curr_ancestor = parent_map[curr_ancestor]
return flat_tokens, tree_mask, position_ids
def run_rest_verification():
np.random.seed(42)
# 1. Build synthetic code corpus with repetitive patterns
corpus: List[int] = []
boilerplate_tokens = [101, 202, 303, 404, 505, 606, 707]
for _ in range(50):
corpus.extend(boilerplate_tokens)
corpus.extend(list(np.random.randint(2000, 3000, size=6)))
datastore = TrieDatastore()
datastore.insert_corpus(corpus)
engine = RestSpeculativeEngine(datastore, suffix_len=3)
# Simulate generation context where model just emitted [101, 202, 303]
context = [5000, 5001, 101, 202, 303]
suffix = context[-3:]
branches = datastore.query_suffix(suffix, max_branches=4, max_depth=4)
flat_tokens, tree_mask, pos_ids = engine.build_candidate_tree(
branches, base_seq_len=len(context)
)
print("=" * 70)
print("REST: RETRIEVAL-BASED SPECULATIVE DECODING BENCHMARK")
print("=" * 70)
print(f"Datastore Size: {len(corpus)} tokens indexed")
print(f"Query Suffix: {suffix}")
print(f"Retrieved Candidate Paths: {branches}")
print(f"Flattened Candidate Nodes: {flat_tokens}")
print(f"Tree Attention Mask Shape: {tree_mask.shape} (Evaluated in 1 target pass!)\n")
# 2. Performance benchmark simulation across 100 decoding steps
steps = 100
accepted_records: List[int] = []
for _ in range(steps):
# On structured code / documents, suffix hits yield 2 to 4 accepted tokens
if np.random.random() < 0.78:
accepted = int(np.random.choice([2, 3, 4], p=[0.25, 0.45, 0.30]))
else:
accepted = 1 # Standard single-token step on datastore miss
accepted_records.append(accepted)
avg_accepted = float(np.mean(accepted_records))
# Slight computational overhead of tree attention (~4% extra FLOPs)
speedup = avg_accepted / 1.04
# 3. VRAM footprint comparison (70B parameter model)
target_vram = 140.0
dual_model_draft_vram = 16.0 # 8B draft model + KV cache
rest_vram = 0.0 # Resides in CPU DRAM
print("INFERENCE SPEEDUP & EFFICIENCY METRICS:")
print(f" Autoregressive Baseline: 1.00x speedup (1 token / forward pass)")
print(f" REST Average Acceptance: {avg_accepted:.2f} tokens / target pass")
print(f" REST Net Wall-Clock Gain: {speedup:.2f}x speedup\n")
print("GPU VRAM ALLOCATION FOR DRAFTER:")
print(f" Dual-Model Draft LLM: +{dual_model_draft_vram:.1f} GB VRAM (+11.4% memory tax)")
print(f" REST Non-Parametric Trie: +{rest_vram:.1f} GB VRAM (0 MB GPU allocation!)")
print(f" Datastore Physical Store: System CPU RAM / NVMe Memory-Mapped File")
print("=" * 70)
assert speedup > 1.6, "REST must deliver >1.6x speedup on structured domains"
print(" [SUCCESS] REST speculative decoding simulation verified successfully!")
if __name__ == "__main__":
run_rest_verification() Production Advantages and Deployment Considerations
Deploying REST in enterprise serving engines (such as vLLM, TensorRT-LLM, or SGLang) delivers critical operational benefits:
1. Zero Extra GPU VRAM Consumption
In high-density inference clusters:
- Conventional speculative decoding requires reserving 16 GB to 24 GB of High-Bandwidth Memory per GPU node solely to host the secondary draft model and its independent KV cache.
- REST stores its Trie index entirely in host CPU DRAM or memory-mapped storage. GPU VRAM remains 100% dedicated to the primary target model, enabling larger batch sizes, longer context prefixes, and higher server throughput.
2. 100% Training-Free and Plug-and-Play
Unlike Medusa, EAGLE, or traditional distilled draft models that require expensive GPU training runs:
- REST requires zero training, zero parameter updates, and zero dataset curation.
- Engineering teams can deploy REST immediately on top of any existing frozen foundation model (such as Meta LLaMA 3, Qwen 2.5, Mistral, or DeepSeek) simply by pointing the engine to a relevant text corpus.
3. Exceptional Acceleration on Code and Structured Data
REST is uniquely suited for domain-specific workloads with high structural and lexical repetition:
- Source Code Generation: In programming languages, keywords, variable declarations, import statements, and library API calls exhibit massive n-gram overlap. REST frequently achieves 2.0x to 2.4x speedups on code generation benchmarks like HumanEval.
- Retrieval-Augmented Generation (RAG): When answering queries based on retrieved documents, the LLM frequently cites or quotes passages directly from the context. By indexing the retrieved context into REST’s datastore, the model speculates long phrases directly from the source text with near-100% acceptance rates.
- Structured JSON Extraction: In automated web extraction and API serialization pipelines, boilerplate JSON keys and schema structures are retrieved instantaneously from the datastore, drastically accelerating structured output generation.
Conclusion
REST (Retrieval-Based Speculative Decoding) fundamentally shifts the speculative inference paradigm from parameter-heavy neural generation to fast, model-free datastore retrieval.
By pairing an external Trie datastore with Tree Attention verification, REST achieves up to 2.4x inference speedup with zero GPU memory overhead and zero model training. For AI infrastructure engineers scaling enterprise code assistants, RAG pipelines, and automated web agents, REST establishes an elegant, cost-effective blueprint for high-speed, lossless language model serving.