Inference economics · P/D disaggregation

Should you split prefill and decode? The math of inference cost

Disaggregation is sold as a way to keep the two phases of inference from slowing each other down. A cloud provider cares about something else: how many GPU-hours the same stream of requests will take. We built a simple model out of resources, bottlenecks and packing, and checked when separate pools save money and when they only leave hardware idle.

In short
  • On one GPU type, separate pools cannot be cheaper than a shared one, unless the phases interfere more than they would running in turn. This follows from prefill and decode hitting different resources: once split, each pool underuses what the other needs.
  • There are two reasons to pay for a split: specialization and synchronization. A separate pool can run differently: with another parallelism layout, another batch size, on other hardware. If that makes it s times faster, the split pays off when s > (1+ρ)/(1+θρ). And if the interference θ exceeds one, as it does with wide-EP, the split pays off even without specialization. The letters are explained below.
  • The phase balance is set not by the cache hit rate but by the ratio of uncached input to output. In our measurements a GPU does prefill about 12 times faster than decode. So an agent with a 90% hit rate is still almost balanced, and only at about 99% does prefill become negligible.
  • A large MoE on a rack is a special case. Wide-EP decode synchronizes dozens of GPUs at every layer, and a slice of prefill stalls them all. There the split wins through specialization, not through an “absence of interference”.
  • At a high cache hit rate the architecture barely matters. Cost is set by HBM capacity and bandwidth, by how long KV lives in memory, and by routing that brings a session back to its cache.

01 · THE QUESTIONA claim first, then the question: is it cheaper?

A language model’s work on a request splits into two phases. First the model reads the whole prompt (prefill), then it writes the answer one token at a time (decode). A common piece of advice is to give the phases different GPUs, so that one user’s long prompt does not slow down everyone else’s generation. Latency gets smoother that way. This is what is called disaggregation, or P/D.

For latency the advice works. But a provider sells tokens and pays for GPU-hours. It tries to reuse as much of the KV cache as it can: whatever has already been computed is not computed again. Agent sessions with a long context and short additions push the reuse share to 90–99%. Then there is little prefill work left, and a separate prefill pool risks sitting idle. In a shared pool it is enough to scale one resource: the one that is short.

In this article we build a model that answers the question quantitatively. It is the same problem as classic cluster planning, where servers are packed by CPU and memory. Jobs have different resource profiles, machines have a capacity for each resource, and the idle part of the hardware costs money. The terms we use (prefill, decode, KV cache, cache hit, HBM, SLO) are collected in a glossary at the end of the article.

02 · TWO PHASESWhat each phase consumes

Why think about this: everything that follows rests on the phases eating different things. Prefill is large matrix multiplications, where every byte of weights read from memory is used for thousands of tokens at once. Decode reads the same weights at every step for the sake of one token in each sequence of the batch. That is why the same GPU processes an order of magnitude more tokens per second in prefill than it produces in decode.

GPU resource load by phaseillustration

Prefill

The whole prompt in one pass

compute
HBM bandwidth
HBM capacity
bound by arithmetic · ≈ 82K tok/s per GPU

Decode

One token per step for every sequence

compute
HBM bandwidth
HBM capacity
bound by memory · ≈ 6.9K tok/s per GPU
Bar lengths are an illustration, not a profiler. The speeds in the bottom line are our measurements of DeepSeek-V4.1-Flash-1017 on 8×B300 in vLLM: prefill 658K tok/s per node on 8K prompts and decode 55K tok/s per node at 1K → 1K with 2,048 concurrent requests, both divided by 8 GPUs.

These two measurements give the main constant of the article. Call it κ (kappa): how many times faster a GPU does prefill than decode. For us κ ≈ 82K / 6.9K ≈ 12. In other words, one output token costs about as much GPU time as 12 uncached input tokens.

What it means. The phases do not compete for the same resource. Prefill loads the arithmetic and leaves memory bandwidth idle. Decode loads memory bandwidth and capacity and leaves the arithmetic idle. Such workloads are worth packing together.

03 · THE BOTTLENECKResources as vectors: the bottleneck law

Why a model is needed: without one, the “together or apart” argument comes down to comparing charts from different papers. A model shows where a gain comes from and lets you compute it on your own numbers.

Describe each class of requests (RAG, reasoning, agent) by a resource vector: how many seconds of one GPU’s work it takes on each resource. There are four resources:

Every GPU provides exactly one “GPU-second” of each resource per second. Then the number of GPUs the stream needs, under ideal packing (no queues, no bursts), is:

N = maxrΣj λj · aj,rCr

Here j is a request class, λj is how many such requests arrive per second, aj,r is how much of resource r one request needs, and Cr is how much of resource r one GPU provides. The maximum is taken over resources: the fleet is set by the resource that runs short. The other resources are bought but partly idle.

This is the same law by which servers are packed in a data center. If some jobs need a lot of CPU and little memory and others the reverse, it pays to put them on the same machines. Separate “CPU servers” and “memory servers” would leave half the hardware unused. Schedulers like Borg and the whole theory of multidimensional packing are built on this observation.

What it means. “Should the phases be split” is a special case of “should jobs with different resource profiles be packed together”. That question has a known answer, and the next section derives it.

04 · THE THEOREMWhy splitting is not cheaper on one GPU type

Write the prefill phase’s total demand for each resource as Pr and the decode phase’s as Dr. A monolith covers the sum of the demands; separate pools on the same GPUs cover each phase on its own. The maximum of a sum is at most the sum of the maxima, so:

Ntogether = maxrPr + DrCr
Nsplit = maxrPrCr + maxrDrCr
Nsplit ≥ Ntogether · equality only when both phases share one bottleneck

Prefill’s bottleneck is compute, decode’s is memory. So on identical hardware separate pools always leave more resources idle. In the best case the shared pool costs half as much: that happens when both phases load the GPU equally.

The “under ideal packing” caveat matters. The theorem assumes that prefill’s arithmetic and decode’s memory reads run on the GPU at the same time without getting in each other’s way. In reality they do, and that is the next section. It also shows that with strong enough interference the inequality flips.

What it means. Disaggregation by itself does not save GPUs. Where it does save, the source of the saving is something else, and it has to be named explicitly.

05 · INTERFERENCEθ: how much the phases really get in each other’s way

Why another letter: it turns the word “interference” into a number that can be measured. When the engine puts both decode and a chunk of someone else’s prefill into one step (this is called chunked prefill), the step takes longer than pure decode. Describe the length of such a step as:

T = max(Tcomp, Tmem) + θ · min(Tcomp, Tmem)

Tcomp is how long the step would take if it were bound by arithmetic alone, Tmem by memory reads alone. The coefficient θ (theta) is non-negative. At θ = 0 one phase’s work hides entirely in the other’s idle time: prefill rides along with decode for free. At θ = 1 the phases run strictly in turn, as if the GPU split its time between them. θ can also exceed one: that happens when the engine synchronizes many GPUs at every step, and a chunk of prefill on one of them holds up all the others (section 07). Then a shared step takes longer than the two phases in turn.

Now normalize the load. U is how many GPUs would be needed if they did nothing but the prefill of the whole stream. V is the same for decode. Then:

Ntogether = max(U, V) + θ · min(U, V)
Nsplit = U + V
how much prefill and decode one GPU gets donesquare versus chord
0 0 0.5 0.5 1 1 prefill, share of the GPU maximum decode, share of the GPU maximum co-location, θ = 0: the whole square θ = 0.5 separate pools = co-location at θ = 1 load (0.6, 0.6)
The axes are the share of what the GPU gets done when busy with one phase only. Separate pools give the straight line: each GPU does either one or the other. Ideal co-location (θ = 0) is the whole square: the GPU does the maximum of both phases at once. Real co-location lies in between. The white dot is the load (0.6, 0.6): one shared GPU with a small θ is enough for it, separate pools need 1.2 GPUs.

What is θ in practice? The best data point we know is the paper “Not All Prefills Are Equal” (PPD, 2026): Llama-3.1-8B on one H100, 1,024-token prefill. At a batch of 200, a full prefill of a new prompt increased the decode step time (TPOT) by 48%, while a short addition to an already cached context increased it by only 2%. So θ depends on the kind of prefill: for an agent’s short additions it is close to zero, for long cold prompts it is large.

What it means. Long cold prompts really do interfere with decode, and they are worth moving away. Short additions to a warm context are better left on the decode GPU next to their cache. That is how PPD’s hybrid approach and Together AI’s cold/warm prefill split are built.

06 · SPECIALIZATIONs: the first reason to pay for a split

At s = 1 the formulas above always say “together”. Separate pools can win only when each of them works more efficiently than it would in the shared pool. Call that gain s: how many times faster a specialized pool is on its phase. Where it comes from:

The last letter is ρ (rho), the phase balance: the smaller of U and V divided by the larger. ρ = 1 means the phases need equal numbers of GPUs, ρ → 0 means one phase has almost disappeared. Comparing U/s + V/s with the monolith’s formula gives the condition (for simplicity we assume both pools gain equally; in general the split scheme costs U/sP + V/sD, and sP may turn out to be below one):

splitting is cheaper when s > 1 + ρ1 + θ·ρ
the decision boundary for several θs = (1+ρ)/(1+θρ)
0.8 1.0 1.2 1.4 1.6 1.8 2.0 0 0.25 0.50 0.75 1 ρ, phase balance: 0 = one phase only, 1 = equal s, specialization gain θ = 0 θ = 0.25 θ = 0.5 θ = 1 θ = 1.5 (wide-EP) above your curve: cheaper split below: cheaper together
Each θ has its own curve. Above the curve separate pools are cheaper, below it the shared pool is. At θ = 1 the curve is the horizontal s = 1, and the split pays off with any specialization at all. At θ > 1 (the wide-EP case, section 07) the curve drops below one: the split pays off even if the pools are no better.

What the map shows:

What it means. The “keep them apart so they don’t interfere” argument is about latency. For cost the question is different: does specialization give the pool a gain s that exceeds the loss from worse packing. The map shows exactly how much has to be gained.

07 · LARGE MoEWhy DeepSeek-class models get split anyway

Why a separate section: large model providers like DeepSeek run their models precisely with disaggregation, and the model has to explain that. Two terms first.

MoE (mixture of experts) is a model in which every layer consists of hundreds of “experts”, and every token passes through only a few of them. Expert parallelism (EP) is a layout in which the experts are spread across GPUs, and at every layer tokens are sent to wherever their experts live. Wide-EP is the same across dozens of GPUs: each holds fewer experts, so at a decode step it reads fewer weights.

node versus rack: where the experts liveDeepSeek-V4.1-Flash
One node · 8×B300 · DEP8 shared pool: both phases on every GPU attention 48 exp. attention 48 exp. attention 48 exp. attention 48 exp. attention 48 exp. attention 48 exp. attention 48 exp. attention 48 exp. NVSwitch · all-to-all at every MoE layer every GPU: its own copy of attention, its own KV cache and 48 of 384 experts; every step waits for all 8 ranks Rack · GB300 NVL72 · 72 GPUs vLLM 8P1D layout · one NVLink domain prefill · 8 DEP2 instances · 16 GPUs 192 experts per GPU, no spares decode · 1 DEP32 instance · 32 GPUs 12 experts per GPU + EPLB spares 24 GPUs unused in that benchmark How many experts one GPU reads per decode step 384 routed + 1 shared per layer · 6 per token · the wider the EP, the fewer weights per GPU DEP2 192 prefill DEP4 96 MoE part of the step 13.4 ms DEP8 48 our node DEP32 12 MoE part of the step 6.0 ms MoE-part times: vLLM post, GB200, 64 requests per GPU, 128K context · the rest of the step ≈ 10 ms
On the left, our node: eight copies of attention, each with its own KV cache and 48 of the 384 experts; at every MoE layer the tokens travel to their six experts over NVSwitch and back. On the right, vLLM’s layout on a GB300 NVL72 rack in the 8P1D configuration: eight prefill instances of 2 GPUs and one decode instance of 32 GPUs, with the remaining 24 GPUs free in that benchmark. The wider the EP, the fewer experts and weights per GPU per decode step, and the more GPUs wait for the slowest one.

So for the DeepSeek class on a rack, θeff > 1 and s is well above one. Both reasons push toward splitting. But that is a gain from specialization and synchronization, not a consequence of the slogan “the phases interfere with each other”.

On a single node of 8 GPUs the choice is different, although the interference is there too. Our measurement on 8×B300 showed that vLLM in one shared DEP8 configuration delivers 658K tok/s of prefill and 55K tok/s of decode, and on an 8K → 1K mix the same node produces 29.5K tok/s of decode while spending 36% of its prefill capacity. If decode at 8K context is no slower than at 1K, the formula of section 05 turns these numbers into θ ≈ 1.3: the eight DEP8 ranks synchronize just like thirty-two do. But splitting such a node into 2 GPUs for prefill and 6 for decode would mean losing wide-EP on both sides, that is, getting s < 1 on decode. Here the split loses not because there is no interference, but because specialization is impossible on 8 GPUs.

What it means. Disaggregation is a tool of scale. It pays off where a decode instance spans dozens of GPUs. For a single node, a dense model or a small MoE, the default choice is a shared pool with chunked prefill.

08 · EXAMPLEThree workload types on the same numbers

Why an example: to see how the formulas behave at realistic request sizes. The GPU speeds are taken from our measurement (82K tok/s prefill and 6.9K tok/s decode per GPU, KV 3,309 bytes per token, 160 GiB for KV per GPU). The workloads themselves are an illustration:

GPUs per stream, ideal packingillustration on our speeds
together, θ = 0.2 together, θ = 0.5 split, s = 1 RAG ρ = 0.30 15.5 16.8 19.0 Reasoning ρ = 0.01 11.6 11.7 11.7 Agent, hit 90% ρ = 0.84 6.8 8.2 10.7 Agent, hit 99% ρ = 0.08 5.9 6.0 6.3 Mix of three ρ = 0.90 25.7 31.6 41.4
The monolith at θ = 0.2 and 0.5 against separate pools without specialization. ρ is each scenario’s phase balance. The table below has the resource vectors and the required specialization gain s*.
Resource vectors and the s* threshold by scenario
ScenarioComputeHBM bandwidthHBM capacityρTogether θ=0.2Together θ=0.5Split s=1s*, θ=0.2…0.5
RAG14.64.30.20.3015.516.819.01.13…1.22
Reasoning0.111.60.40.0111.611.711.71.01…1.01
Agent, hit 90%4.95.83.20.846.88.210.71.30…1.58
Agent, hit 99%0.55.83.20.085.96.06.31.04…1.07
RAG + reasoning + agent 90% mix19.621.73.80.9025.731.641.41.31…1.61

The numbers are GPU-equivalents per second. The decode pool is bound by the larger of the two HBM resources. s* is how many times faster the pools must become for the split scheme to match the shared one.

The balance formula is simple:

UV = uncached inputoutput · 1κ · the phases are balanced when there is κ times more uncached input than output

Compute it on your own numbers

GPUs together
GPUs split
phase balance ρ

The calculator uses the formulas above with our GPU speeds (82K and 6.9K tok/s, a 29 ms decode step, as in the 1K → 1K measurement) and ideal packing: no queues, no headroom, no minimum instance size. KV options: DeepSeek-V4.1-Flash (FP8), GLM-5.3-Flash (BF16), a hypothetical midpoint, and a dense 70B model with GQA (BF16). The prefill and decode speeds do not change with them; that is a deliberate simplification, so that the effect of KV size alone is visible.

What it means. Look not at the cache hit rate but at the ratio of uncached input to output. Compare it with κ on your hardware, and you will know which corner of the section 06 map your stream is in.

09 · PRICESShadow prices: what a token of each phase really costs

Why: a provider charges different prices for input, cached input and output. The model shows what those tokens cost the provider itself.

Choosing a fleet is a linear programming problem. There is a set of possible configurations (hardware × role × parallelism × batching policy), each with its own hourly price and its own prefill and decode throughput. Demand has to be covered at the minimum total. Such a problem has two useful properties:

Compare with the price list. DeepSeek’s public prices for V4.1-Flash: $0.30 per 1M uncached input tokens, $0.006 for cached ones, $1.20 for output. Taking a notional $9.50 per GPU-hour and our speeds at full load, compute costs about $0.03 per 1M prefill tokens and about $0.38 per 1M decode tokens. An illustration: no headroom for peaks, SLOs or idle time.

What it means. The price of cached input is a price of storage, not of compute. The economics of agent inference is set by where and how long KV sits, not by whether the pools are split.

10 · HEADROOM AND DRIFTWhat real, uneven traffic takes away

Why: so far we have assumed ideal packing under a steady stream. A real stream fluctuates, and the fluctuations need headroom. Here the shared pool has two more advantages.

Hence the practical option: flexible roles, nodes with one model that switch between prefill and decode when the mix changes. The price of a switch is the time it takes to restart the engine with a different profile. For DeepSeek-V4.1-Flash on 8×B300 a vLLM cold start took us 930 s, and a restore from a warm warmctl snapshot 88 s. That is not an argument for or against P/D, but the condition under which a flexible fleet works at all.

What it means. Even where a split pays off on average, a fixed P:D ratio is a bad idea. A fleet that moves the boundary between the pools as the cache hit rate moves is the better deal.

11 · KVKeep the cache or recompute it

Why: at a high cache hit rate the main expense is storing KV between turns. A rule is needed for where and how long to keep it. Two simple thresholds.

Load or recompute. Loading KV over the network is faster than recomputing it if the link is faster than B* = (KV bytes per token) × (prefill speed). For DeepSeek-V4.1-Flash that is ≈ 0.25 GB/s per GPU, for GLM-5.3-Flash ≈ 1 GB/s. For a dense 70B model with GQA at the same prefill speed it would be ≈ 27 GB/s; at the 30–50K tok/s per GPU realistic for it, 10–16 GB/s. For models with a compact KV any server network beats recomputation, and moving KV between pools costs next to nothing: an 8K-token prompt is 27 MB.

How long to keep it. A session’s KV is worth keeping in memory tier i for a pause of length T if the expected saving on recomputation exceeds the cost of storage:

keep while T < T* = preturn · Crecomputeci · KV bytes

Here preturn is the probability that the session comes back, Crecompute is the cost of a repeat prefill, and ci is the price of storing a byte for a second in tier i (HBM, host DRAM, local NVMe, remote storage). For HBM the formula simplifies: T* equals the time in which the GPU, prefilling, would fill its whole KV budget. For DeepSeek-V4.1-Flash that is about 10 minutes at p = 0.9. For a dense model with a hundred times larger KV it is seconds, which is why DRAM and NVMe are needed there.

A subtlety: ci is a shadow price. If HBM is not full, storing in it is free, and the cache is worth keeping to the limit. The price appears only when HBM capacity becomes the bottleneck. For agents with long pauses that happens first.

What it means. New models with a KV of a few kilobytes per token shift the economics from recomputation to storage. Memory tiers and routing that brings a session back to its cache give more than the choice between a monolith and P/D.

12 · LITERATUREWhat the papers actually showed

Why: most of the headline multipliers were obtained against a specific, often old, baseline. They cannot be added or multiplied; each paper has its own model, hardware, lengths and metric.

More sources and their baselines
WorkWhat it showedAgainst what
ThunderServe (MLSys 2025)up to 2.1×, on average 1.7× throughput at a comparable priceHeterogeneous cloud GPUs; the gain is from heterogeneity, not from P/D as such
Llumnix (OSDI 2024)up to −36% cost at the same tail latencyMigration of requests and KV between instances; not P/D
Preble (ICLR 2025)1.5–14.5× in average latencyRound-robin with no prefix awareness; a routing effect
SGLang (NeurIPS 2024)up to 6.4× for the whole systemThe engines of the time; isolates neither the cache nor P/D
MegaScale-Infer (SIGCOMM 2025)up to 1.9× per GPUSplitting attention from experts, a different axis, not P/D
CacheBlend (EuroSys 2025)2.2–3.3× in TTFTFull recomputation; reuses non-prefix RAG fragments with partial recomputation
Cake (ICML 2025)2.6× in TTFT on averageLoading only or recomputing only; does both in parallel
vLLM, P/D + wide-EP (Oct 2026)DEP2 prefill + DEP32 decode on a rackA comparison of configurations within vLLM; numbers in section 07

What it means. The literature gives no universal winner. It gives mechanisms: specialization, heterogeneous hardware, cache-aware routing and the cold/warm split. Each is a separate term in the model, and they have to be computed on your own workload.

13 · DECISIONWhen to split

14 · NEXTWhat we want to measure

The model’s main unknown is θ for a modern MoE on a real engine. It can be estimated on one node without any disaggregation. Our published 8K → 1K run on 8×B300 produced 29.5K output tokens per second. Meanwhile the prefill of the incoming 8K prompts took about 36% of the node’s prefill capacity. If pure decode at the same context is measured separately (with prefix cache, no new prefill), the formula of section 05 yields θ. For example, pure decode at 36K tok/s would give θ ≈ 0.5, and 46K would give θ ≈ 1. And if decode at 8K context is as fast as the 55K measured at 1K context, θ ≈ 1.3: on DEP8 the phases interfere with each other more than if they ran in turn. That is the first thing worth measuring.

It is a small experiment; budget and timing to be decided later. When the numbers arrive, they go into the calculator above in place of the illustrations.


Glossary

Token
A piece of text, usually part of a word. The model reads and writes in tokens, and providers price a million tokens separately for input and output.
Prefill
The first phase: the model processes the whole prompt in one pass. Thousands of tokens go in parallel, so the GPU is busy with arithmetic.
Decode
The second phase: the model emits the answer one token per step. At every step the GPU re-reads the model weights and the accumulated context from memory. Little arithmetic per step, a lot of reading.
KV cache
The intermediate results of prefill for every token of the context (the attention keys and values, hence K and V). Decode reads them at every step, so they stay in GPU memory for as long as the request lives. Its size is bytes per token, and it differs a hundredfold between models.
Cache hit
The share of input tokens whose KV cache already exists: the same system prompt, say, or an agent’s previous turns. Those tokens need not be recomputed, and APIs sell them cheaper. The rest we call uncached.
HBM
The memory on the GPU itself. It is two different resources: capacity (how many gigabytes of weights and KV fit) and bandwidth (how many terabytes per second can be read).
Compute-bound / memory-bound
What runs out first: arithmetic (compute-bound, that is prefill) or reading from memory (memory-bound, that is decode).
TTFT, ITL
Time to first token: how long you wait for the first token of the answer. Inter-token latency: the pause between the following tokens. Prefill sets TTFT, decode sets ITL.
SLO
The promised service level, for example “ITL p95 ≤ 50 ms”. Requests that meet the SLO are called goodput.
Pool
A group of GPUs with one role. In a monolith one shared pool does both prefill and decode. With disaggregation there are two pools, and the KV cache travels from the prefill pool to the decode pool over the network.
GPU-hour, TCO
Renting one GPU for an hour is a provider’s main cost line. TCO (total cost of ownership) is the full cost: GPU-hours plus network, storage and operations.

Sources