Skip to content
团子云技术 Lite 1.048596
Go back

llm-d KV Cache 感知与流量编排技术报告

团团虾声明:本文由 codex 基于 llm-d 代码仓库深度探索并撰写。


llm-d KV Cache 感知与流量编排技术报告

0. 阅读版本与说明

本文基于本地 llm-d 仓库 main 分支阅读整理。

从当前文档形态看,llm-d 仍处在积极开发和相对早期的阶段:架构文档、proposal、well-lit path、guide 和监控配置同时存在,部分能力还带有 experimental、working branch 或实现约束说明。本文因此不把当前代码视为长期稳定接口来解读,重点放在调度思想和系统分工上:EPP 如何把 prompt prefix、KV cache index、endpoint 负载、P/D role、latency prediction 和 offloading tier 合并成请求级路由决策。

1. 摘要

llm-d 的 KV cache 能力分成两层:

llm-d 不接管模型计算,也不直接替代 vLLM 的 cache 管理。它的关键价值是把单机 KV cache 变成集群级可见的调度信号:某个请求的 prompt prefix 已经在哪些 pod 上命中、命中多少、命中在哪个存储层、当前 pod 是否拥塞、继续粘到该 pod 是否会伤害 SLO。EPP 再把这些信号和队列长度、running requests、KV cache utilization、token load、latency prediction、P/D role 等信息合并,做请求级流量编排。

2. 架构位置

llm-d 的请求路径可以简化为:

graph LR
    Client["Client / OpenAI API"] --> Gateway["Gateway / Proxy"]
    Gateway --> EPP["llm-d Router / EPP"]
    EPP --> Gateway
    Gateway --> MS["Model Server Pod<br/>vLLM / SGLang"]

    Kube["Kubernetes API<br/>InferencePool / Pods"] --> EPP
    Metrics["Prometheus Metrics<br/>queue / running / KV usage"] --> EPP
    KVEvents["KVEvents<br/>BlockStored / BlockRemoved / AllBlocksCleared"] --> EPP
    Tokenizer["Tokenizer<br/>UDS sidecar or local"] --> EPP

各组件分工:

组件职责和 KV cache 的关系
Model server执行 prefill/decode,管理本机 KV cache产生 KV block,执行 prefix reuse,暴露 cache metrics,发出 KVEvents
InferencePool定义一组可服务同一模型的 pods给 EPP 提供候选 endpoint 集合
Gateway / Proxy接入请求,执行 L7 转发调用 EPP 获取目标 pod,不理解 KV 细节
EPP请求解析、准入、排队、调度维护 cache index,执行 prefix scorer,结合负载和 SLO 选 endpoint
KV-Cache IndexerEPP 内部的数据层组件维护 block key -> pods/tier 的近实时索引
Offloading connector把 KV 扩展到 CPU、SSD、共享存储扩大有效 cache 容量,提供 tier-aware reuse 的基础

3. KV cache 状态从哪里来

3.1 Prometheus metrics

model server 暴露指标,EPP 通过 metrics 获取每个 endpoint 的运行状态。和 KV cache 直接相关的核心指标包括:

这些指标适合回答”这个 pod 还有多少 KV 空间""继续打过去是否可能造成碎片或淘汰”。它们不能精确回答”某个 prompt prefix 的 block 是否在这个 pod 上”。

3.2 近似 prefix index

轻量部署中,EPP 使用 approx-prefix-cache-producerprefix-cache-scorer 感知 prefix locality。

流程:

  1. EPP 解析请求中的 prompt。
  2. 由于 EPP 默认没有 tokenizer,按字符到 token 的比例估算 token block。
  3. producer 对 prompt prefix 做 rolling hash,生成 block hash chain。
  4. EPP 在本地 LRU index 中查询这些 hash 最近被路由到哪些 pods。
  5. scorer 按命中比例给候选 pod 打分。
  6. EPP 做出路由决定后,假设该 pod 将拥有这些 prefix block,并更新本地 index。

这个模式部署成本低,不需要 model server 发 KVEvents,也不依赖 ZMQ。但它是基于调度历史的推断,pod 内部真正淘汰了哪些 block,EPP 并不知道。

3.3 精确 KVEvents

生产级精确模式中,model server 在 KV cache 变化时发布 KVEvents,EPP 内部的 KV-Cache Indexer 订阅事件并维护全局索引。

核心事件:

事件含义EPP 处理
BlockStored某个 block 已写入某个 pod 的某个 cache tier把 block key 加入 block key -> pod/tier 映射
BlockRemoved某个 block 被淘汰或从某个 tier 移除从索引里删除对应 pod/tier 记录
AllBlocksCleared某个 pod 清空了全部 cache删除该 pod 在索引中的所有记录

事件传输有两种模式:

精确模式的索引来源是 model server 的真实 cache 事件,比近似模式更能处理 eviction、offload、多模态、LoRA 和复杂 cache 策略。

3.4 Tokenizer / Data Producer

精确匹配需要 token ID,而不是字符片段。EPP 的 tokenizer data producer 在调度前完成这些工作:

生产部署推荐 tokenizer 作为 UDS sidecar。这样可以避免 EPP 进程内反复加载 tokenizer,也减少网络开销。

4. llm-d 感知哪些 KV cache 维度

4.1 endpoint 维度

EPP 首先要知道”有哪些候选 endpoint”。这些信息来自 InferencePool 和 Kubernetes pod watch:

4.2 容量与压力维度

这些信号用于判断”这个 pod 还能不能接请求”:

调度上通常会把低 KV utilization、短队列、少 running requests 的 pod 排在前面,降低过载、碎片和 cache eviction 风险。

4.3 prefix locality 维度

这是 llm-d KV 感知的核心。EPP 关心的是:当前请求的 prompt prefix 在哪个 pod 上可复用,能复用多长。

精确模式中,scorer 会把请求 token 切成 block key chain,并查索引:

Prompt blocks: B0 B1 B2 B3 B4

Pod A:         B0 B1 B2 B3 --  => hit length = 4
Pod B:         B0 B1 -- -- --  => hit length = 2
Pod C:         -- -- B2 B3 B4  => hit length = 0

KV block 有因果依赖。即使 Pod C 有 B2/B3/B4,没有 B0/B1 也不能复用这段 suffix,所以 longest consecutive prefix 才是有效命中长度。

4.4 tier 维度

KV block 可能位于不同层级:

EPP 的精确 scorer 可以按 tier 加权。示例权重是 GPU 1.0、CPU 0.8。同一个 block 如果存在多个 tier,取最高权重。这样调度不会只看”有没有”,还会看”在哪里”。

4.5 cache 身份维度

同一段文本不一定对应同一份可复用 KV。索引需要把以下身份折入 block key:

这些维度避免错误复用。比如同一段文字配不同图片,或者同一 token 序列挂不同 LoRA adapter,KV 语义都不同。

4.6 时间与一致性维度

KVEvents 到达 EPP 有延迟。EPP 刚把请求路由到某个 pod 时,新的 BlockStored 事件可能还没发出来。为避免连续同 prefix 请求被打散,scorer 支持 speculative indexing:

这解决了路由决策和事件传播之间的短暂空窗。

5. 流量编排流程

5.1 聚合 serving 模式

聚合模式中,每个 pod 既能 prefill 也能 decode。EPP 只需要选一个目标 endpoint。

sequenceDiagram
    participant Client
    participant Proxy
    participant EPP
    participant Index as KV Index
    participant MS as Model Server

    Client->>Proxy: OpenAI-compatible request
    Proxy->>EPP: ext-proc request metadata/body
    EPP->>EPP: parse request and run data producers
    EPP->>Index: lookup prefix block hits
    Index-->>EPP: hit length and tier per candidate pod
    EPP->>EPP: filter, score, pick endpoint
    EPP-->>Proxy: selected pod IP/port
    Proxy->>MS: forward original request
    MS-->>Proxy: streaming response
    MS-->>Index: KVEvents for stored/removed blocks

调度步骤:

  1. Parser 把请求转换成内部 InferenceRequest
  2. Data Producer 做 tokenization、prefix lookup、latency prediction 等预处理。
  3. Flow control 判断是否排队、放行或拒绝。
  4. Scheduler 从 InferencePool 拿候选 endpoints。
  5. Filters 去掉不满足条件的 endpoints,例如 role 不匹配、SLO headroom 不足。
  6. Scorers 分别计算 prefix hit、KV utilization、queue depth、running requests、token load、latency headroom 等分数。
  7. Weighted scoring 合并分数。
  8. Picker 选择目标 endpoint,例如 max-score 或 weighted-random。
  9. Proxy 把原请求转发给目标 pod。

5.2 Prefix-aware routing 的权衡

单纯追求 prefix hit 可能导致热点 pod 过载。llm-d 的编排方式是把 cache 命中和负载一起算:

因此,KV cache locality 是重要信号,但不是唯一信号。EPP 最终目标是吞吐、TTFT、TPOT 和 SLO 的整体平衡。

5.3 P/D disaggregation 模式

P/D disagg 把 prefill 和 decode 分到不同角色的 pods。EPP 的 disagg-profile-handler 会运行两个 profile:

流程:

sequenceDiagram
    participant Client
    participant Proxy
    participant EPP
    participant DSidecar as Decode Sidecar
    participant D as Decode Worker
    participant P as Prefill Worker

    Client->>Proxy: Request
    Proxy->>EPP: ext-proc request
    EPP->>EPP: select decode endpoint
    EPP->>EPP: estimate uncached suffix on decode endpoint
    alt enough cache on decode
        EPP-->>Proxy: decode endpoint only
        Proxy->>DSidecar: request
        DSidecar->>D: decoder-only request
    else large uncached suffix
        EPP->>EPP: select prefill endpoint
        EPP-->>Proxy: decode endpoint + prefill header
        Proxy->>DSidecar: request with x-prefiller-host-port
        DSidecar->>P: prefill request
        P-->>DSidecar: KV transfer params
        DSidecar->>D: decode request with KV transfer params
        D->>P: pull KV cache by NIXL/RDMA
    end
    D-->>DSidecar: response
    DSidecar-->>Proxy: response

关键点:

6. KV offload 与调度的组合

offload 解决的是”cache 放得下多久、能不能跨节点复用”的问题;EPP 解决的是”知道 cache 在哪里后,请求该打到哪里”的问题。

6.1 CPU offload

vLLM OffloadingConnector 可以把被挤出 HBM 的 KV block 放到 CPU DRAM。EPP 通过 tier-aware index 知道某些 block 虽然不在 GPU,但仍在该 pod 的 CPU tier。调度上,这类命中通常弱于 GPU 命中,但强于完全 miss。

适用场景:

6.2 Storage offload

llm-d FS backend 把 KV block 存到共享文件系统。多个 vLLM 实例可以访问同一路径,从而实现跨 pod、跨节点复用。

适用场景:

风险点:

6.3 外部 KV cache engine

LMCache、Mooncake、KVBM 等外部引擎通常通过 model server 的 KV connector API 接入。llm-d 侧的共同契约是 KVEvents:不管底层 cache engine 如何管理内存、磁盘或远端存储,只要能把 cache mutation 暴露给 EPP,EPP 就能把它纳入 prefix-aware routing。

7. 典型调度策略

7.1 长 prompt 多轮会话

目标:复用上一轮 prompt prefix,降低 TTFT。

策略:

7.2 高并发冷请求

目标:避免大量冷 prefill 打爆少数 pods。

策略:

7.3 长上下文 P/D disagg

目标:隔离长 prefill 对 decode 的干扰。

策略:

7.4 多 LoRA 或多模态请求

目标:避免错误命中,尽量复用同 adapter/同多模态输入的 KV。

策略:

8. 可观测性

EPP 和 model server 提供的关键观测项:

指标用途
inference_pool_average_kv_cache_utilization观察 pool 级 KV 压力
inference_pool_per_pod_queue_size观察单 pod 排队
inference_pool_average_running_requests观察 pool 运行中请求数
inference_extension_scheduler_attempts_total观察调度成功、失败和目标分布
inference_extension_scheduler_e2e_duration_seconds观察 EPP 调度耗时
inference_extension_plugin_duration_seconds观察具体 filter/scorer/picker 开销
inference_extension_prefix_indexer_size观察 prefix index 规模
inference_extension_prefix_indexer_hit_ratio观察 prefix lookup 命中率
inference_extension_prefix_indexer_hit_bytes观察命中的 prefix 字节量
vllm:kv_cache_usage_perc观察 vLLM KV cache 使用率
vllm:kv_offload_*观察 offload 传输、大小和耗时

排查时建议按三层看:

  1. model server 层:KV utilization、queue、running requests、offload latency。
  2. EPP 数据层:KVEvents 是否正常、index size 是否增长、hit ratio 是否符合预期。
  3. EPP 调度层:scheduler attempts、plugin duration、目标 pod 分布是否倾斜。

9. 工程边界与风险

9.1 近似模式会漂移

approx index 基于”请求曾经被发到哪个 pod”的历史推断。pod 内 cache 被淘汰、进程重启、负载挤压后,EPP 可能仍认为该 pod 有对应 prefix。高价值生产场景应优先评估 precise 模式。

9.2 精确模式依赖事件链路

precise 模式需要 tokenizer、KVEvents、ZMQ 订阅、index backend 都正常。事件丢失、延迟过高或 EPP 多副本订阅不完整,会直接影响调度准确性。

9.3 cache 命中不等于一定更快

GPU 命中通常收益最高;CPU 或 storage tier 命中要看传输延迟。共享存储吞吐不足时,从远端拉 KV 可能比 recompute 更慢。实际策略要结合 TTFT、TPOT 和 storage metrics 调权。

9.4 P/D disagg 强依赖网络

P/D disagg 的收益建立在高性能 KV transfer 上。没有 RDMA 时,NIXL 可能退到 TCP,只适合测试或开发。生产环境需要确认 IB/RoCE/EFA、GPUDirect RDMA、NIXL backend 和 pod 网络拓扑。

9.5 身份隔离必须严格

KV block key 必须包含模型、LoRA、多模态 extra keys、prefix chain 等身份信息。错误复用 KV 属于正确性问题,不是单纯性能问题。

10. 结论

llm-d 感知 KV cache 的核心链路是:model server 暴露 metrics 和 KVEvents,EPP data producer 把请求转成可匹配的 token/block 表示,KV-Cache Indexer 维护集群级 cache 位置索引,scheduler 用 prefix scorer、KV utilization scorer、queue/load scorer、latency scorer 等插件合并决策。

流量编排的本质是把”请求内容”和”集群实时状态”放在同一个调度循环里。聚合 serving 里,EPP 选择一个最合适的 model server pod;P/D disagg 里,EPP 同时选择 decode 和必要的 prefill endpoint,并由 sidecar 和 NIXL/RDMA 完成 KV 传递。KV offload 则把 cache 保留时间和共享范围扩大,让 EPP 的 cache-aware routing 有更大的命中空间。

在长上下文、多轮 agent、多租户、高 QPS、MoE 和 P/D disagg 场景下,这套机制能减少重复 prefill、降低 TTFT、平衡 decode 压力,并减少热点 pod 引发的尾延迟。

11. 参考


Share this post on:

Previous Post
Modular 这家公司到底在做什么:一个 AI 推理基础设施的调研
Next Post
【转载】KV Cache 的五个时代