The main question of this lecture: after eight lectures of mechanisms,
who actually ships all this, how did we get here, and what new workload breaks our
two-workload view?
Imagine tomorrow is your first day owning a production LLM deployment. You open the course
notes: paged KV, sparse attention, continuous batching, disaggregation, parallelism, kernels,
speculative decoding. You can derive each one. Then you open GitHub and meet the names:
vLLM, SGLang, TensorRT-LLM, Dynamo, Mooncake. Search "vLLM vs SGLang 2026" and drown in
benchmark blog posts. And just as you settle on an engine, the RL team walks over: "we need to
swap the model's weights into your serving fleet every ten minutes, and please don't crash."
Nothing in your mental model of serving quite covers that.
This final lecture closes exactly those three gaps: who ships it (the framework
landscape and how to choose), how we got here (the 2022–2026 evolution and its four
main lines), and what breaks next (RL rollout, a workload that is inference but serves
nobody). Everything stays anchored to the one ratio you have carried since Lecture 1.
The Spine Revisited: One Formula, Eight Modules
Let us remember our main idea, one last time:
Main idea:
the whole course is a set of interventions on the decode arithmetic intensity
\[ I \approx \frac{2P\,B}{2P + B \cdot kv}
\quad \text{FLOP/byte,} \]
where \(P\) is the (activated) parameter count, \(B\) the decode batch size, and
\(kv = 2\, n_{layers}\, n_{kv}\, d_h\, b_{dtype}\) the KV bytes per token per
request. Decode sustains high goodput only when \(I\) approaches the hardware ridge
\(\pi/\beta\) (≈ 295 FLOP/byte on H100). Every module attacks one identified term, or the
hardware constants beneath it.
How to: start at the fraction in the middle; each dashed arrow attaches the modules that act
on that term. If a serving problem is slow, find which term dominates at your operating point. Then the
arrow tells you which module to reach for.
Three structural remarks, because they are the difference between knowing the mechanisms and
wielding them:
The interventions compose multiplicatively. Paging enlarges the feasible region of
\(B\), scheduling fills it, sparsity and MLA shrink the cost of filling it, parallelism splits what
remains, kernels make the hardware deliver its peak, and speculation changes what "one step"
produces.
They are ordered by leverage. At low \(B\), the \(2P\) weight-read term dominates, and
only batching, parallelism, and speculation help. At high \(B\) and long \(L\), the
\(B \cdot kv\) term dominates, and KV compression and sparsity become the binding constraint.
That is exactly why Modules 2 and 3 sit at the center of this course.
The model itself is a knob. MLA and DSA act on \(kv\) directly,
and MTP acts on the speculative \(\tau\). The architecture is a dial on the same formula. Keep
that thought; it is the seed of the co-design argument below.
Engineering Takeaway: when a serving deployment underperforms, do not
profile blindly. Compute the operating point \((B, L)\), evaluate both terms of the denominator
\(2P + B \cdot kv\), and identify which module's mechanism addresses the dominating term. A
prefill-heavy, cache-poor workload needs Module 2's reuse, not Module 7's kernel tuning; a
decode-bound long-context workload needs Modules 3/5, not more tensor parallelism.
The Framework Landscape: Four Systems on a Converging Checklist
Fine, the mechanisms are yours. Now somebody has to press the deploy button. The mechanisms do
not exist in the abstract; they live inside a small number of production engines, and the honest
way to picture them is not a league table but a map: each system has a hallmark, a thing it
does more natively than anyone else.
How to: read left to right first. Everything left of the dashed line is a single engine
instance; right of it lives the layer that schedules across instances and treats their KV as one pool.
Then read top to bottom: vLLM's commitment is breadth, and the two below it trade breadth for depth in a
chosen target (frontier models for SGLang, NVIDIA silicon for TensorRT-LLM).
Walking the map slowly, system by system:
vLLM, the general-purpose engine. Hallmark: breadth. It originated PagedAttention
(Module 2) and remains the default reference engine. After the v1 rewrite it is a multi-process
EngineCore with asynchronous scheduling: scheduler, executor, and API server run as separate
processes, so CPU-side scheduling overlaps GPU execution (Module 4's overlap theme). Its
distinguishing commitment: the largest hardware matrix (NVIDIA, AMD, TPU, Trainium, ...), a
day-0 convention for new models, and pluggable attention backends (FlashAttention, FlashInfer,
FlashMLA, vendor kernels) swappable per model and platform. If your constraint is "many models,
many platforms, one engine", it is natively satisfied here.
SGLang. Hallmark: depth in scheduling and frontier-model support. It originated
RadixAttention (Module 2's prefix reuse) and invests deepest where vLLM is broadest: a
zero-overhead overlap scheduler, structured output compiled into the serving path, and the
deepest DeepSeek support: large EP plus PD disaggregation reproducing DeepSeek's official
deployment form, and day-0 support for V3.2's sparse attention including the dual-page-size
KV work of Module 3. Frontier MoE models at scale tend to appear here first in open source.
TensorRT-LLM, NVIDIA's official engine. Hallmark: kernel and compilation depth.
First-day optimizations for new NVIDIA hardware appear here first because the engine is
co-developed with the GPU roadmap; it sits in a closed loop with NIXL (KV transfer), Dynamo
(orchestration), and Triton (kernel authoring). Brand-new, NVIDIA-endorsed silicon → shortest
path to peak.
Dynamo, which is not an engine at all but an orchestration layer above engines. It productizes
everything in this course that lives beyond a single instance: global PD scheduling (a global
prefill queue; conditional, per-request disaggregation), KV-aware routing (send each request to
the worker holding the most overlapping prefix), KVBM (tiered KV blocks across
HBM/DRAM/NVMe/remote), and NIXL as the transfer substrate. Workers can be vLLM, SGLang, or
TensorRT-LLM; it is deliberately engine-agnostic.
Here is the layer-by-layer checklist, with the course mechanism behind each cell. Read the
last two rows carefully, because they are the punchline:
Global prefill queue, xPyD conditional disaggregation
Parallelism (M6)
TP / PP / EP / DP / DCP
TP / PP / large EP (DeepSeek's form)
TP / PP / EP
Cross-instance orchestration and scaling
Kernels (M7)
Pluggable attention backends
FlashInfer / FlashMLA, dual page size
In-house kernels, Blackwell-first
(NIXL transfer layer instead)
PD disaggregation (M5)
Supported
Supported
Supported
Global scheduling, conditional disaggregation
Speculative decoding (M8)
EAGLE/Medusa-style, MTP
EAGLE-2/3, MTP
Medusa/EAGLE-style
(delegated to engines)
Sparse attention (M3)
Backend-dependent
Day-0 V3.2 DSA
Backend/compiler-dependent
n/a
Everything on the checklist exists everywhere. Speculation and sparse attention are
no longer differentiators in kind, only in maturity and defaults.
This leads to the lecture's two observations. Honestly, they reframe what
"choosing an engine" even means:
Observation 1: single-engine checklists have converged. Every
technique in this course exists in all three engines. What remains: maturity (whose overlap
scheduler actually hides CPU time under load), defaults (which backend and chunk size you get out
of the box), and ecosystem (hardware and model coverage, surrounding tooling).
Choosing by feature presence is almost always a mistake.
Observation 2: the center of competition is moving outside the
engine. The remaining large gains are in multi-instance orchestration, shared tiered KV pools, and
cache-aware routing; Dynamo and Mooncake mark the shift. In the language of the spine formula:
within one instance, \(B\) and \(kv\) are close to their local optima; the open frontier is letting
\(B\) and the KV pool span a whole cluster without losing locality.
?If every engine has every feature, what could a benchmark post titled "engine X is
2× faster than engine Y" even be measuring? Think about Lecture 1's metrics discipline before
opening.
Possible answer
Almost never a mechanism. Usually it measures a default or a maturity gap at a specific
operating point: an engine whose default is colocated prefill looks terrible on chat until you
enable chunked prefill or disaggregation; one overlap scheduler may hide CPU time better at your
\(B\); one prefix-cache implementation may degrade better at your hit-rate distribution. Defaults
are load-bearing: compare tuned configurations on your traffic replay, or
the number says nothing transferable. (This is pitfall #1 at the end of the lecture, already in
the wild.)
☺
TA says: want the theory stress-tested? Watch what the 2026 frontier
releases actually did. When Kimi K3 dropped (weights on 2026-07-27), vLLM upstreamed the
entire serving stack on day 0 in v0.27.0 (hybrid KDA-state + paged-MLA cache management,
fused FlashKDA / AttnRes / LatentMoE kernels, native MXFP4 MoE, DSpark speculation, PD
disaggregation over NIXL), shipped as Docker images on the CUDA 13.0 stack, validated floor
8×B300 (16×B200 or 8×MI355X alternates); v0.28.0 followed with decode context parallelism, fused
FlashKDA decode, ROCm, and EAGLE3-on-KimiLinear. SGLang matched day-0 and published official K3
and GLM-5.3-Flash cookbooks (ReplaySSM for speculation on the linear state, unified-pool
KDA/MLA caches). TensorRT-LLM? No native K3 or GLM-5.3 support. The NVIDIA-sanctioned path
is Dynamo recipes on GB200/GB300: the first clear instance of an orchestration layer, not
an engine, deciding frontier-model coverage. New entrant to track: TokenSpeed is on Moonshot's
recommended-serving list for K3. And GLM-5.3-Flash needs vLLM 0.29.0+ (sparse-MLA kernels are
Hopper-or-newer) with SGLang day-0. For 2026 hybrid models, one question dominates the
model-family axis: which engine has the state-management path upstreamed? Hybrid caches
and rollback are too intricate to bolt on locally.
Which Framework When: Four Axes, Not a Checklist
Because the checklists converge, selection reduces to four axes: workload type, model family,
hardware, team constraints. The decision guide compresses to a table you can interrogate:
Axis / scenario
Starting point
Why
Heterogeneous hardware (NVIDIA + AMD + TPU), many model families
vLLM
Broadest backend matrix; pluggable attention per platform
New open-weight model released this morning
vLLM or SGLang
Both hold a day-0 convention; check which merged support first for that family
DeepSeek family (V3/V3.2/R1) at scale, large EP + PD
SGLang
Reproduces the official deployment form; day-0 DSA; dual page size
Structured-output heavy (agents, JSON-mode APIs)
SGLang
Compiled constrained decoding in the serving path
Brand-new NVIDIA silicon (Blackwell bring-up)
TensorRT-LLM
First-day kernels and quantization appear here first
Two cautions on using the table: (1) Defaults are load-bearing:
compare tuned configurations, never out-of-box ones. (2) The axes interact: "DeepSeek on
Blackwell" pits SGLang's model depth against TensorRT-LLM's hardware depth, and the honest answer
is a benchmark on your traffic, or both engines under one orchestrator.
Worked example (framework selection end-to-end, do it with me):
A provider must serve DeepSeek-V3.2 (MLA + DSA, 256+1 routed experts, MTP) on a
64-node H800 cluster; traffic is 70% multi-turn agentic sessions with heavy shared system
prompts (expected prefix hit \(h \approx 0.8\)), p95 TTFT SLO 2 s at
128K context; the team also runs weekly RL post-training. Step 1 (model family): V3.2's production form is large EP + PD with DSA day-0 in SGLang;
the alternative is re-implementing dual-page-size DSA cache management yourself. → SGLang. Step 2 (workload): at \(h = 0.8\), effective prefill FLOPs drop to
\((1-h)\,2PL = 0.2\,2PL\). Radix-tree reuse is not optional; it is a 5× prefill-cost
lever. Cluster-level cache-aware routing then decides which worker realizes the hit. Step 3 (cluster): 64 nodes with a TTFT SLO under load implies independent prefill/decode
pools (Module 5's interference argument), a global prefill queue, and KV-aware routing →
Dynamo above the SGLang workers, KVBM tiering the 128K-context KV (≈4.4 GB per request
even with MLA) across HBM/DRAM. Step 4 (RL): embed the same engine under verl with weight hot-update, reusing the
production stack so rollout numerics match serving numerics. Decision: Dynamo + SGLang + verl-embedded rollout; re-evaluate TensorRT-LLM workers only if
the fleet moves to newer NVIDIA silicon. Every arrow traces to a numbered module. That is the
point of the course.
Engineering Takeaway: evaluate engines on your own traffic replay,
not feature matrices. The cells are all "yes" by now; what differs is tail latency under your
prefix-hit distribution, your model, your hardware. And expect the next procurement question to be
about the orchestration layer, not the engine.
The Evolution Timeline: Four Main Lines
Now the second gap: how did we get here? Line up 2022–2026 and, surprisingly, a plot appears.
The field keeps attacking whichever term of the spine formula was binding at the time:
How to: read the milestones left to right, then re-read column by color. The ordering
(kernels → scheduling → memory → reuse → disaggregation → co-design → orchestration) is not historical
accident; it is the intensity formula's binding term, moving year by year.
Each line deserves one paragraph of "what changed / why it matters / what comes next":
Line 1, scheduling granularity: the decision quantum went
request (FasterTransformer's static batches) → step (Orca, 2022) → token budget
(Sarathi, 2023–24). Each refinement put the decision closer to where cost is incurred, keeping
\(B\) pinned at the ceiling; token budgets let prefill and decode coexist in one step without the
GEMM monopolizing the device. Next: elastic scheduling for loads that fluctuate an order
of magnitude within a step (RL rollout and agentic traffic), with requests that must be
interruptible, not merely preemptible.
Line 2, memory becomes a managed resource: watch the KV cache's career:
an execution detail inside the attention kernel (2022) → a paged, allocatable resource (vLLM,
2023) → a reusable asset shared across requests (radix trees, 2023–24) → a tiered, routable,
cluster-scale asset (Mooncake 2024; Dynamo KVBM + routing 2025). Paging removed the fragmentation
tax; reuse gave the \((1-h)\) free-prefill lever; tiering acknowledged KV working sets exceed HBM
(DRAM/NVMe/remote at 25–50 GB/s RDMA hold the cold part); routing made locality a scheduling
signal. Next: KV as the cluster data structure: cross-engine cache formats,
session-spanning KV, lifecycle for interrupted trajectories.
Line 3, deployment decoupling: single colocated instance → PD
disaggregation (industrialized by Mooncake, 2024) → multi-pool orchestration with independent
scaling, global queues, and conditional per-request disaggregation (Dynamo, 2025). Prefill is a
GEMM workload, decode a GEMV one; colocating them means decode's TPOT SLO loses. Decoupling lets
each phase sit at its own roofline corner with its own parallelism and replica count, and lets
the pools scale independently as the prefill:decode cost ratio shifts with traffic mix and hit
rate. Next: conditional disaggregation as default; NIXL toward NVLink-domain speeds
across nodes; orchestration that counts the training cluster as a peer.
Line 4, model–system co-design (the new differentiator): the model side
started making architecture decisions for system goals. MLA compresses \(kv\) by ≈10×
(320 KB → ≈34 KB/token); DSA turns attention from \(O(L)\) to \(O(k)\) reads per step with a
132 B/token indexer; MTP builds speculative capability into the checkpoint itself (85–90%
second-token acceptance); DeepSeek's open-source week published the matching kernels and
large-EP deployment form. The first three lines are converging: everyone pages, everyone
reuses prefixes, everyone disaggregates. And converged lines stop differentiating. Co-design is
non-convergent and compounding: the ceiling of what systems can achieve is increasingly decided
at training time, in the KV footprint the architecture commits to, the attention operator
it selects, the speculative heads it ships.
Worked example (the cache trajectory in one line of arithmetic):
follow per-token cache bytes, BF16, across the course's canonical models:
\[ 320\ \text{KiB} \xrightarrow{\;\times 1/9.4\;} 34\ \text{KiB}
\xrightarrow{\;\times 1/1.26\;} 27\ \text{KiB}
\xrightarrow{\;\times 1/2.3\;} 11.7\ \text{KiB} \]
GQA-70B baseline (320 KiB → 320 GiB at 1M context, infeasible single-tenant) → DeepSeek-V3 MLA
(9.4×) → Kimi K3 hybrid (27 KiB on the 24 MLA layers + a fixed 0.2 GiB KDA state ⇒ ≈27.2 GiB at
1M, ≈11.8× under the GQA baseline; the state term stops scaling with \(L\) entirely) →
GLM-5.3-Flash (≈0.26 KB/token/layer averaged over 45 layers vs 1.15 KB for GLM-5.3's MLA)
⇒ ≈11.7 KiB/token, ≈27× below GQA. Cumulatively, a 320 GiB per-request 1M-context working set
becomes ≈12 GiB. And look at where every factor came from: a training-time decision.
Latent compression (M2), fixed recurrent state (KDA), selection sparsity (M3).
Note (read vendor numbers like an engineer): the last factor mixes units
deliberately: 0.26 KB is a per-layer average over a hybrid stack (34 fixed-state KDA
layers + 11 sparse-MLA layers); multiplying by 45 layers is what makes it comparable to the
per-token figures. Always check which basis a cache number is quoted on. And keep one eye on the
dissenter: MiniMax M2/M3 dropped its linear layers after failed ablations. The 2026 convergence
on "hybrid linear + sparse/compressed full attention + built-in speculation" (Qwen3-Next 3:1
GDN + MTP; K3 69 KDA : 24 Gated-MLA + DSpark; GLM-5.3-Flash 34 KDA + 11 sparse-MLA + a shipped
MTP layer; DeepSeek V4 CSA/HCA) is empirical, not a theorem. Dissenters are where a
template's boundary conditions will surface.
Engineering Takeaway: track all four lines when capacity-planning, but
weight line 4 in model selection. Two checkpoints with equal benchmark quality can differ
by an order of magnitude in serving cost purely through \(kv\) and attention structure.
Evaluating a model without computing its \(kv\)/token and attention complexity is, after this
course, an unforced error.
RL Rollout: The Third Workload
Now for the twist that breaks the frame. Since Lecture 1, we have classified traffic into two
workload classes: latency-sensitive online serving (SLO-bound, goodput-defined) and
throughput-bound offline batch. RL post-training adds a third. In frameworks like verl, the
training loop alternates optimizer steps with rollout: the current policy generates samples
(responses or full agentic trajectories) which the trainer then learns from. Rollout is
inference (mainstream stacks embed vLLM or SGLang as the rollout engine), but it obeys neither
class:
No SLO, pure throughput. Nobody waits on a token stream; only total samples per
training step matters. Goodput's latency constraints simply vanish.
But with constraints serving never has: weight sync, partial rollout, numerical
consistency, violent elasticity. Treating rollout as "offline serving with a big batch" is the
classic pitfall.
How to: trace one training step clockwise. Red arrow: new weights must cross in a fraction
ε of step time T; idle rollout GPUs are wasted trainer time. Green box: rollout is inference with the
latency constraints removed and four new ones added. Blue arrow: samples and their likelihood ratio ρ flow
back, and a per-token numerical gap compounds across the whole trajectory. Amber box: the 2026 addition,
snapshotting environments instead of idling through their waits.
Weight Sync: A Time-Budget Model
After every training step (a cadence of minutes or shorter) the updated weights must reach the
rollout engine, in seconds. How much bandwidth does that need? Let's count. Let the trainer hold \(P\)
parameters at \(b_{dtype}\) bytes/element, sharded across \(G\) training GPUs each with inter-node
egress \(\beta_{net}\) (≈ 50 GB/s per GPU on 400 Gbps IB/RoCE). If the transfer must finish within
a fraction \(\epsilon\) of step time \(T\):
\[ \underbrace{\frac{P \cdot b_{dtype}}{G \cdot \beta_{net}}}_{\text{broadcast time (tree/pipelined)}} \;\le\; \epsilon\, T
\qquad\Longrightarrow\qquad
G\cdot\beta_{net} \;\ge\; \frac{P\cdot b_{dtype}}{\epsilon\, T}. \]
The model assumes trainer shards stream in parallel (each sends its own \(P/G\) shard) with a
pipelined broadcast, per-GPU egress being the bottleneck: the standard NCCL/RDMA weight-broadcast
form.
Worked example (sizing sync for a 671B MoE):
DeepSeek-V3 scale: \(P = 671\)B full checkpoint in BF16 (the system must move all of it,
not just the 37B activated): \(P\,b_{dtype} = 1.34\) TB. Step \(T = 10\) min \(= 600\) s; budget
\(\epsilon = 2\%\) → sync must finish in 12 s.
Required bandwidth: \(1.34\ \text{TB} / 12\ \text{s} \approx 112\) GB/s → as few as
3 sending GPUs suffice; a 64-GPU trainer pushing in parallel finishes in
\(1.34\ \text{TB}/(64 \cdot 50\ \text{GB/s}) \approx 0.42\) s. FP8 checkpoint (671 GB): all times
halve. Dtype is a first-class sync lever. Colocate alternative (train + rollout share GPUs): an in-memory/NVLink-domain swap.
1.34 TB at NVLink ≈ 900 GB/s ≈ 1.5 s; at HBM copy speeds ≈ 0.4 s. Over PCIe alone
(64 GB/s): ≈ 21 s, already over budget; this is why colocate designs stage weights through
NVLink/HBM paths, never PCIe. Sanity check (limits): a 2-minute step at the same 2% budget needs 5× the bandwidth:
560 GB/s, i.e. ≈12 senders, or FP8 + 6 senders. Short-step RL is exactly where
weight sync stops being free.
Note: the structural requirement underneath the budget is an efficient
weight hot-update path: pause generation, swap tensors in place (ideally zero-copy from an
NCCL receive buffer or shared memory), resume. Traditional serving never had this interface,
because serving weights are immutable for the process lifetime.
Colocate vs a dedicated rollout fleet mirrors Module 5's disaggregation logic, one level up.
Each earns its keep at a different scale:
Dimension
Colocate (train + rollout, same GPUs)
Disaggregated (dedicated rollout fleet)
Weight sync
In-memory / NVLink-domain swap; sub-second to ~1.5 s
NCCL/RDMA broadcast; the budget governs; typically 0.4–3 s at \(G \ge 16\)
GPU utilization
Time-sliced: both alternate on the same devices; no idle fleet
Rollout GPUs idle during the optimizer step unless steps are pipelined
Memory pressure
Optimizer + gradients + activations and KV pages must fit; M2's tiering becomes survival
Each side sized for its own working set
Engine integration
In-process; hot-update is a pointer/tensor swap
Engine as a service; hot-update is remote + broadcast
Failure / isolation
A training crash kills rollout and vice versa
Independent scaling and fault domains
Best when
Single mid-size cluster, long optimizer steps relative to rollout
Large clusters, short steps, or rollout fleets doubling as serving capacity
As scale grows, the disaggregated form wins on utilization and isolation, and the
weight broadcast becomes a first-class distributed-systems problem: tree topology, pipelining, FP8
quantization of the update, overlapping sync with the tail of the previous rollout.
Partial Rollout and the δ̄ Watchdog
Agentic trajectories reach hundreds of thousands of tokens, far longer than one training step's
rollout window. If every trajectory must finish on one weight version, two failure modes
follow: the long-tail stall (the slowest trajectory gates the whole step; utilization
collapses while 99% of GPUs wait), and unbounded off-policy drift (the policy that wrote a
long trajectory's early tokens is many updates old by the end). Partial rollout answers both:
at a sync boundary, interrupt in-flight trajectories, keep their KV, swap weights, resume.
Three requirements land squarely on Modules 2 and 4:
An interruptible request lifecycle. A suspend state distinct from running and
from preempted-to-recompute: generation stops at a token boundary, but the request's KV blocks are
pinned, not freed. Stronger than Module 4's preemption, which trades KV away for memory.
KV lifecycle across weight versions. Keep or recompute? An algorithmic decision exposed
to the trainer.
Fairness under suspension. Suspended trajectories resume ahead of fresh requests if
tail completion matters, behind them if throughput does. It is a scheduling knob with direct
consequences for sample efficiency.
?The prefix's K/V were computed by the old weights. Lecture 1 proved the KV cache
is legal by causality. So is keeping the pinned KV after the weight swap still "exact"? Recall
what the proof assumed before you open.
Possible answer
Causality guarantees that later tokens never change earlier activations. But the cached K/V are
still functions of the weights that produced them. Recomputed-equivalent K/V of the prefix
require a re-prefill under the new weights. So engines choose: accept the mixed-version KV
as an off-policy price (the trajectory's prefix was genuinely generated by an older policy), or
selectively re-prefill. Either way it is an algorithmic trade-off the trainer must know about, not
something the engine can silently decide.
Conservative reading: the source text is terse here, but this matches its
phrasing: "cached K/V of the prefix are recomputed-equivalent only if we re-prefill under the new
weights; engines either accept the mixed-version KV as the off-policy price, or selectively
re-prefill."
Now the subtlest constraint, and my candidate for "easiest way to silently break an RL stack."
On-policy corrections assume generation probabilities are known exactly. The
importance-sampling ratio for a token (multiplied, a trajectory):
\[ \rho = \frac{\pi_{train}(a_t \mid s_t)}{\pi_{rollout}(a_t \mid s_t)},
\qquad
\log \rho_{traj} = \sum_{t=1}^{N}\left(\log \pi_{train} - \log \pi_{rollout}\right)_t. \]
The catch: rollout logprobs come from inference kernels (fused, reassociated, possibly
FP8/TF32, speculative-verified) while training logprobs come from training kernels, with
different reduction orders and different dtypes. Floating-point addition is non-associative, so the
same sequence scores differently by small per-token gaps
\(\delta_t = (\log\pi_{train} - \log\pi_{rollout})_t\) even at identical weights, and the ratio
picks up the factor \(\exp\bigl(\sum_t \delta_t\bigr)\). How bad that factor gets depends entirely
on the structure of \(\delta_t\):
Worked numbers, three regimes (pin these to your wall):
Systematic bias \(\bar{\delta} = 10^{-3}\) per token, \(N = 10{,}000\):
\(\sum_t \delta_t = 10\), so \(\rho\) is off by \(e^{10} \approx 22{,}000\times\). A factor of
twenty-two thousand, from one part in a thousand per token! Every
correction is garbage, and clipped variants saturate at their bounds, silently converting the
algorithm into an un-corrected off-policy one.
Zero-mean noise \(\bar\delta = 0\), per-token std \(\sigma_\delta = 10^{-3}\) i.i.d.:
the log-ratio error grows as \(\sigma_\delta\sqrt{N} = 0.1\), so \(\rho\) wobbles by
\(e^{0.1} \approx 1.11\): an 11% noise floor, tolerable for most correction schemes.
The middle case \(\bar\delta = 10^{-4}\): \(\sum_t \delta_t = 1\) → \(e^1 \approx
2.7\times\) at \(N = 10{,}000\), already enough to visibly distort gradients.
Same magnitude, three fates. The operative question is never whether \(\delta_t\) is
small (it always is) but whether it is biased. Mitigations fall straight out of the
algebra: align reduction orders and dtypes in the logprob path (the sampling path can stay fast);
keep per-token ratios so clipping absorbs local spikes; and measure \(\bar\delta\) on held-out
sequences after every kernel or engine upgrade. It is a one-line regression test with
\(10^4\)-token compounding consequences.
☺
TA says: the 2026 instance is Kimi K3 again; it made rollout
infrastructure itself a published system. AgentEnv runs agentic environments in Firecracker
microVMs with 133 ms checkpoint / 49 ms resume, and exercised 51.2M sandboxes over K3's training:
the environment-side counterpart of partial rollout (engine suspends trajectories; AgentEnv
freezes the environments themselves at 49-ms granularity instead of idling GPUs through tool
waits). Rollout is now a three-layer stack (engine, orchestration, environment fleet), each layer
with its own checkpoint/resume machinery. And day-0 LoRA RL on native MXFP4 weights (SGLang +
Miles): the weight-sync \(P\,b_{dtype}\) term drops from a ~1.56 TB checkpoint to adapter-sized, so
the broadcast budget stops binding at short steps. Meanwhile the numerics gap gains a new
term (MXFP4 expert GEMMs vs higher-precision training logprobs), making δ̄ monitoring
mandatory from step one.
Engineering Takeaway: three rollout rules. (1) Budget weight sync
with the feasibility inequality before choosing colocate vs disaggregated; if the
required aggregate bandwidth exceeds your fabric, the decision is made for you. (2) Demand
a real weight hot-update path and a suspend-and-pin-KV scheduler state; retrofitting either into a
serving-only engine is a research project. (3) Treat \(\bar\delta\) as a monitored SLO of
the RL stack: \(10^{-4}\) per token is a 2.7× trajectory-ratio distortion at 10K tokens; you
cannot clip your way out of bias. And when evaluating a 2026-class model for RL, ask for three
artifacts with the weights: the rollout-engine integration, the environment infrastructure, and
the quantized-native training path with a measured \(\bar\delta\). A model that ships all three
has pre-paid the rollout engineering.
The epilogue is the familiar pattern of the timeline: the features RL rollout forced into
existence (hot-update interfaces, interruptible lifecycles, coexistence with training frameworks)
are now official feature areas of both vLLM and SGLang, not forks. A workload pressure
appears, one system answers it, the answer becomes checklist. Whatever workload class follows
agentic RL will arrive the same way: as new constraints on these same layers.
Common Pitfalls
Six misconceptions to lose before you graduate:
Choosing an engine on the feature checklist alone. Everything in this course exists
in all three engines; decide on maturity, defaults, ecosystem, and measured behavior on your
traffic.
Ignoring ecosystem and maturity asymmetries. "Supports large EP" and "reproduces
DeepSeek's official deployment form in production" are different claims: a feature vs an
operational track record.
Treating RL rollout as ordinary offline serving. Max batch, static weights, no
hot-update silently breaks the training loop's assumptions.
Ignoring numerical consistency. A per-token gap of \(10^{-3}\) looks like
floating-point noise... until it compounds into a 22,000× ratio distortion over 10K tokens.
Measure the bias, not the magnitude.
Over-orchestrating small deployments. Below Module 5's disaggregation threshold, a
global queue and KV routing add failure modes without adding throughput.
Evaluating models without their system signature. Equal-quality checkpoints can
differ ≈10× in serving cost via \(kv\) and attention structure alone. Architecture is a
serving-cost parameter.
Research Thinking
How to: read the starting point; read the question and think, for a minute, a day, a
week... and only then open the answers. You are not supposed to reinvent several months of someone's
research; it's the habit of thinking that counts.
?Card 1, "invent" the weight-sync budget. Starting point: rollout GPUs idle during weight
sync stretch the training step exactly as if the trainer were slower. Question: your
trainer runs 2-minute steps on a 671B model (BF16, 1.34 TB) with G = 32 GPUs at 50 GB/s each.
What fraction of the step does sync consume, and what would you demand for
\(\epsilon \le 1\%\)? Compute before opening.
Possible answer
Broadcast time \(= P\,b_{dtype}/(G\beta_{net}) = 1.34\ \text{TB}/(32 \cdot 50\ \text{GB/s}) =
0.84\) s → \(\epsilon = 0.84/120 \approx 0.7\%\): already under 1%. The \(\epsilon \le 1\%\)
requirement asks \(G\beta_{net} \ge 1.34\ \text{TB}/(0.01 \cdot 120\ \text{s}) = 1.12\) TB/s,
i.e. 23 senders at 50 GB/s; the 32-GPU trainer (1.6 TB/s aggregate) meets it with margin. With
only 8 GPUs (400 GB/s) it fails. Then the levers are: FP8 the checkpoint (halves the
bytes); pipeline the sync, overlapping it with the tail of the previous rollout; or go colocate
and stage through NVLink/HBM. The 2026 radical answer: sync adapters. LoRA-on-MXFP4 drops
\(P b_{dtype}\) from ~1.56 TB to adapter size, and the budget stops binding at any realistic step
time.
Existing attempts: pipelined NCCL/RDMA tree broadcast (verl-class stacks), FP8
quantization of the update itself, colocate NVLink-domain swaps, and the SGLang + Miles
quantized-native LoRA path.
?Card 2, bias is not noise. Starting point: \(\log\rho_{traj} = \sum_t
(\log\pi_{train} - \log\pi_{rollout})_t\) and the two stacks disagree by \(\sim 10^{-3}\) per
token no matter what you do. Question: why does a systematic gap of \(10^{-3}\) destroy
the learner while an i.i.d. gap of the same size is fine? And which single regression test
distinguishes them after every engine upgrade? Derive both cases before opening.
Possible answer
Systematic: errors add. \(\sum_t\delta_t = N\bar\delta\), linear in trajectory
length: \(N{=}10^4\), \(\bar\delta=10^{-3}\) ⇒ ratio off by \(e^{10} \approx 22{,}000\times\);
clipping just saturates and hides it. I.i.d.: errors add in quadrature. The log-ratio
std grows as \(\sigma_\delta\sqrt{N} = 0.1\), an 11% wobble: tolerable. Same per-token magnitude,
\(22{,}000\times\) vs \(1.11\times\), separated only by \(E[\delta_t]\). The test: score a
held-out batch of sequences in both stacks after every kernel/engine/dtype upgrade and estimate
\(\bar\delta\) (a mean, not a magnitude). One line of code, \(10^4\)-token consequences.
Bonus: per-token ratios let clipping absorb local spikes before they compound.
?Card 3, predict the stack before you check it. Starting point: the four selection axes
(workload, model family, hardware, team). Scenario: a startup serves Llama-3.3-70B chat plus a
growing set of open-weight models on a mixed fleet (H100 today, AMD MI300X next quarter, TPU spot
under evaluation); single-turn traffic with modest prefix overlap (\(h \approx 0.2\)); 4 nodes
now, 12 planned; two infra engineers. Question: what stack would you recommend, layer by
layer? And which single change would flip the answer?
Possible answer (and how it flips)
Model family: "many models" → the breadth axis dominates → vLLM (widest
hardware matrix, day-0 support). Workload: \(h \approx 0.2\) makes prefix reuse a minor lever
(single-turn traffic pays \((1-h)\,2PL = 0.8\,2PL\)), so SGLang's radix depth is not decisive.
Hardware: mixed fleet again points to vLLM; TensorRT-LLM would strand the AMD/TPU capacity.
Scale/team: at 4–12 nodes of single-phase chat, disaggregation is marginal (Module 5's threshold),
and Dynamo buys two engineers orchestration overhead, not throughput. Stack: vLLM, colocated
with chunked prefill; defer orchestration. The flip: "DeepSeek-V3.2 at 64 nodes" moves every
axis toward SGLang + Dynamo: exactly the worked example above. Same axes, opposite corner.
Have Fun! The Weight-Sync Budget Calculator
Enough reading. Now you sign off on the sync budget. Set the checkpoint, the trainer size,
and the step cadence, and watch the budget bind. Can you find the smallest sender count that keeps a
2-minute step inside 1%? And at what step cadence does any BF16 broadcast fail no matter
how many GPUs you throw at it... until you flip to FP8?
Budget physics: feasibility lives and dies by
\(G\beta_{net} \ge P\,b_{dtype}/(\epsilon T)\). And dtype is a first-class lever, not a detail.
Seminar & Homework
Lab 9: Framework Comparison and RL-Rollout Reading closes the hands-on pack with two
halves, mirroring the lecture:
Engine comparison under matched configs. On two GPUs (or one, sequentially), run one
benchmark harness across vLLM and SGLang: same model revision, BF16 both,
--max-num-batched-tokens matched against SGLang's --chunked-prefill-size,
speculation off both, prefix caching off both for the engine comparison, then a second,
clearly labeled pass with it on. Same seed. Optional: replay the Mooncake open trace at
production-like rates. Deliverable: a one-page benchmark report in the course's required format
(Module 1's report checklist: percentiles, input/output split, goodput under your chosen SLO,
not a single blended number).
Rollout config reading. Take a verl-style colocate rollout config and identify, in the
actual settings you've learned this lecture to expect: the weight-sync mechanism (which broadcast,
over which fabric, against which cadence) and the partial-rollout mechanism (where trajectories
suspend, and what happens to their pinned KV).
Summary
One spine, eight modules. Every mechanism is an intervention on
\(I \approx 2PB/(2P + B\,kv)\): paging and scheduling act on the feasible region and fill of
\(B\); MLA/DSA on \(kv\); parallelism splits \(2P + B\,kv\); kernels
deliver \(\pi,\beta\); speculation amortizes the \(2P\) read over \(\tau\) tokens. Diagnosing a
deployment = finding the dominating term at \((B,L)\).
Frameworks: vLLM = breadth (hardware × models, async EngineCore); SGLang = depth
(radix reuse, frontier-MoE form, day-0 sparse attention); TensorRT-LLM = kernel/compilation depth
on new NVIDIA silicon; Dynamo = orchestration above engines. Single-engine checklists
have converged; decide on the four axes, tuned configs, your traffic.
Evolution: four lines: scheduling granularity (request → step → token budget →
elastic/interruptible), memory as a managed resource (detail → paged → reusable → tiered &
routable), deployment decoupling (colocated → PD → orchestrated pools), and model–system
co-design (the non-convergent differentiator: 320 KiB/token → ≈11.7 KiB/token, ≈27×, decided at
training time).
RL rollout is a third workload class: no SLO, pure throughput, but with weight sync
(\(G\beta_{net} \ge P b_{dtype}/\epsilon T\); 1.34 TB in 12 s needs only 3 senders, over PCIe it
would take 21 s), partial rollout (suspend, pin KV, resume; accept mixed-version KV or
re-prefill), numerical consistency (\(\bar\delta = 10^{-3}\) → 22,000× trajectory distortion;
zero-mean noise → 1.11×; measure the bias), and ×10 elasticity.
The pattern that closes the course: every workload pressure becomes an engine feature
becomes a checklist item. You now know how to read the next one.