Docs · how to

Disaggregate prefill and decode

Split a release into a prefill pool and a decode pool inside one scale-up domain, size them, and watch the two latencies they control.

Prefill processes the prompt in one compute-heavy pass. Decode produces output one token at a time, limited by memory bandwidth. In an aggregated replica they share GPUs and slow each other down. A disaggregated release runs them in separate pools, each with its own parallelism, and moves the KV cache from prefill to decode over the domain's fabric.

When to use it

Use disaggregated Stay aggregated
Large mixture-of-experts models that need many GPUs for decode Models that fit on one server
Long prompts, such as agents with large contexts Short prompts and short outputs
You need predictable inter-token latency under mixed load You want the simplest setup
Your engine supports it (see Engines) Your engine does not

What the engine gives you and what denv adds

DeepInfer's README says a KV cache system, prefill/decode disaggregation and load balancing across data-parallel replicas are required for a complete inference service but are not included in its release, while most of the engine functionality disaggregation needs is already implemented. Other engines vary.

denv supplies the operator side:

  • Placing both pools of a serving unit inside one scale-up domain.
  • Starting, health-checking and replacing each pool.
  • Routing each request to a prefill replica, then to the decode replica that receives its KV cache.
  • Sizing pools separately and scaling serving units within the stage's replicas.
  • Rolling out a new release pool by pool, with checks.

See Engine vs service.

Example: DeepSeek-V4.1-Flash on GB200 NVL72

DeepInfer's published benchmark used 4 GPUs for prefill and 64 for decode, each within one scale-up domain. The catalog recipe follows that layout:

release:
  model: ds-flash
  weights: hf://deepseek-ai/DeepSeek-V4.1-Flash-1017@c41a7e2
  recipe: deepseek-v4.1-flash/deepinfer@3
  topology:
    mode: disaggregated
    prefill: { gpus: 4, replicas: 2 }
    decode: { gpus: 64, replicas: 1 }
    placement: same-domain

One serving unit here is two prefill replicas and one decode replica: 72 GPUs, which fills one NVL72 domain. replicas: 2 on the stage means two domains.

domain 1 (72 GPUs)                      domain 2 (72 GPUs)
  prefill 4 GPUs  --KV-->                 prefill 4 GPUs  --KV-->
  prefill 4 GPUs  --KV-->  decode 64      prefill 4 GPUs  --KV-->  decode 64

Sizing the pools

There is no universal ratio. Start from the recipe and adjust from what you measure:

  • Prompts long, outputs short: prefill is the bottleneck. Add prefill replicas.
  • Prompts short, outputs long: decode is the bottleneck. Keep prefill small.
  • Many requests share a prefix: KV cache hits skip prefill work. Fewer prefill replicas may be enough. See Configure the KV cache.

Each change is a new release, so it goes through a canary like any other.

Placement

placement: same-domain (the default for disaggregated) keeps a serving unit's pools inside one domain, so the KV cache moves over NVLink rather than the network. The dry run refuses a stage whose fleets have no domain large enough:

denv release --dry-run
error  stage prod-us: fleet h200-use1 domain is 8 GPUs; serving unit needs 72 (same-domain)

Use placement: any only if your engine and network support KV transfer between domains and you have measured it.

Watch the two latencies

denv metrics ds-flash/prod-us
Metric Controlled mostly by If it rises
Time to first token (ttft-p99) Prefill pool, queueing, KV cache hits More prefill replicas, better prefix sharing
Inter-token latency (itl-p99) Decode pool More serving units, check decode batch size

Set alarms on both so a release that shifts the balance rolls back:

alarms: { ttft-p99: 1500ms, itl-p99: 60ms, error-rate: 1% }

See Serving topology and Rollout policy.