/ AI Infra
[Infra-16] vLLM KV Connector:接口、生命周期与 LMCache / Mooncake 的边界
从 Scheduler 与 Worker 的协作出发,拆解 vLLM V1 KV Connector 的 lookup、block allocation、load/save 与异步生命周期,并厘清 LMCache、NIXL、Mooncake Transfer Engine 和 Distributed Store 的系统边界。
第一次接触 vLLM 的 KV Connector,很容易把它理解成三个函数:
lookup -> load -> save这个理解适合入门,却不足以解释真实系统。它没有回答:谁决定一段 KV 可以复用?远端命中以后,本地 GPU 的 destination block 由谁分配?Scheduler 不持有 KV tensor,又怎样让 Worker 知道数据应该搬到哪里?异步传输尚未结束时,request 和 block 能不能释放?
真正值得建立的心智模型是:
vLLM V1 的 KV Connector 不是一个 KV 存储产品,而是一套连接 Scheduler 决策与 Worker 数据搬运的协议。
Scheduler 负责判断“哪些 token 的 KV 不必重算”,为它们保留本地 paged-KV blocks,并生成本轮执行所需的 metadata;Worker / ModelRunner 根据这些 metadata 执行真实的 load 与 save,等待异步传输,并把完成状态反馈给 Scheduler。
Scheduler control plane
lookup external KV
allocate local blocks
build transfer metadata
|
v
Worker data plane
resolve KV tensor addresses
load into local blocks
run model
save newly computed KV
report completion沿着这条边界看,LMCache、NIXL 和 Mooncake 的关系也会清楚很多:它们不是三种同级的“KV Connector API”。LMCache 是 KV cache 管理系统,NIXL 与 Mooncake Transfer Engine 偏向数据传输,Mooncake Distributed Store 则是建立在传输能力之上的分布式对象存储。vLLM 中各个 concrete connector,才是把这些系统适配到统一 contract 的那一层。
本文基于截至 2026 年 9 月 7 日的 vLLM V1 接口与相关项目文档。vLLM 的 connector 仍在快速演进,具体类名、配置项和源码路径应以实际部署的 tag 或 commit 为准。
1. KV Connector 到底解决什么问题#
vLLM 本身已经有 Automatic Prefix Caching。假设两个请求拥有相同前缀,第二个请求可以直接复用当前实例 GPU 中已有的 KV blocks,从而跳过共享前缀的 prefill。
Request A: [shared prefix][question A]
Request B: [shared prefix][question B]
^^^^^^^^^^^^^
reuse local KV这不需要 LMCache,也不需要 Mooncake。它是 vLLM 本地 BlockPool 上的 prefix reuse。
问题是,KV 不一定在当前实例的 GPU 上。它可能位于:
- 另一台 Prefill worker 的 GPU;
- 本机 CPU RAM 或 NVMe;
- 一个 LMCache server;
- Mooncake Distributed Store;
- 其他拥有独立索引、传输和淘汰策略的系统。
如果让 Scheduler 直接理解每一种 backend 的 RDMA API、对象 key、内存注册方式和 eviction policy,它很快就会与具体基础设施耦合。KV Connector 的作用,就是在二者之间建立稳定边界:
Reusable KV
|
+----------+----------+
| |
Local GPU APC External / Remote KV
| |
vLLM BlockPool KV Connector
|
+--------------+--------------+
| | |
LMCache Mooncake Store P/D peer因此,KV Connector 同时承担两类职责:
- Storage adapter:把“查询、加载、保存 KV”的抽象映射到具体 backend。
- Scheduler-Worker protocol:协调 external hit、local block allocation、step metadata、异步完成与 request 生命周期。
只看到第一点,就会误以为 connector 是一个薄薄的 get/put wrapper;第二点才是理解 vLLM V1 设计的关键。
2. lookup / load / save 只是语义,不是实际 API#
在 KVConnectorBase_V1 中,并不存在一组恰好叫 lookup()、load()、save() 的核心接口。更准确的映射是:
lookup ~= get_num_new_matched_tokens()
load ~= start_load_kv()
+ wait_for_layer_load()
save ~= save_kv_layer()
+ wait_for_save()完整生命周期还包括 allocation、metadata 和 completion:
| 阶段 | 关键方法 | 执行侧 | 作用 |
|---|---|---|---|
| 请求初始化 | on_new_request() | Scheduler | 建立 connector 的 request-scoped 状态 |
| External lookup | get_num_new_matched_tokens() | Scheduler | 查询本地已命中部分之后,外部还能提供多少连续 prefix KV |
| 分配绑定 | update_state_after_alloc() | Scheduler | 将 external tokens 对应的本地 destination blocks 告诉 connector |
| Step metadata | build_connector_meta() | Scheduler | 生成 Worker 在本 step 消费的 load/save/transfer plan |
| Metadata 绑定 | bind_connector_metadata() | Worker | 在 model execution 前绑定本轮计划 |
| Load 启动 | start_load_kv() | Worker | 开始把外部 KV 写入本地 paged-KV buffer |
| Layer barrier | wait_for_layer_load() | Worker | attention layer 使用 KV 前等待该层 ready |
| Save | save_kv_layer() | Worker | 将新计算的一层 KV 发布到 backend |
| Save barrier | wait_for_save() | Worker | 确保源 block 在异步保存完成前不会被覆盖 |
| 完成反馈 | get_finished() | Worker | 汇报异步 send / recv 已完成的 request |
| 跨侧反馈 | build_connector_worker_meta() / update_connector_output() | Worker / Scheduler | 将 connector-specific 状态带回 Scheduler |
| 请求结束 | request_finished() | Scheduler | 释放 block 前完成 request 级收尾,必要时延迟释放 |
这组接口表达了一个很重要的事实:
“外部系统说它有这段 KV”和“这段 KV 已经安全地出现在当前 GPU 的指定 blocks 中”不是同一个事件。
中间还隔着本地 block 分配、metadata 下发、数据传输与完成确认。
3. 一条请求如何穿过 Connector#
把一条 request 从 Scheduler 跟到 ModelRunner,是理解 connector 最有效的方法。
3.1 先合并本地命中,再问 external cache#
假设 prompt 有 8192 个 token:
prompt:
[0 ........................................................ 8191]
local GPU APC:
[0 ............... 2047]
external store:
[0 ........................................... 6143]
must compute:
[6144 ... 8191]Scheduler 首先知道本地已有 2048 个 computed tokens。随后 connector 的 get_num_new_matched_tokens(request, num_computed_tokens) 查询的不是“远端一共有多少 token”,而是:
从当前 num_computed_tokens 之后开始,
远端还能连续、可靠地提供多少 prefix KV?如果 external store 能提供到 token 6143,那么 connector 返回的是在本地命中基础上新增的 span。Scheduler 最终只计算 6144 到 8191。
这个 contract 有两项严格要求:
- 返回值必须是可加载的最长连续 prefix,不能把中间有洞的数据算作命中;
- 已被 eviction 或当前不可达的数据不能因为索引里“曾经存在”就算作命中。
如果 connector 还不能立即确定结果,可以返回 None,让 Scheduler 稍后重试;如果数据将在 scheduler steps 之间异步加载,还需要通过 load_async 表达这一状态。
3.2 命中以后,先分配 destination blocks#
External hit 不是远端 block ID 的直接复用。Decode worker 或 cache consumer 必须先在自己的 paged KV cache 中获得 destination blocks:
external KV object
|
| materialize
v
consumer local block IDs: [42, 81, 103, ...]Scheduler 的 CacheManager / BlockPool 完成分配后,会调用:
update_state_after_alloc(
request,
blocks,
num_external_tokens,
)这一步把两个此前分离的信息绑在一起:
what to load = external token span / content identity
where to load = current worker's local blocks异步 load 下,这个方法可能对同一个 request 调用不止一次:先为 remote KV 分配 destination blocks,传输完成后再为需要实际计算的 tail 分配 blocks。实现 connector 时,应依据 num_external_tokens 判断自己是否负责 load,而不是看到非空 blocks 就启动传输。对 MultiConnector 来说尤其如此,因为未被选中的 child 也可能看到真实 allocation。
3.3 Scheduler 生成计划,Worker 执行搬运#
Scheduler 通常不是持有 GPU KV tensor 的进程。它不应该亲自执行 RDMA read、GPU memcpy、LMCache load 或 Mooncake Store get。
它能生成的是一个传输计划:
request_id
external token count
local destination blocks
load / save decision
transfer ID or external keys
connector-specific flagsbuild_connector_meta() 把这个计划装入 SchedulerOutput。Worker / ModelRunner 在每次执行前调用 bind_connector_metadata(),再由 concrete connector 把 block IDs 解析成真实 tensor 地址并发起操作。
Scheduler connector state
|
v
build_connector_meta()
|
v
SchedulerOutput
|
v
bind_connector_metadata()
|
v
Worker connector state
|
v
DMA / RDMA / backend get这也是 register_kv_caches() 存在的原因:某些传输层需要提前注册 KV cache tensor 对应的 memory region,真正的数据面只能在 Worker 侧建立。
3.4 Load、forward 与 save 可以按层重叠#
start_load_kv() 可以在 forward 之前发起异步 load;wait_for_layer_load(layer_name) 则允许把 barrier 推迟到某个 attention layer 真正使用 KV 的时刻。保存端也可以随着 forward 逐层调用 save_kv_layer()。
理想的 layerwise pipeline 类似:
Load L0 ----> Attention L0 ----> Save L0
Load L1 ----> Attention L1 ----> Save L1
Load L2 ----> Attention L2 ----> Save L2相比之下,all-at-once 模式是:
Load all layers -> Forward all layers -> Save all layersBase class 提供 layerwise hooks,并不代表每个 connector 都真正实现了 pipeline。有的实现会在 start_load_kv() 中一次搬完全部 layer,此时 wait_for_layer_load() 基本是 no-op。判断一个 connector 是否真的 overlap,应该看时间线和同步点,而不是只看方法名字。
如果 connector 在 Python 侧参与逐层同步或拷贝,还要检查它对 CUDA graph mode 的要求;组合到 MultiConnector 后,只要一个 child 要求 PIECEWISE CUDA graph,整体执行模式就会受到约束。
3.5 传输完成不等于请求完成#
异步系统里至少存在五个不同事件:
1. transfer submitted
2. network transfer completed
3. destination GPU block is safe to read
4. source block is safe to free or reuse
5. request is safe to resume or finish它们不一定发生在同一时刻。get_finished()、wait_for_save() 与 request_finished() 分别覆盖不同的生命周期边界。
尤其要注意:本 step 的 connector metadata 可能在 execution 后被清理,但传输仍在后台继续。因此,需要跨 step 存活的 request / transfer 状态,必须进入 connector 自己管理的长期状态,不能继续依赖临时 metadata。
4. Block ID 与 Block Hash:地址和身份不是一回事#
分布式 KV cache 中最容易混淆的两个概念是 local block ID 与 external key。
block hash / external key:
"这段 KV 的内容是谁?"
local block ID:
"这段 KV 在当前 engine 上放在哪里?"Local block ID 是当前 vLLM worker 的 paged-KV slot。它决定本地 tensor 中的源地址或目标地址,但通常没有跨实例意义。同一个 prefix 在两台 worker 上完全可以落在不同 ID:
same prefix hash = H(prompt[0:1024])
worker A local block = 17
worker B local block = 203Block hash、external key 或 transfer ID 则负责跨实例识别内容。以 Mooncake Store 为例,Scheduler 侧可以用 block hash 查询共享对象;Worker 侧再用本地 block ID 计算实际内存地址。为了避免不同 shard 被错误复用,external key 还需要纳入模型与并行拓扑身份,例如 TP、PP、DCP 或其他 rank 信息。
一旦把“内容身份”和“本地地址”分开,distributed prefix cache 的数据模型就清楚了:
content-addressable key
|
| lookup in shared system
v
external KV object
|
| connector maps object to allocation
v
consumer-local paged KV blocks5. KV Connector、LMCache、NIXL 与 Mooncake 分别在哪一层#
这些名字经常一起出现在 P/D 分离文档里,但它们不属于同一个抽象层。
5.1 KV Connector:vLLM 的接入 contract#
KV Connector 定义 lookup、allocation coordination、metadata、load/save hook 和 async completion,但它本身不提供存储容量,也不定义 replication、eviction 或网络协议。
换言之,它回答的是:
一个外部 KV 系统怎样接入 vLLM?而不是:
KV 最终存在哪里、怎样跨网络移动?5.2 LMCache:KV cache management layer#
LMCache 是独立于 inference engine 的 KV cache 管理层。它关注的是跨请求 reuse、CPU / SSD / remote tier、offload、存储插件以及更长的 KV 生命周期。
vLLM
|
LMCache connector
|
LMCache cache management
|
+-- CPU RAM
+-- SSD
+-- remote cache / object storage
+-- NIXL
+-- Mooncake
`-- other plugins因此,LMCache 不只是一个 memcpy library。尤其在 MP 架构中,LMCache server 可以作为独立 daemon 存活,让 cache lifecycle 与 vLLM engine process 解耦。
5.3 NIXL:传输抽象,不是 LMCache 本身#
NIXL 为异构内存和网络提供传输抽象,具体路径可由 UCX、libfabric 等 plugin 承担,并在合适环境中落到 RDMA、共享内存等 transport。
vLLM NixlConnector
|
v
NIXL
|
transport plugin
|
UCX / libfabric / ...
|
RDMA / shared memory / ...所以 LMCache = NIXL = RDMA 并不成立。LMCache 可以使用 NIXL;NIXL 可以通过特定 plugin 使用 RDMA;但三者分别处在 cache management、transfer abstraction 和底层网络能力三个层次。
5.4 Mooncake Transfer Engine:高性能数据搬运#
Mooncake Transfer Engine 是统一的数据传输框架,关注 RDMA、TCP、EFA、NVMe-oF、NVLink、多 NIC 聚合与 topology-aware routing。
vLLM MooncakeConnector
|
v
Mooncake Transfer Engine
|
RDMA / TCP / EFA / NVLink / ...Transfer Engine 的核心是搬运 bytes。它本身不是一个 prefix index,也不负责 reusable KV 的 eviction-managed 生命周期。
5.5 Mooncake Distributed Store:建立在 TE 上的共享存储#
Mooncake Distributed Store 位于更高一层,负责对象 placement、replication、eviction、DRAM / SSD 多层存储和生命周期管理。vLLM 的 MooncakeStoreConnector 在它之上建立基于 hash 的 prefix sharing。
MooncakeStoreConnector
|
v
Mooncake Distributed Store
index / placement / lifecycle
|
v
Mooncake Transfer Engine
|
v
network and storage transports5.6 放在一张表里比较#
| 维度 | vLLM KV Connector | LMCache | Mooncake TE | Mooncake Distributed Store |
|---|---|---|---|---|
| 定位 | vLLM 接入接口与调度协议 | KV cache 管理系统 | 高性能数据传输引擎 | 分布式 KV / object cache store |
| 自己是否持有 KV | 不规定 | 是 | 不以持久 cache store 身份持有 | 是 |
| Prefix lookup | 定义查询 contract,不定义索引 | 提供 reuse / indexing | 不负责 | 通过 store key / hash 查询 |
| 数据搬运 | 由实现决定 | 可使用不同传输 backend | 核心职责 | 基于 TE 搬运 |
| Tiered offload | 不定义策略 | 核心能力 | 只可充当数据面 | 支持 DRAM / SSD 等层级 |
| Eviction / replication | 不定义 | 由 cache/backend 管理 | 不负责 cache 语义 | 明确管理 |
| vLLM 适配器 | 所有 concrete connectors 的基类 | LMCacheConnectorV1 / LMCacheMPConnector | MooncakeConnector | MooncakeStoreConnector |
最容易忽略的一点是:LMCache 与 Mooncake 并不一定是互斥竞品。LMCache 可以把 Mooncake 作为下层 backend:
vLLM
|
LMCacheConnector / LMCacheMPConnector
|
LMCache
|
Mooncake backend这和 vLLM 直接通过 MooncakeStoreConnector 对接 Mooncake Store 是两种不同的 integration boundary。区别不只在数据路径,还在于由谁拥有 cache index、policy 和 control plane。
6. 两种核心数据流:P2P Handoff 与 Shared Cache#
从使用目标看,connector 的数据流主要可以分为两类。
6.1 P/D P2P:交接当前请求的 KV#
在 Prefill-Decode 分离中,Prefill worker 刚刚计算出的 KV 必须交给选定的 Decode worker。MooncakeConnector 与 NixlConnector 都可以承担这种 current-request fast path。
Client / Router
|
v
Prefill Scheduler
|
v
Prefill Worker ---- compute prompt ----> source GPU KV blocks
| |
| transfer metadata | P2P transfer
v v
Decode Scheduler ----------------------> Decode Worker
|
destination GPU blocks
|
transfer completion
|
v
Decode这里的目标不是查询“过去有没有相同 prefix”,而是把当前 request 的新鲜状态低延迟地交给 downstream worker。
P2P fast path 少了一层长期 store 的 lookup、placement 和持久化,适合直接 handoff;但它不会自动形成一个可供未来任意请求复用的 cluster-wide prefix pool。
6.2 Shared Store:复用过去请求的 KV#
MooncakeStoreConnector 与 LMCache connector 更典型的数据流是 external lookup-load-save:
New request
|
v
Local APC lookup
|
v
External prefix lookup
|
+-- miss --------------------> compute normally
|
`-- hit
|
v
allocate local blocks
|
v
load KV from cache/store
|
v
compute uncached tail
|
v
save newly computed KVShared cache 把 KV 生命周期从某张 GPU 和某个 engine process 中解耦,适合长前缀、高重复率、多实例调度以及 HBM 容量受限的 workload。
但命中并不天然代表划算。收益的基本条件仍然是:
external lookup latency + transfer latency
<
skipped prefill computation latency长 prefix、高命中率与高速网络更容易满足这个条件;短 prefix、低复用率或拥塞链路则可能让外部 cache 的控制面和数据面开销无法摊销。
7. 常见部署组合应该怎样选#
7.1 只用 MooncakeConnector#
Prefill vLLM
|
MooncakeConnector
|
Mooncake TE P2P
|
MooncakeConnector
|
Decode vLLM适合目标单一的 P/D handoff:当前请求在 P 节点完成 prefill,立即将 KV 交给 D 节点。优势是 fast path 清晰,可利用 GPUDirect RDMA、多 NIC 与 topology-aware transport;不足是没有长期 shared-prefix store。
7.2 只用 MooncakeStoreConnector#
vLLM A --+
vLLM B --+--> MooncakeStoreConnector --> Distributed Store
vLLM C --+适合跨请求、跨实例复用,以及使用 DRAM / SSD 扩展 KV 容量。代价是每次 hit 都要付出 external lookup 和 materialization 成本,还要运营 store metadata、容量、replication 与 eviction。
7.3 MooncakeConnector + MooncakeStoreConnector#
通过 MultiConnector 可以把短生命周期与长生命周期的数据路径拆开:
MultiConnector
/ \
/ \
MooncakeConnector MooncakeStoreConnector
| |
current-request P2P cross-request reuse
| |
Prefill -> Decode Distributed Store当前 MultiConnector 的关键语义是:load 时按配置顺序选取第一个报告可用 token 的 child,save 时则写入所有 children。
因此,顺序与 fan-out 都是实际系统行为:
- 排在前面的 child 决定优先 load path;
- 某个 child 的 false-positive hit 可能阻断后续可用 source;
- save-to-all 会增加带宽、内存占用与存储写放大;
- child 的异步状态和 CUDA graph 限制需要由组合层统一处理。
7.4 NixlConnector + LMCacheMPConnector#
LMCache 的现代 MP 部署把职责拆得更明确:
MultiConnector
/ \
/ \
NixlConnector LMCacheMPConnector
| |
current P -> D KV cross-request reuse
| |
NIXL co-located LMCache server
| |
UCX / RDMA CPU / SSD / remoteNixlConnector 负责当前 request 的 Prefill 到 Decode transfer,LMCacheMPConnector 负责向独立 LMCache server offload / load,以支持跨请求 reuse。
它的优点是 transfer fast path 与 cache management 边界清晰;代价是组件和故障域更多。具体版本还要检查 MultiConnector 真实 block allocation、failure policy 与 LMCache server 间共享能力是否满足部署需求。
7.5 LMCache 使用 Mooncake backend#
如果希望由 LMCache 统一管理 cache policy,同时复用 Mooncake 的存储与传输基础设施,也可以采用:
vLLM -> LMCache connector -> LMCache -> Mooncake backend此时 vLLM 面对的是 LMCache semantics,Mooncake 位于更低层。它与直接部署 MooncakeStoreConnector 的核心差别,是 control plane 与 cache lifecycle 的所有权。
8. 正确性难点:Lookup 是一项承诺#
External cache 最危险的问题之一,是 lookup 与 load 之间的 TOCTOU:
lookup:
"有 32 个 blocks"
time passes
scheduler allocates
backend evicts data
load:
"只剩 20 个 blocks"普通缓存的 exists() 失败通常只是一次 miss;KV Connector 中却更严重。Scheduler 已经根据 positive hit 跳过了部分 prefill,并为这些 token 分配 destination blocks。如果 backend 无法兑现,Decode 看到的上下文状态就不完整。
因此,connector 的 positive lookup 应被理解成一种可兑现承诺,而不只是 metadata 里的一个布尔值:
positive lookup
|
+-- pin / lease / reserve data until load
|
`-- or provide explicit failure and recomputation pathvLLM 的 CPU offload 相关讨论已经展示过 lookup 到 allocation 之间发生 eviction 的实际 race。这个案例不代表所有 connector 都存在相同 bug,却揭示了 contract 必须回答的共性问题:
- Hit 是否同时获取 lease 或 pin?
- Lease 由 Scheduler 侧还是 Worker 侧释放?
- Load 部分失败时能否 rollback 到 recomputation?
- Async transfer 超时后谁负责取消和回收 blocks?
- Backend crash 后 request 是 fail-fast,还是重新 prefill?
Shared prefix cache 与 current-request P/D handoff 的失败语义也不同。共享 cache 保存的是可重算 derivative state,miss 或 eviction 通常可以 fallback 到 recomputation;P/D handoff 则是当前 request 的直接数据依赖。一旦 Decode Scheduler 已把相应 token 视为 computed,传输失败就不能悄悄当作 cache miss。生产配置通常应让这类失败显式暴露,或实现完整的重算状态转换。
9. 性能分析:不要只看 RDMA 带宽#
Connector benchmark 很容易退化成一张 GB/s 表,但用户真正感受到的是端到端 TTFT、TPOT 和吞吐。至少要拆开观察:
lookup latency
allocation and scheduling delay
metadata serialization / IPC
transfer setup
actual data movement
per-layer readiness
forward overlap
completion notification
request resume delay一个 connector 可以拥有很高的峰值 RDMA 带宽,却因为 lookup 慢、transfer 粒度小或 completion polling 粗糙而无法改善 TTFT。所谓 async 也可能只是在 API 上异步:如果 forward 前存在全局 barrier,load 与模型计算并没有真正 overlap。
建议至少记录这些时间点:
t0 external lookup start
t1 lookup result committed
t2 destination blocks allocated
t3 transfer submitted
t4 each layer becomes ready
t5 model forward starts
t6 transfer fully completes
t7 request resumes / first token emitted有了这条 timeline,才能回答:
- External hit 是否真的比 recompute 更快?
- 网络传输与 forward 重叠了多少?
- Scheduler 排队是否吃掉了数据面收益?
- 慢的是 control plane、memory registration,还是 transport?
- Save fan-out 是否影响在线 Decode?
P/D 分离本身也不保证吞吐提高。它首先解决的是 Prefill 与 Decode 的资源隔离,以及 TTFT / TPOT 可以独立扩缩容的问题;KV transfer 是为这种架构新增的成本,而不是免费的优化。
10. 源码阅读路线:先控制流,后数据搬运#
研究 connector 时,不建议一上来钻进 RDMA implementation。更高效的方式是沿一条 request 分四遍阅读。
第一遍:只追 Scheduler 状态#
从 get_num_new_matched_tokens() 开始,回答:
local hit 是多少?
external hit 是多少?
最终 num_computed_tokens 是多少?
request 继续 compute,还是等待 remote KV?先分清 None、0、positive hit 与 load_async=True 的状态转换。
第二遍:追 block allocation#
围绕 update_state_after_alloc() 记录:
request_id
num_computed_tokens
num_external_tokens
local block IDs
block size
KV cache group重点确认 destination block 是 consumer-local slot,external key 与 local block ID 没有混用。
第三遍:追 metadata 边界#
完整打印一次:
Scheduler connector state
-> build_connector_meta()
-> SchedulerOutput
-> bind_connector_metadata()
-> Worker connector state分布式 connector 常见问题未必来自 transport API,而可能是 token count 正确但 block list 错误、request 与 transfer ID 串线,或不同 TP / PP rank 使用了不一致的 key namespace。
第四遍:追真实数据面#
最后再跟:
start_load_kv()
wait_for_layer_load()
save_kv_layer()
wait_for_save()
get_finished()把 submit、layer ready、forward 和 completion 时间对齐,确认 pipeline 是否真的形成。
推荐源码入口#
| 阅读顺序 | 路径 / 类 | 重点问题 |
|---|---|---|
| 1 | v1/base.py | Scheduler / Worker contract 与 async lifecycle |
| 2 | factory.py | Connector 名称、构造与插件入口 |
| 3 | scheduler.py | Local hit、external hit、allocation 与 wait state |
| 4 | gpu_model_runner.py | Metadata binding、forward context 与 KV tensor 注册 |
| 5 | multi_connector.py | First-hit load、save-to-all 与 child state 汇总 |
| 6 | nixl/connector.py | NIXL P/D transfer 与异步完成 |
| 7 | lmcache_connector.py | vLLM 与进程内 / 直接 LMCache 集成边界 |
| 8 | lmcache_mp_connector.py | 独立 LMCache server 的 MP 架构 |
| 9 | mooncake_connector.py | P/D direct transfer 与 TE integration |
| 10 | mooncake/store/connector.py | Shared-store connector 的入口 |
| 11 | mooncake/store/scheduler.py | Hash lookup 与 Scheduler-side request tracking |
| 12 | mooncake/store/worker.py | Memory registration、get/put 与 address calculation |
main 分支会持续变化。定位线上问题时,应固定到部署版本对应的 tag / commit,再检查该版本的 docs、config schema 和 connector 实现。
11. 一张图总结整个生命周期#
最终,可以把 KV Connector 压缩成三组问题:
"外部有没有?"
|
get_num_new_matched_tokens()
|
v
"放到哪里?"
|
CacheManager allocation
|
update_state_after_alloc()
|
v
"告诉 Worker 怎么搬"
|
build_connector_meta()
|
v
start_load_kv()
|
wait_for_layer_load()
|
v
model forward
|
save_kv_layer()
|
wait_for_save()
|
v
get_finished() / request_finished()第一组是可复用性判断,第二组是本地资源绑定,第三组是数据面执行与生命周期闭环。任何 connector,无论后面接的是 P2P GPU、LMCache、Mooncake Store、CPU RAM 还是 SSD,都必须在这条链上给出一致的语义。
12. 总结#
理解 vLLM KV Connector,最重要的是不要把它缩减成一个远端缓存客户端。
它真正连接的是两种世界:
Scheduler world
tokens, prefix hits, block allocation, request states
KV Connector
Worker world
tensors, addresses, memory registration, data transfer在这条边界上:
- 本地 APC 负责当前实例 GPU 上的 prefix reuse,connector 负责本地之外的 KV;
get_num_new_matched_tokens()是可兑现的 external-hit contract,不是普通exists();- Block hash 表示内容身份,local block ID 表示本机落点;
- Layerwise hook 提供 pipeline 可能性,但是否真正 overlap 取决于 concrete connector;
- LMCache 管 cache,NIXL 与 Mooncake TE 偏向搬运,Mooncake Store 管分布式对象生命周期;
- P2P handoff 与 shared-prefix cache 是两类不同数据流,生产系统经常通过
MultiConnector组合二者。
一旦沿着 lookup -> allocation -> metadata -> load/save -> completion 理清 request 生命周期,LMCacheConnector、NixlConnector、MooncakeConnector 和 MooncakeStoreConnector 就不再是几套互相割裂的黑盒,而是同一 vLLM protocol 对不同数据管理与传输语义的适配。
延伸阅读#
- vLLM
KVConnectorBase_V1API - vLLM Disaggregated Prefilling
- vLLM
MultiConnectorAPI - vLLM Mooncake Store Connector Usage Guide
- vLLM Mooncake Connector Usage Guide
- vLLM NixlConnector Usage Guide
- LMCache Documentation
- LMCache MP Disaggregated Prefill
- Mooncake GitHub Repository
- vLLM #44223: Semantic KV Cache Reuse Interface
- vLLM #38474: Mooncake Store Connector for Shared KV Cache
- vLLM #39702: CPU Offload Lookup / Eviction Race