LLMModule 12 · Video 09

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.

Video length41:42
Published29 October 2026
LevelIntermediate: attention and the KV cache at the level of Module 09 pages 01–02
Builds onModule 09 · KV cache, Module 09 · Batching, Video 04 · Speculative decoding

Video by Jia-Bin Huang · Watch on YouTube

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

  1. A request arrives. The scheduler checks how much of its start has already been read and cached.
  2. The model reads the new part of the prompt in one big pass (prefill), storing notes called the KV cache.
  3. Then it writes one token at a time (decode), batched together with many other users' requests.
  4. Memory for the notes is handed out in small pages, so nobody hogs space they don't use.
  5. 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

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.

TimeChapterWhat to look forWhere in the course
0:00How does an LLM server handle millions of requests?The framing: throughput and latency at the same time.09 · Request lifecycle
1:00The server: scheduler, KV cache, sampler, modelThe four parts the rest of the video fills in.09 · Request lifecycle
1:56Next-token prediction: why each step repeats workWithout caching, every step re-reads the whole prefix.30u30 Day 13
4:31The KV cache: compute keys and values onceWhat is cached per layer and per token.09 · KV cache (Problem 3)
6:01Prefill vs decode: compute-bound vs memory-boundWhy prefill does many FLOPs per byte and decode does few.09 · Batching (cost model)
7:53The scheduler: batching for throughputBatching decode steps shares one read of the weights.09 · Batching (Problem 6)
8:59Continuous batching (Orca) and prioritiesIteration-level scheduling: requests join and leave every step.09 · Problem 6, Problem 21
9:46Chunked prefill (Sarathi-Serve)Splitting a long prompt so it doesn't stall everyone's decode.09 · Problem 7
10:49Goodput and separate prefill/decode poolsDistServe's goodput metric and Splitwise-style disaggregation.09 · Problem 22
11:58How much memory does the KV cache need?The per-token formula and why it limits batch size.09 · Problem 3, 08 · Stage 08
13:02How operating systems manage memory: virtual memoryPages, page tables and why fragmentation goes away.09 · Problem 4
13:40PagedAttention: the KV cache in blocksBlock tables mapping logical to physical KV blocks.09 · Problem 4
14:40Preemption: when the memory pool fills upSwap to CPU or drop and recompute later.09 · Batching
15:23Shared prefixes: system prompts, chats, agentsHow much of real traffic repeats what came before.09 · Problem 8
16:40Prefix caching: vLLM's hashed blocksEach full block keyed by a hash of its tokens and its parent.09 · Problem 8; §2 below
17:47RadixAttention: a radix tree of cached prefixesToken-level longest-prefix match in a tree.New here: §2 and the demo
19:03Eviction and HiCache: when the cache runs outLRU leaf eviction, then spilling to CPU memory and storage.New here: §2
20:45Cache-aware scheduling: longest prefix firstReordering the queue so shared prefixes are used before eviction.New here: §2
22:56Results, and the catch: fairnessThroughput gains, and requests with no shared prefix waiting longer.§2; 09 · Problem 21
24:08Structured outputs: JSON, regex and state machinesMasking invalid tokens at each step.New here: §2
25:28Compressed state machines and XGrammarJump-forward decoding; precomputing most of the mask.New here: §2
26:38Speculative decoding: draft, then verifySeveral tokens per expensive forward pass, same distribution.Video 04; 09 · Problem 10
27:44Drafters: draft models, Medusa, EAGLE, n-gramWhere the guesses come from.Video 04
29:14Inside a decode step: FlashAttentionTiling attention to avoid writing the score matrix to HBM.09 · Problem 5
30:50Kernel launches and CUDA graphsWhy small decode steps are dominated by launch overhead.09 · Problem 14; §2
32:16torch.compile and overlap schedulingFusing kernels, and hiding CPU scheduling behind GPU work.New here: §2
33:18Fewer bytes: FP8 KV cache and quantizationDecode is bandwidth-bound, so fewer bytes means faster steps.09 · Problem 11
34:14DeepSeek-V3 on 8 GPUs: tensor parallelismSplitting a large model's layers across GPUs.09 · Problem 23; Video 02
35:13Latent attention and data-parallel attentionWhy TP duplicates an MLA cache, and the DP-attention fix.New here: §2; 09 · Problem 23
36:13Mixture of experts and wide expert parallelismExperts spread over many GPUs, with all-to-all token exchange.09 · Problem 23; Module 05 · MoE
39:10Replicas and the cache-aware routerSending requests to the replica that already holds their prefix.09 · Problem 8 (has a routing demo)
40:21Recap: never idle, never wasteful, never twiceThe 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:

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.

Prompt tokens
–
Radix-tree hit rate
–
Hashed-block hit rate
–
Tokens evicted
–

Try this: on Chat with a 1,000-token system prompt and 16-token blocks, the radix tree reuses 89.3% of prompt tokens and hashed blocks 88.7%. The granularity gap is small at vLLM's usual block size. At 256-token blocks it opens to 89.3% vs 78.2%. Set the shared prefix to 0 and Few-shot drops to 0% reuse: there's nothing to share. Now go back to Chat and cut capacity to 2,000 tokens: LRU starts evicting older conversations and the hit rate falls to 76.4%. This is the capacity squeeze that HiCache attacks by moving evicted KV to CPU memory instead of deleting it.

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:

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

TopicCourse page
Request path, streaming, cancellation09 · Request lifecycle
KV sizing, PagedAttention, FlashAttention09 · KV cache and attention
Continuous batching, chunked prefill, fairness09 · Batching and scheduling
Prefix caching, cache-aware routing, disaggregation09 · Prefix caching and disaggregation
Inter-token latency, speculative decoding09 · Latency; Video 04
FP8/INT4 quantisation and GPU choice09 · Quantisation and GPUs
Launch overhead, profiling, kernels09 · Profiling and kernels
vLLM vs SGLang vs TensorRT-LLM, metrics09 · Engines and observability
Large MoE serving, TP/EP/DP attention, cost09 · Scale and cost; Video 02
Designing models for cheap serving (GQA, KV quantisation)08 · Stage 08 · Inference-aware design

Why it matters for robotics

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:

Related videos on the author's channel: Speculative Decoding (Video 04 here) and FlashAttention.