本文目录
vLLM、SGLang 与 TensorRT-LLM 能把一组 GPU 上的模型执行得很快,但数据中心级推理还需要回答引擎之外的问题:一条请求该去哪个副本,prefill 与 decode 要不要分离,两个资源池各扩多少实例,KV Cache 在哪台 worker 上,worker 重启后怎样停止使用过期状态,以及怎样把模型服务映射到 Kubernetes 的 Pod、网络和拓扑约束。
NVIDIA Dynamo 位于这层。它不是另一个 attention kernel,也不是要替换现有 inference engine;它把不同 backend 变成可发现、可路由、可分离、可扩缩和可恢复的分布式推理系统。引擎继续负责模型 forward、Paged KV Cache、continuous batching 与 sampling,Dynamo 负责跨实例的 request、control 和 state 协作。
2025 年最初发布时,Dynamo 的核心被概括为 Planner、Smart Router、Distributed KV Cache Manager 与 NIXL。此后开源项目持续演进,增加了 Kubernetes Operator、KVBM、更多路由/部署模式和故障处理。具体 CRD 字段与命令会变化,但架构问题相对稳定。
本文不按组件逐项抄文档,而是沿一条请求经过的三条路径理解 Dynamo:
request path 谁接收请求、选择 worker、执行 P/D 并返回 token
control path 谁观察负载、决定资源数量与 placement
state path 谁跟踪 KV、worker membership、事件与故障后的失效
为什么单个高性能引擎还不够
先看最简单的多副本部署:
┌─► Engine Pod A
HTTP Gateway ──┼─► Engine Pod B
└─► Engine Pod C
如果 Gateway round-robin,请求数量可能均匀,计算成本却不均匀:
- A 已缓存请求的 20k-token 公共前缀;
- B 没有缓存,但队列为空;
- C 缓存命中最多,却正在处理多个长输出;
- 新请求是长 prefill、短回答;
- 另一个请求 prompt 很短,却会生成很久。
HTTP 层看到的都是一个连接,LLM runtime 看到的是完全不同的 future work。路由不理解 token、KV 与 active decode,就会同时错过 cache reuse 和 load balance。
再加入 P/D disaggregation:
Frontend → Prefill Pool → KV transfer → Decode Pool → token stream
系统还要选择 P 与 D 的配对、把首 token/采样状态交接、管理 KV transfer 和两个池的独立容量。普通无状态服务发现只能告诉你 Pod 存活,不能完成这套推理语义。
三条路径为什么要分开
Request Path
Request path 在每次请求和 token stream 上发生,必须尽量短、稳定:
client
│
▼
Frontend / Gateway
│
▼
Router decision
│
├─ aggregated ─► one engine worker
│
└─ disaggregated ─► P worker ─► D worker
│
▼
token stream
它负责协议、路由、执行与流式返回。Planner 不应位于每个 token 的同步关键路径,否则控制器短暂抖动会直接阻塞生成。
Control Path
Control path 在秒到分钟尺度观察:
- 请求率、input/output length;
- P queue token、D active KV 与 step latency;
- TTFT/ITL SLO;
- worker 数量和 GPU 拓扑;
- 扩容冷启动时间与成本。
Planner 根据这些信号计算目标 replica/placement,Operator 或其他 connector 把目标应用到底层资源。
State Path
State path 保存快速路由和恢复所需的软状态:
- worker membership 与 health/lease;
- 每台 worker 的 KV block/prefix 事件;
- 全局 prefix index;
- external/offloaded KV 位置;
- in-flight request 与 P/D handoff 状态。
它允许 Router 快速判断 cache overlap,也必须在 worker 失败时让过期状态及时消失。State path 可以容忍 cache miss 和重建,却不能容忍把错误 KV 当作正确 KV 使用。
三条路径的交互是:
metrics / events
Request Path ─────────────────────────► State + Control
▲ │
│ worker set / routing index / targets │
└──────────────────────────────────────┘
Request Path 从 Frontend 开始
Dynamo Frontend 可以直接作为 OpenAI-compatible 入口,也可以与 Kubernetes Gateway API Inference Extension 组合:外部 Gateway 负责认证、限流和边缘策略,Endpoint Picker Plugin 选择目标,再把流量送到相应 worker sidecar/Frontend。
两种拓扑的差别在 routing boundary:
Dynamo-native:
client → Dynamo Frontend → integrated Router → workers
Gateway API:
client → Gateway → EPP worker selection → Frontend sidecar → workers
它们不应该同时各做一次互相冲突的负载均衡。部署时要明确谁拥有最终 worker selection,哪些 request hints 可以穿透,以及 retry 后是否仍保持 session/KV locality。
Smart Router 的目标不是“命中最长前缀”
对请求 $r$ 与 worker $i$,设:
- $P_r$ 为 prompt block 数;
- $H_{ri}$ 为该 worker 已有的可复用 prefix blocks;
- $Q_i$ 为当前 prefill/decode 负载;
- $C_i$ 为 worker 容量与设备能力。
可以构造简化成本:
\[Cost(r,i)= w_p(P_r-H_{ri}) +w_d\widehat{DecodeLoad}_i +w_q\widehat{Queue}_i\]Router 选择较低成本 worker。实际 Dynamo cost model 和可调参数随版本演进,这个式子只表达原则:未命中的 prefill 计算、active decode 与排队都要计价。
只看 Cache 会形成热点
系统 prompt 首次落到 A 后,所有共享前缀请求都选 A;A 越有缓存,越被继续选择,队列越来越长,其他 worker 空闲。
cache-only positive feedback
more hits on A → more requests to A → more cache on A → more hits on A
只看 Load 会反复重算
Least-loaded 每次把请求发往空闲 worker,多轮会话可能在副本间跳动,长前缀反复 prefill。
成本模型要比较复用收益与等待
如果 A 能省 10k-token prefill 但要排队 2 秒,B 需要重算却能立即执行,谁更好取决于模型 profile 与 TTFT deadline。Router 应估计 work,而不只是把 cache overlap 当作绝对优先级。
Router 怎样知道 Worker 有哪些 KV
Event-backed Index
Worker 在 KV block allocate/store/free/evict 时发布事件,Router/Indexer 用这些事件维护全局 prefix registry,常以 radix/prefix structure 进行匹配:
worker A: block stored(hash h1, h2, h3)
worker B: block evicted(hash h1, h2)
│
▼
global KV index
│
new prompt hashes ─┴─► per-worker overlap
事件需要 worker epoch 和顺序信息。若 A 重启后重新使用同一名字,旧实例迟到的 store 事件不能污染新实例状态;event gap 也要通过 snapshot/reconciliation 修复。
Prediction-based Index
若 worker 不发布完整事件,Router 可以根据自己曾经发送的请求推测哪些 prefix 可能仍存在。这减少基础设施依赖,但 eviction、preemption 和进程内 cache policy 会让预测逐渐偏离真实状态。
错误预测通常应导致一次 cache miss 和重算,而不是读错数据。worker/connector 在实际加载时仍要验证 block 是否存在;Router 的 index 是 placement hint,不是 correctness authority。
Load-only Fallback
当 event path 不健康或某 backend 不支持 KV reporting,系统应能退回 load-aware/round-robin,而不是停止服务。降级会增加 prefill 计算,但保持 request path 可用。
Prefix Identity 必须跨 Backend 一致
Router 要比较 KV overlap,所有 worker 必须对相同执行输入得到相同 block identity。至少包含:
- 精确 token IDs,而不是原始文本;
- block boundary 与 hash chain;
- model/revision;
- tokenizer、chat template 与 special tokens;
- LoRA/adapter;
- 影响 hidden state 的位置和模型配置;
- 多模态输入的内容 identity;
- KV layout/dtype compatibility namespace。
如果 Python 进程使用随机化 hash seed,而系统直接依赖进程 hash,两个 worker 对同一 token block 可能得到不同结果。官方部署文档会对相关 backend 给出一致性配置;更稳妥的原则是使用显式、稳定、带版本的内容哈希。
Aggregated 与 Disaggregated 是两种 Request Graph
Aggregated
一个 worker 完成 prefill 和 decode:
Frontend → Router → Engine Worker(P+D) → stream
优点是权重只保存一份、KV 不跨 worker;短 prompt、低并发、小模型或慢网络下通常更简单。
Disaggregated
两个池分别执行:
Frontend → Router
│
▼
Prefill Worker
│ KV + first-token state
▼
Decode Worker → stream
Dynamo 让两个 worker pool 独立设置 replica、GPU 数和并行策略,并使用 NIXL 完成 KV transfer。P 的扩容压力主要来自 input length、context 与 prefix reuse;D 的压力来自并发、output length 和 active KV memory。
分离并不总是更快。官方文档同样建议在小模型、短 prompt、低 concurrency 或缺少高速 fabric 时比较 aggregated layout,不能把 disaggregation 当成默认正确答案。
P/D Request Handoff 传的不只是 KV Bytes
Prefill 完成后,D 需要:
- 已计算 token 数与 position;
- P 产生的 KV block descriptors;
- 第一个 token 或下一分布相关状态;
- sampling 参数与 RNG/sequence state;
- stop、grammar 与 request deadline;
- source worker identity 与 transfer epoch。
NIXL 负责 memory transfer,Dynamo/engine connector 负责这些 request metadata。两端必须使用相同模型、KV dtype、block size 与 layout;否则可能直接传输失败,更危险的是 bytes 能复制但 attention 以错误 stride/head 分片解释。
同节点可以走 CUDA IPC/NVLink 等路径,跨节点通常需要 RDMA-capable fabric 与正确 device plugin/网络配置。传输 backend 的选择是 NIXL 层问题,P/D request 生命周期属于 runtime。
KVBM 在 Engine Cache 之上补什么
Inference engine 已经有自己的 GPU KV block manager。Dynamo KVBM(KV Block Manager)的目标是把 block 生命周期扩展到更广的 memory hierarchy 与共享场景:
attention hot path
│
▼
GPU HBM blocks
│ offload / onboard
▼
pinned CPU DRAM
│
▼
local SSD / remote storage tiers
KVBM 关心:
- block layout 与 model dimensions;
- allocate/register/match 等状态;
- offload/onboard 调度;
- external store/backend;
- 与 NIXL registration/transfer 集成;
- block events 与 metrics。
它不等同于 Router 的 KV index。Router 持有“某 worker 可能有某 prefix”的轻量 registry;KVBM/engine cache 才持有实际 block 和生命周期。
它也不等同于 Mooncake Store/LMCache 等所有外部存储。Dynamo 允许不同 KV management 方案接入,具体 backend 支持和成熟度随 release 变化。设计中应明确谁是 block allocator、谁负责 eviction、谁发布 event,避免两个管理器同时认为自己拥有同一块内存。
多层 KV 的价值与代价
假设从某层加载 $S$ 个 token 的 KV 耗时:
\[T_{load}(tier,S)\]重新 prefill 耗时:
\[T_{prefill}(S)\]只有:
\[T_{lookup}+T_{load}+T_{onboard}<T_{prefill}\]复用才降低 TTFT。命中 SSD/remote storage 不一定比短 prompt 重算快。
多层 cache 还要决定:
- write-through 还是按价值选择 offload;
- HBM eviction 后是否保留 host copy;
- 热 block 是否复制;
- 长 prefix 与短热门 prefix 谁更值得容量;
- worker failure 后 external copy 是否可继续使用;
- 多租户数据能否共享。
因此 KVBM 是 memory system,不是一项无代价的开关。
Planner 为什么不能只做 HPA
通用 HPA 常用 CPU/GPU utilization 或 request count。LLM 服务真正约束的是 TTFT 与 ITL,它们受序列长度、KV capacity、P/D queue 和 transfer 共同影响。
两个流量窗口即使 request/s 相同:
Window A: ISL 500, OSL 2000 → Decode-heavy
Window B: ISL 16000, OSL 100 → Prefill-heavy
需要扩的池完全相反。单个 replicas 指标无法表达。
Dynamo Planner 消费 runtime metrics,分别计算 target prefill/decode replica。当前文档提供 throughput、latency、load 和 SLA-oriented 等优化目标;实现和算法在版本间会迭代,但控制回路可以概括为:
metrics
│
▼
estimate / predict workload
│
▼
performance model + correction factor
│
▼
target (num_prefill, num_decode)
│
▼
Kubernetes/runtime connector applies scaling
│
└──────── observed result feeds back ────────┐
│
└─► next interval
SLA Planner 的输入不能只靠 Request Count
更完整的 workload vector 是:
\[W_t=(\lambda_t, E[ISL_t],E[OSL_t], Q_{P,t},U_{KV,D,t}, TTFT_t,ITL_t)\]其中:
- $\lambda$:到达率;
- ISL/OSL:输入/输出长度分布;
- $Q_P$:prefill queue/token work;
- $U_{KV,D}$:decode active KV utilization;
- TTFT/ITL:实际 SLO 反馈。
Planner 可以用预部署 profile/AIConfigurator 的性能模型估计不同拓扑,也可以用在线观测校正预测。目标不是让所有 GPU 100% 利用,而是在 SLA 与成本之间选择容量。
扩缩容延迟让控制器必须预测
创建 GPU Pod 需要调度、拉镜像、加载数十到数百 GB 权重、建立并行组、编译/捕获 kernel 与健康检查,可能远慢于流量 burst。纯反应式规则:
queue high → start worker → worker ready after burst is over
既错过 SLO,又可能在负载下降后留下昂贵空闲副本。
Planner 需要:
- 预测下一窗口的 request/ISL/OSL;
- 把 cold-start time 加入 lookahead;
- 设置 min replica/headroom;
- 对 scale-up/scale-down 使用不同阈值;
- 加入 cooldown 与最小稳定时间;
- 让 P/D 联合变化,避免只扩一侧;
- 在模型误差增大时回退保守策略。
当前 target 与实际 ready capacity 也要分开。Kubernetes 中 replicas 已增加,不等于 worker 已加载模型并进入 Router endpoint set。
DynamoGraphDeployment 与 Operator 做什么
在 Kubernetes 中,Dynamo 用声明式资源描述 Frontend、worker 和连接关系。Operator 将期望状态转化为 deployment/pod group、service discovery 和配置。
DynamoGraphDeployment
├─ Frontend component
├─ Prefill worker component
├─ Decode worker component
├─ dependencies / endpoints
└─ scaling and placement policy
│
▼
Dynamo Operator reconcile
│
▼
Pods / Services / EndpointSlices / worker metadata
多节点 tensor/pipeline/expert parallel worker 不是一组可以任意独立重启的 Pod。它们需要 gang scheduling、共同 readiness 和 topology-aware placement。Dynamo 文档中的 Grove/PodClique 一类资源用于表达这些 worker group 和 scaling group。
这里的关键原则是:扩的是一个可工作的分布式 instance,而不是零散 GPU Pod 数。 只启动部分 rank 的 replica 会占用资源却无法接收请求。
Backend-Agnostic 的真实含义
Dynamo 支持 vLLM、SGLang、TensorRT-LLM 等 backend,意味着 request/control/state 抽象可以接入不同 engine,不意味着所有功能组合完全等价。
Connector/integration 至少要适配:
- OpenAI request 与 engine sampling schema;
- stream/cancel/error 语义;
- engine KV event;
- P/D mode 和 transfer metadata;
- block layout、dtype 与 parallel rank;
- health、metrics 与 graceful drain;
- structured output、LoRA、speculative decode 等能力。
现行架构既有 integrated backend(Dynamo worker 与 engine 同进程),也探索 sidecar 连接 stock engine 的方式。同进程能深度访问 KV 与 scheduler,升级耦合更强;sidecar 隔离版本并复用原生 server,能暴露的内部状态较少。
所以选择 backend 时应查对应版本的 feature/compatibility matrix,而不是只看首页列出的名称。
Failure 首先发生在 State 失真
Worker Crash
worker 失联后,Router 必须停止分配新请求,KV index 要移除其 block,P/D in-flight transfer 要失败或重路由,客户端请求要重试、迁移或明确终止。
Stale KV Event
一个 worker 重启后进程名相同、内存已重建。旧 epoch 的迟到事件如果仍被接受,Router 会把请求送去命中不存在的 cache。事件必须携带 instance/epoch,discovery lease 过期时清理关联状态。
Graceful Scale Down
直接删除 D worker 会中断所有 active decode:
stop new routing
│
▼
drain / migrate / finish active requests
│
▼
release KV and transfer leases
│
▼
remove endpoint and terminate
若输出很长,完全 drain 可能耗时很久。平台需要 deadline、migration 能力或可接受的取消策略。
Control Plane Failure
Planner/Operator 短暂不可用时,现有 request path 应尽量继续运行在最后一个稳定配置;否则一个 autoscaler 故障会放大成推理中断。控制器恢复后再 reconcile actual vs desired。
Overload
故障减少容量时,盲目继续接收会把排队扩散到全部 worker。load shedding/admission control 应在 deadline 已不可满足前拒绝或降级,而不是让请求超时后才释放资源。
Dynamo 与 llm-d 的边界
两者都处于 inference engine 之上的分布式/云原生层,也都关注 KV-aware routing、P/D disaggregation 与 Kubernetes,因此并不是简单的“底层库 vs 上层平台”关系。
可以从生态与控制边界理解:
Dynamo
提供自己的 distributed runtime、Frontend/Router、Planner、KVBM、NIXL 集成和 Kubernetes Platform/Operator,把数据面与控制面组成一套 NVIDIA 主导的开放推理栈,并连接多种 engine。
llm-d
围绕 Kubernetes、Gateway API Inference Extension、vLLM 与社区组件组织推理服务,强调 Kubernetes-native scheduling/routing 与可组合生态。
二者功能会有重叠,也可能共享标准或底层组件。选择时应比较:
- 当前目标 engine 与模型的成熟支持;
- 是否需要 KVBM/NIXL/Planner 的一体化路径;
- 现有 Kubernetes Gateway、调度与可观测体系;
- 多厂商硬件/软件要求;
- 升级节奏与团队可维护边界;
- 故障与数据面的实测能力。
不要同时启用两套都认为自己拥有最终 worker selection 或 replica target 的 controller,除非明确划分 authority。
怎样评估 Dynamo 而不是评估底层 Engine
Baselines
至少分开:
1. single engine / one replica
2. multiple engines + round-robin
3. multiple engines + load-aware routing
4. Dynamo KV-aware routing
5. Dynamo disaggregated P/D
6. P/D + KVBM / external KV tiers
7. P/D + Planner autoscaling
每增加一层,说明哪项指标变化。否则无法知道收益来自 engine kernel、cache routing、P/D 还是更多 GPU。
固定 Model 与 Backend
同一模型 revision、量化、TP/PP/EP、engine version 和 sampling 参数。Dynamo 官方发布中的大倍数常来自特定硬件、DeepSeek-R1 workload、context/generation 配置与完全不同的 P/D parallelism;这些结果不能代表任意部署。
Workload
保留真实:
- 到达时间与 burst;
- ISL/OSL joint distribution;
- session/prefix overlap;
- tenant/model/LoRA;
- reasoning、tool use 和 multimodal;
- cancel/timeout。
随机生成互不相同 prompt 会让 KV-aware routing 看不到价值;所有请求共用同一 prompt 又会夸大它。
Metrics
TTFT / ITL / E2E P50-P99
SLO attainment and per-GPU goodput
prefill recomputed tokens
KV overlap predicted vs actual
P/D queue and transfer visible latency
HBM/host/storage KV utilization
router decision latency
scale decision → ready capacity time
request retry/migration/failure rate
GPU-hours and cost per qualified request
一条可落地的上线顺序
1. 先稳定 Backend
在固定副本上验证模型正确性、TP/PP、吞吐和 sampling。不要把 engine 本身的问题带进分布式 control plane。
2. 加 Frontend 与 Load-aware Routing
验证 streaming、cancel、retry、worker discovery 和 graceful drain。先不依赖 KV events,建立可用基线。
3. 打开 KV-aware Routing
检查 block hash、事件完整性、预测/实际 hit、热点和 index 恢复。故意断开 event path,验证能安全降级。
4. 评估 P/D
用 DistServe 一类 goodput 方法选择初始 P/D ratio,测 NIXL path、KV layout 和生命周期。比较 tuned aggregated baseline。
5. 再加入 KVBM/External Tier
以净节省 prefill 时间衡量,不只看 hit rate。隔离 model/adapter/tenant namespace。
6. 最后启用 Planner 自动应用
先让 Planner shadow mode 输出建议,与人工 capacity model 对比;误差可控后再自动 scale-up,最后谨慎开放 scale-down。所有变更都要有上下限和回滚。
这种顺序让每层的正确性和收益可归因。一次性部署完整 stack,遇到 TTFT 波动时很难知道是 Router、KV、transfer、Planner 还是 engine。
小结
NVIDIA Dynamo 解决的是“怎样把多套高性能推理引擎变成一个持续运行的分布式系统”。Request path 通过 Frontend、Router 与 P/D workers 处理低延迟执行;control path 由 metrics、performance model、Planner 和 Operator 调整资源;state path 用 worker discovery、KV events、prefix index、KVBM 与 NIXL 支撑复用和故障恢复。
KV-aware routing 的核心不是最长前缀优先,而是比较复用省下的 prefill 与目标 worker 的排队/active decode;KVBM 的核心不是无限扩容 HBM,而是让 block 在多层 memory 中以明确 owner 和状态 offload/onboard;Planner 的核心不是追求满卡,而是根据 ISL/OSL、P queue、D KV 和 TTFT/ITL 分别调整两个池。
Dynamo 不替代 vLLM、SGLang 或 TensorRT-LLM,也不让它们的功能差异消失。它提供跨 backend 的协作框架,具体组合仍要经过兼容性和故障验证。与 llm-d 等平台比较时,也应关注 authority、生态和运维边界,而不是只比较组件名称。
真正成熟的部署不是“把所有特性打开”,而是让三个路径各自可观测、可降级:Router 丢失 KV state 时退回负载路由,Planner 故障时保持稳定容量,external cache 失败时允许重算,P/D transfer 不可用时有明确失败或 aggregated fallback。这样,分布式优化才不会把一次可恢复的 cache miss 放大成整个服务中断。
参考资料
觉得有帮助?
分享给同样关注系统性能的人。