TAI BUI
Back to curriculum
LearnPython

Disaggregated Prefill/Decode — NVIDIA Dynamo and llm-d

Prefill is compute-bound and decode is memory-bound, so running both on the same GPU wastes one resource. Disaggregation splits them onto separate pools and transfers KV cache between them over NIXL (RDMA/InfiniBand or TCP fallback). NVIDIA Dynamo (GTC 2025 announce, 1.0 GA) sits above vLLM/SGLang/TRT-LLM, and its Planner Profiler and SLA Planner auto-rate-match prefill:decode ratios to meet SLOs. NVIDIA publishes throughput gains in this ballpark: developer.nvidia.com (2025-06) shows a ~6x improvement for DeepSeek-R1 MoE on GB200 NVL72 + Dynamo in the medium-latency regime, and the Dynamo product page (developer.nvidia.com, undated) advertises up to 50x MoE throughput on GB300 NVL72 + Dynamo vs Hopper. The "30x" figure is a community aggregate across full-stack Blackwell + Dynamo + DeepSeek-R1 reports. We have not found a single primary source stating exactly 30x, so treat it as a directional claim. llm-d (Red Hat + AWS) is Kubernetes-native: prefill, decode, and router run as independent Services with per-role HPA. llm-d 0.5 adds hierarchical KV offloading, cache-aware LoRA routing, UCCL networking, and scale-to-zero. On economics, an internal rollup of multiple customer disclosures suggests 30–40% savings on $2M-class inference spend (i.e., $600-800K/year) when switching from colocated serving to disaggregated with Dynamo at constant SLA. The specific $2M→$600-800K figure is an internal composite, not a single published case study, so use it as an order-of-magnitude anchor, not a reference citation. Short prompts (<512 tokens, short output) don't justify the transfer cost.

TIME: ~75 minutesPREREQUISITES: Phase 17 · 04 (vLLM Serving Internals), Phase 17 · 08 (Inference Metrics)

Learning Objectives

  • Explain why prefill and decode have different optimal GPU allocations and quantify the waste under colocation.
  • Diagram the disaggregated architecture: prefill pool, decode pool, KV transfer via NIXL, router.
  • Name the condition when disaggregation does NOT pay off (short prompts, short outputs).
  • Distinguish NVIDIA Dynamo (stack-above) from llm-d (Kubernetes-native) and match each to an operational context.

The Problem

You run Llama 3.3 70B on 8 H100s. Under a mixed workload (long prompts, short outputs), GPUs idle during decode because most of the compute went to prefill. Flip the workload to short prompts and long outputs and the opposite happens. Colocating prefill and decode means you over-provision both.

The budget impact is 20-40% of GPU time wasted on the wrong resource. You end up buying H100 compute to run memory-bound decode, or buying H100 HBM bandwidth to run compute-bound prefill. Either way you pay for something you aren't using.

Disaggregation splits prefill and decode onto separate pools, each sized for its own bottleneck. The KV cache moves from the prefill pool to the decode pool over a high-bandwidth interconnect.

Warm-up0/3
1. A service reports a P99 latency of 800 ms. What does that figure mean?Warm-up
Twenty dots on a timeline from 0 to 1000 ms, nineteen clustered before a dashed 800 ms mark and one past it, asking what that mark represents for the group.
Illustrative finish times; the question is what the mark means, not the exact times.
2. In Kubernetes, what does a Horizontal Pod Autoscaler (HPA) do?Warm-up
Three moments at a food truck: a short line with one cook, a short line with one cook, and a long line with extra cooks added, asking what decides the cook count.
Everyday metaphor, not a specific platform.
3. What does time to first token (TTFT) measure for a streaming LLM response?Warm-up
A bar split into a waiting segment and a segment leading up to the first word of a streamed reply, with a bracket spanning the whole bar labelled as the span the question asks about.
Setup: the span from sending the request to the first word, not the full reply.
Check: The Problem0/2
1. What is disaggregated serving?Define
One GPU box handling a request from arrival to response, with a note that an alternative design uses two separate kinds of GPU pool instead, asking what that alternative is called.
Setup: the single-pool case only. The two-pool alternative is the question.
2. A colocated 8-GPU H100 cluster serving long prompts with short outputs shows idle GPU time during decode. After the workload flips to short prompts and long outputs, the idle time shows up in the other phase. What is the root cause?Analyze
Two bars for the same cluster under two workload mixes: one mostly prefill work with idle time, the other mostly decode work with idle time on the other side, asking for the root cause.
Setup: the two observed workload mixes, taken directly from the question.

The Concept

Why the bottlenecks differ

Prefill runs the transformer over the full input prompt in one forward pass. Matrix multiplications dominate, so it is compute-bound. H100 FP8 gives ~2000 TFLOPS of useful throughput, and batch efficiency is good because one forward processes many tokens.

Decode generates one token at a time and reads the full weights on each iteration, so it is memory-bandwidth-bound. HBM3 gives ~3 TB/s. Batching only helps at high concurrency, where the weight read amortizes across the batch.

Colocated, you buy GPUs that have to be good at both. An H100 handles both phases, but it costs the same whichever one you're running. At scale you want the prefill pool on H100 (compute-heavy) and the decode pool on H200 (memory-heavy) or on aggressively quantized weights.

The architecture

            ┌──────────────┐
  Request → │    Router    │ ───────────────────────┐
            └──────┬───────┘                        │
                   │                                │
                   ▼ (prompt only)                  │
            ┌──────────────┐    KV cache    ┌───────▼──────┐
            │ Prefill pool │ ─── NIXL ────► │ Decode pool  │
            │  (compute)   │                │  (memory)    │
            └──────────────┘                └──────┬───────┘
                                                   │ tokens
                                                   ▼
                                                 Client

NIXL is NVIDIA's inter-node transport. It uses RDMA/InfiniBand when available and falls back to TCP otherwise. The transfer latency is real: typically 20-80 ms for the KV cache of a 4K-token prompt on 70B FP8. That is why short prompts don't justify disaggregation. The transfer tax exceeds the savings.

Dynamo vs llm-d

NVIDIA Dynamo (GTC 2025 announce, 1.0 GA):

  • Sits above vLLM, SGLang, and TRT-LLM as an orchestrator.
  • The Planner Profiler measures the workload and the SLA Planner auto-configures prefill:decode ratios.
  • Rust core, Python extensibility.
  • Throughput gains: NVIDIA reports 6x for DeepSeek-R1 MoE on GB200 NVL72 + Dynamo in the medium-latency regime (developer.nvidia.com, 2025-06). Community reports of "up to 30x" on full Blackwell + Dynamo + DeepSeek-R1 stacks lack a single primary source, so read them as directional.
  • GB300 NVL72 + Dynamo: up to 50x MoE throughput vs Hopper per the Dynamo product page (developer.nvidia.com, undated).

llm-d (Red Hat + AWS, Kubernetes-native):

  • Prefill, decode, and router run as independent Kubernetes Services.
  • Per-role HPA scales on queue depth (prefill) and KV utilization (decode).
  • topologyConstraint packDomain: rack packs prefill+decode cliques onto the same rack for high-bandwidth KV transfer.
  • llm-d 0.5 (2026) adds hierarchical KV offloading, cache-aware LoRA routing, UCCL networking, and scale-to-zero.

Pick Dynamo if you want a managed orchestrator that sits above your engines. Pick llm-d if you want Kubernetes-native primitives and are committed to the CNCF ecosystem.

Economics

This is an internal composite, not a single published case study, so treat it as an order-of-magnitude anchor:

  • $2M/year inference spend on colocated serving.
  • Switched to disaggregated with Dynamo.
  • Same request volume, same P99 latency SLA.
  • Reported savings: $600K–$800K/year (30–40% reduction).
  • No new hardware.

I synthesized this figure from multiple customer disclosures. The closest published data points are Baseten's 2x faster TTFT and 61% higher throughput with Dynamo KV routing (baseten.co, 2025-10), and VAST + CoreWeave's projection of 60–130% more tokens/$ at a 40–60% KV hit rate (vastdata.com, 2025-12). The savings come from right-sizing each pool, so prefill-heavy workloads (RAG with 8K+ prefixes) benefit more than balanced ones.

When NOT to disaggregate

  • Prompts under 512 tokens and outputs under 200 tokens: the transfer tax dominates the gain.
  • Small cluster (under 4 GPUs): not enough pool diversity.
  • A team that cannot operate two GPU pools with per-role scaling. Dynamo helps, but not trivially.
  • No RDMA fabric: the TCP transfer tax is heavier.

The router integrates with Phase 17 · 11

Disaggregated routers are KV-cache-aware (Phase 17 · 11). A request lands on the decode pool that holds its prefix, and if nothing matches it flows prefill to decode. Hit rate and disaggregation compound, because the cache-aware router decides whether a new prefill is needed at all.

MoE on Blackwell is where the big numbers are

GB300 NVL72 + Dynamo shows 50x MoE throughput over Hopper baselines. MoE expert routing is compute-heavy on prefill but memory-heavy on decode (expert caches), so disaggregation is a double win. Frontier model serving in 2026 is MoE-dominant (DeepSeek-V3, future GPT-5 variants).

Numbers you should remember

Benchmark numbers drift. NVIDIA and the inference stack vendors post updated results every quarter, so re-check before quoting.

  • DeepSeek-R1 on GB200 NVL72 + Dynamo: ~6x throughput vs baseline in the medium-latency regime (developer.nvidia.com, 2025-06). Community "up to 30x" claims on full Blackwell + Dynamo stacks are directional aggregates without a single primary source.
  • GB300 NVL72 + Dynamo: up to 50x MoE throughput vs Hopper (developer.nvidia.com, undated).
  • Savings anchor (internal composite, not a single case study): $600-800K/year off a $2M annual spend at constant SLA.
  • Disaggregation threshold: prompts >512 tokens + outputs >200 tokens.
  • KV transfer via NIXL: 20-80 ms for 4K-prompt KV on 70B FP8.
Check: The Concept0/24
1. What is NIXL?Define
A term card showing NIXL, tagged as named in the architecture diagram from the Concept section, with a question asking what it is.
The term as the lesson names it; its role is the question.
2. What is NVIDIA Dynamo?Define
A term card showing NVIDIA Dynamo, tagged with its GTC 2025 announcement and current 1.0 GA status, with a question asking what it is.
The announcement date and GA status are from the lesson text; what it does is the question.
3. What is llm-d?Define
A term card showing llm-d, tagged as the other stack compared against Dynamo in the lesson, with a question asking what it is.
Locational tag only; what llm-d is, concretely, is the question.
4. What does Dynamo's Planner Profiler do?Define
Two bare component names, Planner Profiler and SLA Planner, with Planner Profiler highlighted and marked with a question mark, asking what it does.
Names only, taken from the lesson; neither is defined in the picture.
5. What does Dynamo's SLA Planner do?Define
Two bare component names, Planner Profiler and SLA Planner, with SLA Planner highlighted and marked with a question mark, asking what it does.
Names only, taken from the lesson; neither is defined in the picture.
6. What does the llm-d setting `topologyConstraint packDomain: rack` do?Define
A YAML snippet reading topologyConstraint, packDomain: rack, with a question asking what the setting does.
The setting as it is quoted directly in the question.
7. What is UCCL in llm-d 0.5?Define
A term card showing UCCL, tagged as one addition in llm-d 0.5, with a question asking what it is.
Locational tag only; what UCCL is, concretely, is the question.
8. What is MoE expert routing?Define
A term card showing MoE expert routing, tagged as named in Key Terms, with a question asking what it means.
Locational tag only; the mechanism is the question.
9. Why do prefill and decode want different optimal GPU configurations?Explain
Two boxes, Prefill and Decode, each with two unlabeled resource meters, where Prefill fills the first meter heavily and the second lightly, and Decode does the opposite.
Resource names are withheld; the question is which meter each phase saturates and why one GPU wastes the other.
10. Why does decode end up memory-bandwidth-bound?Explain
A decode step box that produces one new token pointing to a box asking what it reads, repeated for every step in a row.
Setup: one token out per step. What goes in is the question.
11. In the disaggregated architecture, what travels from the prefill pool to the decode pool, and how?Explain
Only the prefill pool and decode pool boxes with a question mark on the arrow between them, asking what crosses that link and over what transport.
Setup: just the one link between the two pools; the router and client are left out.
12. When does disaggregation NOT pay off in the simplest case?Explain
Two mostly-empty meters labelled prompt length and output length, both filled only a small amount, with a question asking when splitting the GPUs does not pay off in the simplest case.
The meters are illustrative and unlabeled with numbers; filling in a threshold is the question.
13. Besides short prompts and short outputs, which conditions argue against disaggregating?Explain
Three boxes, cluster size, network fabric and team readiness, each with a blank value, with a note that the short-prompt and short-output case is covered elsewhere, asking which of the three also argues against disaggregating.
Setup only: three candidate factors, none filled in, and the prompt-length case explicitly excluded.
14. What is the core architectural difference between Dynamo and llm-d?Explain
Two boxes, Dynamo and llm-d, each with a blank for its architecture, asking for the core architectural difference between them.
Names only; the architecture of each is the question.
15. Which set of features did llm-d 0.5 add?Explain
A single box labelled llm-d 0.5 with a blank for what is new in this release, asking which set of features shipped.
No features are itemized; the full set is the question.
16. A forum post claims Dynamo delivers "up to 30x" throughput. How should that be read next to NVIDIA's own published figures?Explain
A forum quote claiming Dynamo delivers up to 30x throughput, placed next to NVIDIA's own published figures of about 6x on GB200 NVL72 and up to 50x MoE on GB300 NVL72, asking how the forum figure should be read.
All three figures are taken directly from the lesson's own text and the question's options.
17. How does disaggregation interact with cache-aware routing?Explain
A new request splitting into two branches, one where a decode replica already holds its prefix and one where no replica holds it, both left blank, asking how cache-aware routing interacts with disaggregation.
Setup: the branch point only. Both outcomes are the question.
18. How should the "$2M to $600-800K a year" savings figure in the Economics discussion be treated?Explain
A $2 million a year colocated figure next to a $600,000 to $800,000 a year figure after switching, with a note that volume, SLA and hardware are unchanged, asking how the figure should be treated.
Both dollar figures are taken directly from the question and the lesson's Economics section.
19. A request arrives and no decode replica holds a matching prefix. What path does it take through the disaggregated system?Explain
A five-step chain starting at Router and ending at Client, with the three middle steps left blank as question marks, for a request with no matching cached prefix.
Router and Client are the known endpoints; the three steps between them are the question.
20. A team moved a chatbot (about 300-token prompts, 100-token outputs) onto 3 GPUs split into two pools over plain TCP. Costs did not fall and latency rose. Which diagnosis fits?Analyze
Three facts about a chatbot migration: roughly 300-token prompts and 100-token outputs, 3 GPUs split into two pools, and plain TCP with no RDMA, with a result that costs did not fall and latency rose, asking which diagnosis fits.
All three facts are taken directly from the question.
21. In llm-d, prefill and decode pods are scheduled on different racks and KV transfer is slower than expected despite RDMA. Which change fits the lesson's design?Analyze
Prefill pods on rack 1 and decode pods on rack 2, linked by RDMA across racks, with a note that the transfer is still slower than expected, asking what change fits.
Setup: the cross-rack placement, taken directly from the question.
22. A pure Kubernetes shop wants per-role autoscaling with Kubernetes-native primitives and has no preference for a particular orchestrator. Which stack fits best?Design
A requirements box for a Kubernetes-only shop that wants per-role autoscaling and Kubernetes-native primitives with no orchestrator preference, asking which stack fits best.
Requirements taken directly from the question; no stack name is shown.
23. You are sizing pools for a RAG service with a P99 prefix of 8K tokens and 300-token outputs, on a cluster with an RDMA fabric. Which design follows from the lesson?Design
A RAG workload box with an 8K-token P99 prefix, 300-token outputs and an RDMA fabric, with blanks for the prefill pool, the decode pool and the link between them.
Workload parameters taken directly from the question; the pool design is the question.
24. Team A serves RAG with P99 prompts of 8K tokens and 300-token outputs on 16 GPUs with an InfiniBand fabric. Team B serves a chatbot with about 300-token prompts and about 100-token outputs on 3 GPUs over plain TCP. Which should disaggregate?Design
Two team cards: Team A running RAG with an 8K P99 prefix and 300-token outputs on 16 GPUs with InfiniBand, and Team B running a chatbot with about 300-token prompts and 100-token outputs on 3 GPUs over plain TCP, asking which should disaggregate.
Both teams' parameters are taken directly from the question.

Use It

main.py simulates colocated vs disaggregated serving for a single request. It prints per-request latency for colocated, disaggregated over RDMA, and disaggregated over TCP, plus which side wins, across a sweep of prompt lengths.

Check: Use It0/6
1. `main.py` uses 125,000 bytes of KV cache per token for 70B FP8. How large is the KV cache for a 4,096-token prompt?Apply
A terminal showing the constant 125,000 bytes per token and a line asking for 4,096 tokens times that many bytes.
The constant is copied from main.py; the product is the question.
2. Using the KV size for a 4,096-token prompt (about 512 MB), how long does `main.py` estimate the transfer takes over TCP at 10 GB/s?Apply
A terminal showing the 512 MB KV size given in the question, the TCP transport rate of 10 GB/s, and a line asking for the transfer time in milliseconds.
The KV size is given directly in the question; the TCP rate is main.py's own constant.
3. With prefill at 40 tokens per ms, how long does `main.py` model prefill for an 8,192-token prompt?Apply
A terminal showing the prefill rate of 40 tokens per ms and a line asking for 8,192 tokens divided by that rate.
The rate is copied from main.py; the quotient is the question.
4. In `main.py`, how long does a colocated request take with a 256-token prompt and 100 output tokens (prefill 40 tokens/ms, colocated decode 0.10 tokens/ms)?Apply
A terminal with prompt 256, output 100, prefill rate 40 tokens per ms, and colocated decode rate given, asking for the total colocated time.
All four numbers are given directly in the question.
5. The sweep in `main.py` shows disaggregated serving winning every row, even 256 prompt tokens with 100 output tokens, so no crossover appears. Which constant most explains that?Analyze
A table of all 8 rows from a real run of main.py: prompt and output lengths with colocated, RDMA and TCP times in milliseconds, where disaggregated wins every row including the shortest.
Captured by running the lesson's own main.py, unmodified. Which constant explains the pattern is the question.
6. You set the decode-pool rate in `main.py` equal to the colocated rate (0.10 tokens/ms) and rerun the sweep. What should the table show?Analyze
A terminal showing the decode-pool rate hypothetically changed from 0.18 to 0.10, equal to the colocated rate, with a question for the rerun result.
The constant change is exactly what the question describes; the lesson's shipped main.py still uses 0.18.

Ship It

This lesson produces outputs/skill-disaggregation-decider.md. Given a workload and a cluster, it decides whether to disaggregate.

Author's Note

I'd run main.py first and read the table with suspicion. Disaggregated wins on all eight rows, including 256 prompt tokens with 100 output tokens, so the sweep never shows the crossover that the When NOT to disaggregate list describes. The reason is in the constants: the decode pool is given 0.18 tokens/ms against 0.10 colocated, and that faster decode outweighs the transfer on every row.

The transfer itself is small in this model. At 4096 tokens the KV cache is 4096 x 125,000 bytes, about 512 MB, which is roughly 5 ms over RDMA at 100 GB/s and 51 ms over TCP at 10 GB/s. The TCP column is where the cost shows up. Set DECODE_TOK_PER_MS_DECODE_GPU equal to the colocated rate and colocated wins everywhere, since the only difference left is the transfer.

It also times one request alone. There is no queue, no prefill interfering with decode, and no idle GPU, so the waste from the Problem section is not modeled at all. I'd use the script as a calculator for KV transfer cost (it is the arithmetic behind exercise 4) and get the real decision from your own prompt and output length distribution.

Exercises

  1. Run main.py. At what prompt length does disaggregation beat colocation? The shipped cases never flip, so change the constants or the sweep until they do.
  2. Design the prefill pool and decode pool for a RAG service with P99 prefix length 8K, output 300.
  3. Dynamo vs llm-d: pick one for a pure-Kubernetes shop with no Python runtime preference.
  4. Compute KV transfer cost: 4K prefill on 70B FP8 = ~500 MB KV. At RDMA 100 GB/s, transfer = 5 ms. At TCP 10 GB/s = 50 ms. Which matters for your SLA?
  5. MoE expert routing changes KV access patterns. How does disaggregation behave with MoE that activates different experts per token?

Key Terms

TermWhat people sayWhat it actually means
Disaggregated serving"split prefill/decode"Separate GPU pools for each phase
NIXL"NVIDIA transport"Dynamo's inter-node KV transfer (RDMA/TCP)
NVIDIA Dynamo"the orchestrator"Stack-above coordinator for vLLM/SGLang/TRT-LLM
llm-d"Kubernetes native"Red Hat + AWS K8s disaggregated stack
Planner Profiler"Dynamo auto-config"Measures workload, configures pool ratios
SLA Planner"Dynamo policy"Auto-rate-matches prefill:decode to meet SLOs
packDomain: rack"llm-d topology"Pack prefill+decode on same rack for fast KV
UCCL"unified collective"llm-d 0.5 networking layer for scale-to-zero
MoE expert routing"expert per token"DeepSeek-V3 pattern; disaggregation helps

Further Reading

Code

Ships With