How LLM serving actually works: vLLM and SGLang inside
A 40-minute tour of an inference server, covering the scheduler, KV cache, model and sampler, with vLLM and SGLang as the worked examples. Most of it maps straight onto Module 09 · Inference engineering, so this guide is mainly a chapter-by-chapter map into those pages. It explains in full only what Module 09 doesn't: RadixAttention and HiCache, cache-aware scheduling, structured outputs with XGrammar, CUDA graphs and overlap scheduling, and data-parallel attention for MLA.
Start here (no maths needed)
When millions of people chat with an AI at once, a program called a server decides whose words get worked on next, remembers what it has already read, and keeps the expensive chips busy every moment. This video opens that program up.
Think of it like this
Picture a busy restaurant kitchen. Orders arrive all the time. A good head chef doesn't cook one meal from start to finish before looking at the next order. She keeps every pan going and adds new dishes as soon as a burner frees up (batching). She keeps chopped onions ready, because half the dishes start with them (caching shared prefixes). She writes the shared prep on a list shaped like a family tree: "onions → onions and garlic → onions, garlic and tomato", so any dish can pick up the longest bit already done (a radix tree). When the counter is full, old prep goes to the fridge instead of the bin (HiCache). And she never lets a cook stand around waiting for the next ticket (overlap scheduling).
How it works
- A request arrives. The scheduler checks how much of its start has already been read and cached.
- The model reads the new part of the prompt in one big pass (prefill), storing notes called the KV cache.
- Then it writes one token at a time (decode), batched together with many other users' requests.
- Memory for the notes is handed out in small pages, so nobody hogs space they don't use.
- Tricks such as guessing ahead (speculative decoding), sticking to a format (structured outputs) and fast GPU kernels make each step cheaper.
Why robots care
A robot that asks a big model for a plan, or a fleet of robots sharing one model in the cloud, depends on that server answering quickly every time. The same tricks, especially reusing the start of a prompt that never changes, decide how fast the answer comes back.
Try it
- Write ten sentences that all begin "Once upon a time, in a land far away,". Count how many words you'd save if you only wrote the shared start once and drew arrows from it. That's prefix sharing.
- In the demo below, pick the Workload, then raise Block size from 1 to 256 and watch the orange "rounding loss" grow. Then shrink Cache capacity and see old conversations get evicted.
Words to know
- Prefill
- Reading the whole prompt in one pass before the first token comes out.
- Decode
- Writing the answer one token at a time.
- KV cache
- The stored keys and values for every token so far, so they aren't recomputed.
- Prefix cache
- Keeping the KV cache of a prompt's start so a later request with the same start can skip it.
- Radix tree
- A tree where each branch holds a run of tokens, so shared beginnings are stored once.
Going deeper: everything below is the full version, with some maths. On a first read, skim the headings and the "Why it matters for robotics" box, and come back for the rest later.
1. Watch
Chapter guide and map into the course
"New here" means Module 09 covers the topic only in passing, so section 2 below explains it.
| Time | Chapter | What to look for | Where in the course |
|---|---|---|---|
| 0:00 | How does an LLM server handle millions of requests? | The framing: throughput and latency at the same time. | 09 · Request lifecycle |
| 1:00 | The server: scheduler, KV cache, sampler, model | The four parts the rest of the video fills in. | 09 · Request lifecycle |
| 1:56 | Next-token prediction: why each step repeats work | Without caching, every step re-reads the whole prefix. | 30u30 Day 13 |
| 4:31 | The KV cache: compute keys and values once | What is cached per layer and per token. | 09 · KV cache (Problem 3) |
| 6:01 | Prefill vs decode: compute-bound vs memory-bound | Why prefill does many FLOPs per byte and decode does few. | 09 · Batching (cost model) |
| 7:53 | The scheduler: batching for throughput | Batching decode steps shares one read of the weights. | 09 · Batching (Problem 6) |
| 8:59 | Continuous batching (Orca) and priorities | Iteration-level scheduling: requests join and leave every step. | 09 · Problem 6, Problem 21 |
| 9:46 | Chunked prefill (Sarathi-Serve) | Splitting a long prompt so it doesn't stall everyone's decode. | 09 · Problem 7 |
| 10:49 | Goodput and separate prefill/decode pools | DistServe's goodput metric and Splitwise-style disaggregation. | 09 · Problem 22 |
| 11:58 | How much memory does the KV cache need? | The per-token formula and why it limits batch size. | 09 · Problem 3, 08 · Stage 08 |
| 13:02 | How operating systems manage memory: virtual memory | Pages, page tables and why fragmentation goes away. | 09 · Problem 4 |
| 13:40 | PagedAttention: the KV cache in blocks | Block tables mapping logical to physical KV blocks. | 09 · Problem 4 |
| 14:40 | Preemption: when the memory pool fills up | Swap to CPU or drop and recompute later. | 09 · Batching |
| 15:23 | Shared prefixes: system prompts, chats, agents | How much of real traffic repeats what came before. | 09 · Problem 8 |
| 16:40 | Prefix caching: vLLM's hashed blocks | Each full block keyed by a hash of its tokens and its parent. | 09 · Problem 8; §2 below |
| 17:47 | RadixAttention: a radix tree of cached prefixes | Token-level longest-prefix match in a tree. | New here: §2 and the demo |
| 19:03 | Eviction and HiCache: when the cache runs out | LRU leaf eviction, then spilling to CPU memory and storage. | New here: §2 |
| 20:45 | Cache-aware scheduling: longest prefix first | Reordering the queue so shared prefixes are used before eviction. | New here: §2 |
| 22:56 | Results, and the catch: fairness | Throughput gains, and requests with no shared prefix waiting longer. | §2; 09 · Problem 21 |
| 24:08 | Structured outputs: JSON, regex and state machines | Masking invalid tokens at each step. | New here: §2 |
| 25:28 | Compressed state machines and XGrammar | Jump-forward decoding; precomputing most of the mask. | New here: §2 |
| 26:38 | Speculative decoding: draft, then verify | Several tokens per expensive forward pass, same distribution. | Video 04; 09 · Problem 10 |
| 27:44 | Drafters: draft models, Medusa, EAGLE, n-gram | Where the guesses come from. | Video 04 |
| 29:14 | Inside a decode step: FlashAttention | Tiling attention to avoid writing the score matrix to HBM. | 09 · Problem 5 |
| 30:50 | Kernel launches and CUDA graphs | Why small decode steps are dominated by launch overhead. | 09 · Problem 14; §2 |
| 32:16 | torch.compile and overlap scheduling | Fusing kernels, and hiding CPU scheduling behind GPU work. | New here: §2 |
| 33:18 | Fewer bytes: FP8 KV cache and quantization | Decode is bandwidth-bound, so fewer bytes means faster steps. | 09 · Problem 11 |
| 34:14 | DeepSeek-V3 on 8 GPUs: tensor parallelism | Splitting a large model's layers across GPUs. | 09 · Problem 23; Video 02 |
| 35:13 | Latent attention and data-parallel attention | Why TP duplicates an MLA cache, and the DP-attention fix. | New here: §2; 09 · Problem 23 |
| 36:13 | Mixture of experts and wide expert parallelism | Experts spread over many GPUs, with all-to-all token exchange. | 09 · Problem 23; Module 05 · MoE |
| 39:10 | Replicas and the cache-aware router | Sending requests to the replica that already holds their prefix. | 09 · Problem 8 (has a routing demo) |
| 40:21 | Recap: never idle, never wasteful, never twice | The three principles behind every technique above. | 09 · Problem 18 (engines) |
2. The ideas, explained
The parts Module 09 already covers, briefly
Decode at batch size b reads every weight once per step, but does only about 2 FLOPs per weight per sequence. So it's memory-bandwidth bound, and batching is nearly free until compute or KV memory runs out. Prefill processes thousands of tokens per weight read and is compute-bound. Orca (Yu et al., OSDI 2022) schedules at the level of single iterations, so requests join and leave the batch every step. Sarathi-Serve (Agrawal et al., OSDI 2024) splits long prefills into chunks under a per-step token budget, so decodes aren't stalled. DistServe (Zhong et al., OSDI 2024) argues for optimising goodput, meaning the request rate served while meeting both time-to-first-token and time-per-output-token targets, and puts prefill and decode on separate GPUs. Splitwise (Patel et al., ISCA 2024) makes the same split on hardware chosen for each phase. PagedAttention (Kwon et al., SOSP 2023) stores the cache in fixed-size blocks with a per-request block table, as an OS does with virtual memory. When the pool fills, vLLM preempts a request and either swaps its blocks to CPU memory or frees them and recomputes later.
KV bytes per token = 2 × layers × kv_heads × head_dim × bytes_per_value
decode step time ≈ (weight bytes + Σ KV bytes of the batch) / HBM bandwidth
goodput = max request rate at which ≥ X% of requests meet their TTFT and TPOT SLOs
Deeper: 09 · Batching and scheduling, 09 · KV cache and attention, 09 · Disaggregation.
Prefix caching: hashed blocks vs a radix tree
The KV cache of a prefix depends only on that prefix, so any two requests that start the same way can share it. System prompts, few-shot examples, multi-turn chat history and agent loops all do this. There are two ways to find the shared part:
- Hashed blocks (vLLM's automatic prefix caching). Each full KV block gets a key built from a hash of its parent block's key plus its own tokens, so a key identifies the whole prefix up to that block. Lookup walks the new prompt block by block until a key misses. Matching is block-granular: a shared run of 1,000 tokens with 16-token blocks reuses 992 tokens.
- RadixAttention (SGLang). Zheng et al. (2024) keep a radix tree over token sequences. Each edge holds a run of tokens and points to their KV entries in a paged pool. Lookup is a longest-prefix match at token granularity. After a request finishes, its prompt and output stay in the tree, so the next chat turn finds its whole history. Branching programs, such as parallel samples, tree search and few-shot variants, become subtrees sharing one trunk.
radix hit = longest common prefix with anything cached (tokens)
block hit = ⌊ radix hit / block_size ⌋ × block_size (full blocks only)
eviction = LRU over leaves; a node in use by a running request is pinned (ref count)
Granularity is the smallest difference. The bigger ones are the data structure (a tree makes the sharing explicit, which the scheduler and router can exploit) and that SGLang built its whole runtime around reuse. The SGLang paper reports up to 6.4× higher throughput than the systems it compared against, across agent, reasoning, few-shot, JSON, RAG and chat workloads. vLLM's block hashing reaches much of the same reuse for common prefixes; the demo shows by how much.
Interactive: radix-tree prefix sharing vs hashed blocks
Each row is one request, in arrival order; bar length is its prompt length. Blue: tokens reused from the cache under both schemes. Orange: tokens a radix tree reuses but block hashing has to recompute, because they don't fill a whole block. Grey: tokens nobody had cached, which must be prefilled. Workloads: Chat, 3 conversations × 4 turns, interleaved; Few-shot, 12 different questions after one shared prefix; Tree search, a root, 3 branches, and 2 sub-branches each. Each request's prompt and output are cached. Capacity applies LRU leaf eviction to the tree. Lengths come from a fixed seed. This is a simplified model: no in-flight batching, and whole cached segments evicted at once.
Eviction and HiCache
The GPU holds only so much KV. RadixAttention evicts least-recently-used leaves first, so shared trunks, which every request touches, survive longest. As conversations get longer and more users come back for more turns, though, most history must be evicted and the hit rate falls. SGLang's HiCache (LMSYS blog, September 2025) extends the tree into a "HiRadixTree" that acts as a page table for KV held in GPU memory, CPU memory and external storage (disks or remote memory such as Mooncake, 3FS or NIXL backends). A cache controller backs up and loads entries between tiers. The details worth knowing:
- Layer-wise overlap. On a GPU miss that hits CPU memory, layer N+1's KV loads while layer N computes, hiding the transfer.
- Prefetch from storage. On a storage hit, data is prefetched into host memory, with policies that trade time to first token against reuse.
- Write policies. Write-through, write-through-selective (back up only frequently hit entries) and write-back.
- Layouts. GPU memory stays "layer-first" for the kernels; the other tiers use a "page-first" layout for larger I/O transfers.
The blog reports up to 6× throughput and up to 80% lower TTFT in its benchmarks, plus community reports such as a coding-agent deployment whose cache hit rate rose from 40% to 80%. These are vendor and community numbers on chosen workloads, not independent measurements.
Cache-aware scheduling, and the fairness catch
Arrival order isn't the best order for a cache. The SGLang paper sorts waiting requests by the length of their matched prefix, longest first, and notes that this approximates a depth-first walk of the radix tree. Requests that share a subtree run close together, before that subtree is evicted. The cost is fairness: a request with no shared prefix can keep getting overtaken, and its wait grows. Practical schedulers bound this with priorities, ageing or a mix of cache-aware and first-come ordering, which is the fairness and admission-control material of 09 · Problem 21. Across replicas the same idea becomes the cache-aware router. SGLang's, from the v0.4 release, keeps an approximate radix tree per worker and balances load. On a workload of long shared-prefix groups the release reports a hit rate of 75% against 20% and about 1.9× throughput (09 · Problem 8 has a routing demo).
Structured outputs: state machines, compression and XGrammar
To force valid JSON, a regex or a grammar, the server keeps an automaton state per request and, at each step, masks out every vocabulary token that would break the format before sampling:
logits[t] = −∞ for every token t not allowed from state s
sample next token, then s ← δ(s, token)
Two costs dominate. Checking every one of ~100k tokens against the state at every step is expensive, and the model wastes forward passes on text that is fully determined, such as {"name": ". SGLang's compressed finite state machine merges chains of states that each have only one allowed continuation, so the server can append that whole string at once ("jump-forward" decoding) instead of generating it token by token. XGrammar (Dong et al., 2024) targets full context-free grammars, which need a stack. It splits the vocabulary into context-independent tokens, whose validity depends only on the current grammar position and can be prechecked and cached, and context-dependent tokens checked at runtime with an efficient persistent stack. It also overlaps grammar work with GPU execution. The paper reports up to 100× speedup over existing solutions and near-zero end-to-end overhead. SGLang v0.4 integrated it as a grammar backend.
CUDA graphs, torch.compile and overlap scheduling
A decode step for a small batch launches hundreds of small kernels. Each launch costs CPU time, and the GPU can sit idle between them. CUDA graphs record the sequence once and replay it with one launch. Shapes must be fixed, so engines capture one graph per batch-size bucket and pad to it (09 · Problem 14 covers the profiling side). torch.compile fuses elementwise operations into fewer kernels and can emit graph-friendly code. Overlap scheduling attacks the CPU work between steps: batch scheduling, memory allocation and radix-tree matching. The SGLang v0.4 release ("zero-overhead batch scheduler", an idea it credits to NanoFlow) runs the scheduler one batch ahead, preparing step t+1's metadata while the GPU runs step t, with placeholder "future tokens" resolving the dependency on step t's output. The release reports 1.1× throughput over the previous version, most for small models and large tensor-parallel sizes.
serial: [CPU schedule t][GPU run t][CPU schedule t+1][GPU run t+1] …
overlap: [CPU schedule t+1 | GPU run t][CPU schedule t+2 | GPU run t+1] …
Data-parallel attention for MLA
DeepSeek-V3 (DeepSeek-AI, 2024) uses Multi-head Latent Attention: each token caches one small compressed latent shared by all heads, instead of separate keys and values per head. Tensor parallelism normally splits attention by heads, but an MLA latent can't be split that way, so with TP across 8 GPUs every GPU stores the same latent cache, 8 copies. SGLang's DP attention (v0.4) instead gives each GPU its own subset of requests for the attention layers, so each holds only its own requests' cache and the batch can grow. Before the MoE layer, the attention outputs are all-gathered across workers, and afterwards redistributed back. The release reports up to 1.9× decoding throughput on 8×H100. The catch: workers can end up with uneven batches, and the MoE layers still need their own parallelism, which is where wide expert parallelism comes in (09 · Problem 23).
TP-8 attention, MLA: each GPU stores the latent cache of ALL requests (8 copies)
DP-8 attention: each GPU stores the latent cache of ITS requests (1 copy)
Speculative decoding, FlashAttention, FP8 and MoE
These have full treatments elsewhere. Speculative decoding (Leviathan et al., 2023) with drafters from small models, Medusa heads (Cai et al., 2024) and EAGLE/EAGLE-3 feature-level drafting (Li et al., 2024, 2025) is Video 04, with a speedup demo in 09 · Problem 10. FlashAttention (Dao et al., 2022) is 09 · Problem 5. FP8 KV and weight quantisation are 09 · Problem 11. MoE serving and expert parallelism are 09 · Problem 23 and Module 05.
3. Where the course goes deeper
| Topic | Course page |
|---|---|
| Request path, streaming, cancellation | 09 · Request lifecycle |
| KV sizing, PagedAttention, FlashAttention | 09 · KV cache and attention |
| Continuous batching, chunked prefill, fairness | 09 · Batching and scheduling |
| Prefix caching, cache-aware routing, disaggregation | 09 · Prefix caching and disaggregation |
| Inter-token latency, speculative decoding | 09 · Latency; Video 04 |
| FP8/INT4 quantisation and GPU choice | 09 · Quantisation and GPUs |
| Launch overhead, profiling, kernels | 09 · Profiling and kernels |
| vLLM vs SGLang vs TensorRT-LLM, metrics | 09 · Engines and observability |
| Large MoE serving, TP/EP/DP attention, cost | 09 · Scale and cost; Video 02 |
| Designing models for cheap serving (GQA, KV quantisation) | 08 · Stage 08 · Inference-aware design |
Why it matters for robotics
- Prefix reuse fits robot prompts. A robot querying a language model for plans sends the same system prompt, tool list and scene description many times. Prefix caching turns that repeated prefill into a lookup, which is mostly time to first token, and that's what a waiting robot feels.
- Structured outputs make plans parseable. Forcing a JSON action schema or a grammar of allowed skills means the controller never receives an unparseable plan. The XGrammar paper names embodied agent commands as one use case.
- Fleets look like multi-tenant serving. Many robots sharing one cloud model is a classic scheduling problem. Priorities and fairness matter more than raw throughput when one request is a robot mid-motion.
- On-robot is a different regime. A single robot runs at batch 1 on an edge GPU. Continuous batching, routers and DP attention barely apply there; CUDA graphs, quantisation and small distilled models do.
- Weak link for VLA control loops. Flow-matching or chunked action heads don't decode token by token, so much of this video's decode machinery doesn't carry over directly to the action expert, only to the language backbone.
Self-check
Why is decode memory-bound while prefill is compute-bound?
Decode does a forward pass for one new token per sequence, so each weight read from memory is used for only a few FLOPs. Prefill pushes thousands of prompt tokens through the same weights at once, so arithmetic per byte is high.
A shared 1,000-token prefix and 16-token blocks: how many tokens can vLLM-style block hashing reuse, and how many a token-level radix tree?
Block hashing reuses only full blocks: ⌊1000/16⌋ × 16 = 992. A radix tree matches all 1,000.
Why does RadixAttention evict leaves, least recently used first?
Leaves are the parts unique to one request, while internal nodes are shared trunks used by many requests. LRU-over-leaves keeps the widely shared prefixes alive longest and never evicts a node a running request still needs.
What is the fairness cost of longest-prefix-first scheduling?
Requests with little or no cached prefix keep being overtaken by requests that hit the cache, so their queueing delay can grow without bound unless the scheduler adds ageing, priorities or a cap.
How does XGrammar avoid checking the whole vocabulary at every step?
It precomputes validity for context-independent tokens, which depend only on the grammar position, and checks only the context-dependent ones at runtime, using a persistent stack. It also overlaps grammar work with the GPU's forward pass.
Why does tensor parallelism waste memory for MLA, and what does DP attention change?
MLA caches one latent per token shared by all heads, so splitting heads across GPUs leaves every GPU holding the full latent cache. DP attention gives each GPU different requests for attention, so each stores only its own requests' cache, then all-gathers before the MoE layer.
References
From the video's description:
- SGLang: Efficient Execution of Structured Language Model Programs (Zheng et al., NeurIPS 2024)
- Efficient Memory Management for Large Language Model Serving with PagedAttention (Kwon et al., SOSP 2023)
- Orca: A Distributed Serving System for Transformer-Based Generative Models (Yu et al., OSDI 2022)
- Taming Throughput-Latency Tradeoff in LLM Inference with Sarathi-Serve (Agrawal et al., OSDI 2024)
- DistServe: Disaggregating Prefill and Decoding for Goodput-optimized LLM Serving (Zhong et al., OSDI 2024)
- Splitwise: Efficient Generative LLM Inference Using Phase Splitting (Patel et al., ISCA 2024)
- XGrammar: Flexible and Efficient Structured Generation Engine for Large Language Models (Dong et al., 2024)
- Fast Inference from Transformers via Speculative Decoding (Leviathan et al., ICML 2023)
- Medusa: Simple LLM Inference Acceleration Framework with Multiple Decoding Heads (Cai et al., ICML 2024)
- EAGLE: Speculative Sampling Requires Rethinking Feature Uncertainty (Li et al., ICML 2024)
- EAGLE-3: Scaling up Inference Acceleration of Large Language Models via Training-Time Test (Li et al., 2025)
- FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness (Dao et al., NeurIPS 2022)
- DeepSeek-V3 Technical Report (DeepSeek-AI, 2024)
- LMSYS blog: SGLang v0.4: zero-overhead batch scheduler, cache-aware load balancer, faster structured outputs (December 2024); SGLang HiCache: fast hierarchical KV caching (Zhiqiang Xie, September 2025)
- vLLM blog: DeepSeek-V4.1-Flash and wide expert parallelism (October 2026). The description gives no link, and this guide doesn't rely on it.
- Code: vLLM · SGLang
Related videos on the author's channel: Speculative Decoding (Video 04 here) and FlashAttention.