LMCache论文解析:把KV Cache变成企业级LLM推理的存储层
本文最后更新于 2026年6月15日 下午
版本说明:本文基于 arXiv:2510.09665v2《LMCache: An Efficient KV Cache Layer for Enterprise-Scale LLM Inference》整理。论文 v1 提交于 2025-10-08,v2 修订于 2025-12-05。本文重点解析论文中的系统动机、架构设计、接口、性能优化、评测结果和生产经验,不讨论本地 PegaFlow 项目实现。
1. 先说结论
LMCache 这篇论文的核心观点很明确:
KV Cache 不应该只被看成推理引擎内部的一块 GPU 显存,而应该被提升为一个可以跨请求、跨实例、跨存储层管理的“LLM 原生数据层”。
传统推理系统里,KV cache 的生命周期基本绑定在一个请求和一个推理引擎实例里:
- prefill 阶段计算 prompt 的 K/V。
- decode 阶段复用同一个请求里已经算好的 K/V。
- 请求结束后,KV 通常被释放或者只在 GPU prefix cache 里短暂保留。
LMCache 要解决的是更大的系统问题:
- 跨请求复用:多个请求共享长前缀时,不要重复 prefill。
- 跨引擎复用:一个 vLLM/SGLang 实例算出来的 KV,另一个实例也能加载。
- 跨存储层迁移:KV 可以在 GPU、CPU、SSD、远端内存、对象存储、Redis 等后端之间移动。
- P/D 分离传输:prefill GPU 生成 KV,decode GPU 读取 KV,降低 decode 被 prefill 干扰的概率。
- 显式控制:上层 router、scheduler、运维系统可以 lookup、move、pin、clear、compress KV。
论文最值得抓住的不是“用了某个缓存技巧”,而是它把 KV cache 从推理引擎的内部副产品,抽象成了一个可寻址、可迁移、可调度、可压缩的系统资源。
从结果看,LMCache 在论文评测中给出几类关键收益:
| 场景 | 论文中的主要结论 |
|---|---|
| 单节点 CPU offloading | 相比基础 vLLM、vLLM native CPU offloading 和商业服务,TTFT 降低 1.9-8.1 倍;在相同 TTFT 下吞吐提升 2.3-14 倍 |
| 真实 trace | 在五个模型上,高 QPS 下 TTFT 至少降低 3.7-6.8 倍,ITL 降低 19%-58% |
| 远端集中式存储 | 相同 TTFT 下吞吐提升 1.3-3 倍 |
| P/D disaggregation | 相比 vLLM native PD,平均 TTFT 降低 1.53-1.84 倍,平均 ITL 降低 1.12-1.66 倍 |
| CPU 加载带宽 | LMCache 达到约 400Gbps,vLLM native CPU offloading 约 88Gbps |
但这篇论文也传递了一个很重要的限制:加载 KV 不总是比重新 prefill 更快。 当远端带宽低、上下文短、模型 prefill 本身很快时,load 可能反而更慢。因此真实系统需要做 adaptive decision:算一次和加载一次,哪个更划算。
2. KV Cache为什么会变成“存储层”问题
先从最基本的 decoder-only Transformer 推理说起。
对一个 token 生成过程来说,每一层 attention 都需要历史 token 的 Key 和 Value。假设请求已经有 个历史 token,第 个 token 的 decode 如果从头计算所有历史 K/V,会浪费大量计算。因此推理系统会缓存历史 token 的 K/V:
prefill: 输入 prompt,一次性计算 prompt 中所有 token 的 KV
decode : 每生成一个新 token,只计算新 token 的 KV,并读取历史 KV 做 attentionKV cache 的大小可以粗略写成:
其中:
- 表示 K 和 V 两份张量。
- 是模型层数。
- 是 token 数。
- 是每个 token 的 KV 隐藏维度。
- 是每个元素的字节数,例如 FP16/BF16 是 2 字节,量化后更小。
上下文越长、并发越高、模型越大,KV cache 越快变成显存瓶颈。长上下文、RAG、代码助手、多轮会话和 agent 工作流让这个问题更突出,因为大量请求会反复携带相似的系统提示词、文档、工具说明、会话历史或检索片段。
一个简单例子:
请求1: [公司知识库文档A 12000 tokens][问题: 总结风险]
请求2: [公司知识库文档A 12000 tokens][问题: 给出审计建议]
请求3: [公司知识库文档A 12000 tokens][问题: 提取合同条款]如果三个请求都重新 prefill 这 12000 tokens,GPU 会重复做大量相同计算。理想情况是:
- 请求1算出文档A前缀的 KV。
- 系统把这段 KV 存起来。
- 请求2和请求3只加载这段 KV,再计算各自的短问题部分。
这就是 cross-query KV reuse。
再看 P/D 分离:
prefill worker: 处理长 prompt,生成 prompt KV
decode worker : 读取 prompt KV,专注低延迟逐 token 生成这里 KV cache 必须从 prefiller 传到 decoder。它已经不是一个“本请求内的显存优化”,而是两个推理实例之间的通信数据。
所以 LMCache 的动机可以概括为:
只要 KV cache 需要跨请求、跨 worker 或跨存储层复用,它就必须拥有类似存储系统的能力:命名、查找、迁移、驱逐、固定、压缩和高效传输。
3. 论文观察到的真实趋势
论文用 LMCache 真实部署中的 usage tracker 数据说明了几个趋势。
第一,存储的 KV cache 总量在增长,而且超过 GPU memory 的部分也在增长。这说明只靠 GPU 显存保留缓存不现实。很多 KV 必须被 offload 到 CPU、磁盘、远端内存或对象存储。
第二,超过 GPU capacity 的 token 也在被复用。论文用 “average reuse per token” 描述存储 token 被再次访问的次数,并观察到复用在增长。这意味着 offload 到 GPU 外部的 KV 并不是冷数据垃圾,而是有机会被加载回来继续创造价值。
第三,生产里前缀复用不只来自固定 system prompt。论文提到一些企业用户在真正上线后才发现 prefix cache hit ratio 很高,例如公司 G 生产环境里约 50% 命中。原因是现代应用存在大量“动态可复用上下文”:
- coding assistant 的历史对话和代码上下文。
- chat 应用的多轮历史。
- RAG 中重复出现的文档片段。
- agent 工作流中的工具说明、任务模板和中间状态。
这点很关键。过去很多人以为 prefix cache 只适合“固定 prompt + 多个问题”的 benchmark,但论文认为真实企业工作负载里可复用上下文比想象中多。
4. LMCache放在系统里的位置
论文把 LMCache 放在推理引擎和后端存储/网络之间。
flowchart TD
A[Client / Application] --> B[Router / Scheduler]
B --> C[LLM Inference Engine<br/>vLLM / SGLang]
C --> D[KV Connector]
D --> E[LMCache<br/>Distributed KV Cache Layer]
E --> F[GPU Memory]
E --> G[CPU DRAM]
E --> H[Local SSD]
E --> I[Remote DRAM / Disk]
E --> J[Redis / Object Store]
E --> K[RDMA / NVLink / TCP]
这个位置决定了 LMCache 的角色不是替代 vLLM 或 SGLang,而是补齐推理引擎和外部存储/网络之间的 KV movement layer。
从模块看,论文里的 LMCache end-to-end workflow 包括这些核心组件:
| 组件 | 职责 |
|---|---|
| KV Connector | 对接 vLLM/SGLang,拿到 token、block/page、GPU 地址和调度元数据 |
| Token Processor | 判断哪些 token 已经命中、哪些 token 需要新存储 |
| Storage Manager | 管理向后端存储的保存、查找、读取 |
| GPU Connector | 负责 GPU paged memory 和 LMCache buffer 之间的数据搬运 |
| Transfer Channel | 封装 CPU/GPU/SSD/网络之间的数据传输 |
| Memory Allocator | 管理中间 buffer 和存储后端的内存分配 |
| Event Manager | 管理异步 load/store 事件,支持 pipeline |
| Cache Controller | 维护全局 KV metadata,提供 lookup/move/pin/clear/compress 等控制 API |
| LMCache Worker | 每个实例本地的控制面代理,向 controller 汇报 admit/evict 等状态 |
换句话说,LMCache 有两条路径:
- 数据面:真的搬 KV。
- 控制面:知道 KV 在哪里、能不能用、该不该迁移、要不要 pin。
5. Store与Retrieve流程
论文把最常见的两个流程拆成 store 和 retrieve。
5.1 Store:把新算出来的KV存出去
当新请求进入推理引擎,系统需要决定哪些 token 的 KV 是新产生的,需要保存到 LMCache 后端。
sequenceDiagram
participant Q as Query
participant E as vLLM / SGLang
participant C as KV Connector
participant T as Token Processor
participant S as Storage Manager
participant B as Backend Storage
Q->>E: request with tokens
E->>C: expose tokens + block/page metadata + GPU addresses
C->>T: ask which token chunks are new
T-->>C: new chunks to store
C->>S: start_store_kv(metadata)
S->>B: write KV chunks to CPU / disk / remote backend
B-->>S: completion
S-->>C: store done
注意这里不是每生成一个 token 就马上写出。LMCache 会做 delayed decode KV storing:decode 过程中积累到一定 chunk 后再批量写,避免大量小写。
5.2 Retrieve:命中后把KV加载回GPU
当一个请求共享了已有前缀,系统要把命中部分从 LMCache 后端加载回 GPU paged KV memory。
sequenceDiagram
participant Q as Query
participant Sch as Scheduler
participant C as KV Connector
participant L as LMCache
participant S as Storage Manager
participant G as GPU Connector
participant R as Model Runner
Q->>Sch: arrive
Sch->>C: get_num_new_matched_tokens(query)
C->>L: lookup prefix tokens
L-->>C: matched token count / maybe defer
C-->>Sch: external cache hit tokens
Sch->>Sch: allocate GPU KV blocks
Sch->>C: build_connector_meta(block addresses)
C->>R: pass load metadata
R->>G: start_load_kv()
G->>S: fetch chunks from backend
S-->>G: chunks in transfer buffer
G->>G: scatter chunks into paged KV memory
R->>R: run attention with loaded KV
论文里一个细节很重要:get_num_new_matched_tokens 可以返回 None。这表示 LMCache 可能让 vLLM 先把这个请求放回 waiting queue,处理别的请求,同时让当前请求的 I/O 和其他请求的计算重叠。这个设计把 KV loading 从“阻塞式等待”变成了调度层可感知的异步行为。
6. 三个系统挑战
LMCache 论文把难点归纳为三个。
6.1 Paged memory导致小IO太多
vLLM、SGLang 这类高吞吐推理引擎通常使用 paged attention / paged KV memory。它们把 KV cache 拆成小 page,例如 vLLM 一个 page 常常对应 16 个 token 的某一层 KV。论文提到一些常见模型的 KV page 大小大约在 20KB 到 63KB 范围内。
这种设计对 GPU 显存管理和 continuous batching 很友好,但对存储/网络传输不友好。因为你会面对大量小块 I/O:
层数 L = 80
每层 page 数 = 很多
每个 page 只有几十 KB
一次长上下文 load/store = 大量小 copy / 小网络消息小 I/O 的问题是带宽打不满。论文引用的 RCCL/UCCL 传输结果显示,message size 从 64KB 增大到 16MB 时,吞吐从 4GB/s 提升到约 49GB/s,之后基本饱和。
这解释了为什么“能搬”不等于“搬得快”。如果 KV movement 按 page 粒度直接做,即使网络或 PCIe 理论带宽很高,实际有效带宽也会很低。
6.2 推理引擎演进太快
LMCache 需要接入 vLLM 和 SGLang,但这些引擎变化非常快。新模型、新 attention kernel、新硬件后端、新 KV layout 都可能改变 KV cache 在 GPU memory 里的形状和地址组织。
论文举的方向包括:
- Sliding Window Attention。
- Multi-Head Latent Attention。
- 新 attention kernel 的 KV layout 改动。
- scheduler-worker 分离带来的接口边界变化。
如果 LMCache 每次都直接侵入引擎内部实现,就会被上游变化拖垮。因此它需要一个标准化 connector,把“推理引擎内部如何管理 KV”与“外部 KV cache 层如何 load/store/move”解耦。
6.3 缺少KV管理API
只做自动缓存还不够。真实生产系统里,router、scheduler、运维系统、业务应用都可能需要显式控制 KV。
比如:
- Router 想知道哪个实例已经有某段前缀 KV,从而把请求路由过去。
- 金融文档服务想 pin 热门报告的 KV,避免被驱逐。
- Agent 平台想对某段 KV 做压缩,再跨节点迁移。
- 扩缩容时,一个实例即将下线,需要把重要 KV move 到别的实例。
- 切换模型或租户时,需要 clear 某些 KV。
这就是 LMCache controller API 的价值。
7. 性能优化:LMCache为什么快
论文的优化可以分成三类:批量化、计算与 I/O 重叠、减少拷贝。
7.1 Chunk而不是Page
LMCache 不直接以推理引擎的小 page 为传输单位,而是把多个 page、甚至跨层的数据聚合成更大的 chunk。论文提到默认 chunk size 是 256 tokens,且可配置。
简化过程如下:
flowchart LR
A[Scattered Paged KV<br/>GPU memory] --> B[CUDA gather]
B --> C[Contiguous Streaming Buffer<br/>large chunk]
C --> D[DMA / Network Transfer]
D --> E[CPU / SSD / Remote Backend]
加载时反过来:
flowchart LR
A[Backend Storage] --> B[DMA / Network Transfer]
B --> C[Contiguous GPU Buffer]
C --> D[CUDA scatter]
D --> E[Paged KV Memory]
这正是论文里 CPU loading bandwidth 能从 vLLM native CPU offloading 的 88Gbps 提高到 LMCache 400Gbps 的关键原因之一:传输粒度更大,CUDA memcpy / DMA / 网络消息的固定开销被摊薄。
7.2 Layer-wise pipelining
LMCache 支持按层流水线加载。它不是等所有层 KV 都加载完再开始计算,而是让第 层的加载与第 层的计算重叠。
gantt
title Layer-wise KV Load and Compute Pipeline
dateFormat X
axisFormat %s
section Layer 1
load L1 :0, 1
compute L1 :1, 3
section Layer 2
load L2 :1, 2
compute L2 :3, 5
section Layer 3
load L3 :3, 4
compute L3 :5, 7
真实实现中会使用不同 CUDA stream 区分计算和数据搬运。这样做有两个好处:
- 只需要一个固定大小的 GPU buffer,例如一层 KV 的 buffer,而不是为完整上下文所有层准备一大块临时空间。
- I/O 延迟可以隐藏在模型计算后面,降低端到端延迟。
论文 Figure 13 展示了 request asynchronization 后,KV loading 可以与 prefill/decode computation 重叠,端到端 delay 降低约 1.46 倍。
7.3 请求级异步预取
很多时候,请求进入 scheduler 后不会立刻执行,因为 GPU 正在处理其他请求。LMCache 利用这段排队时间预取 KV:
请求到达 -> scheduler waiting queue -> LMCache 先从远端拉到本地 CPU/GPU buffer -> 请求真正执行时少等 I/O这也是 get_num_new_matched_tokens 允许返回 None 的原因之一。它让 connector 不只是回答“命中多少 token”,还可以影响调度:这个请求先等等,我去做 I/O;你先跑其他更合适的请求。
7.4 最小化数据拷贝
多后端写入时,如果每个目标都复制一份 KV,中间内存压力会很大。LMCache 用引用计数管理共享数据:同一份 KV 被多个传输任务读写时,不马上复制,任务完成后递减计数,计数为 0 再释放。
这类机制在存储系统里很常见,但放到 KV cache movement 里很重要,因为 KV cache 本身非常大。
7.5 Dynamic Offloading
论文还讨论了 dynamic offloading。现代推理引擎会维护一批 GPU free pages。朴素策略是把所有 free pages 都复制到 CPU,但这会浪费 CPU 内存和带宽。LMCache 只复制一段窗口,用三个指针管理:
| 指针 | 含义 |
|---|---|
| Start pointer | free-page region 起点 |
| Current pointer | 已经 offload 到 CPU 的位置 |
| End pointer | 计划 offload 的终点 |
核心 tradeoff 是 duplication window 的大小:
- 窗口小:少复制,节省资源,但请求申请较多 page 时更容易 stall。
- 窗口大:更少 stall,但复制更多,浪费带宽和 CPU 内存。
这说明 KV offload 不是“越积极越好”,而是在 GPU page allocation latency、offload bandwidth、CPU memory 占用之间做平衡。
8. Connector接口:LMCache与vLLM/SGLang的解耦点
论文第 6 节是我认为最值得工程实现者关注的部分。LMCache 需要推理引擎暴露标准化 connector,使外部 KV cache 能参与调度和模型执行。
论文列出的核心接口如下。
| 接口 | 所在阶段 | 作用 |
|---|---|---|
get_num_new_matched_tokens(query) |
scheduler | 查询 LMCache 后端命中多少 token;也可返回 None 表示先延迟该请求 |
update_state_after_alloc(query, blocks, num_external_blocks) |
scheduler | GPU block 分配后记录哪些 block 需要从外部加载 |
build_connector_meta(scheduler_output) |
scheduler | 构造 worker/model runner 所需的 KV transfer metadata,包括 GPU page 地址 |
start_load_kv(kv_pointers) |
model runner | inference 前开始从低层存储加载 KV |
wait_load_kv(kv_pointers, layer_id) |
model runner / attention 前 | 等待当前层 KV 可用 |
start_store_kv(kv_pointer) |
model runner | computation 后开始把新 KV offload 到低层存储 |
wait_store_kv(kv_pointer, layer_id) |
model runner / attention 后 | 等待当前层 KV store 完成 |
这个接口拆分很有意思,因为它遵循 vLLM 的 scheduler 和 worker/model runner 分离:
- Scheduler 侧关心“命中多少 token、要不要分配 GPU blocks、怎么影响调度”。
- Model runner 侧关心“具体在哪个 layer load/store、什么时候 wait、KV page 地址是什么”。
如果把这两个层次混在一起,connector 很容易变成一个巨大 invasive patch。LMCache 的接口设计让外部 KV 层既能影响调度,又不强行接管模型执行。
简化生命周期如下:
sequenceDiagram
participant S as Scheduler
participant C as LMCache Connector
participant W as Model Runner
participant A as Attention Layer
participant B as Backend
S->>C: get_num_new_matched_tokens(query)
C->>B: lookup prefix chunks
B-->>C: hit tokens
C-->>S: matched tokens / None
S->>S: allocate KV blocks
S->>C: update_state_after_alloc(...)
S->>C: build_connector_meta(...)
C-->>W: metadata with GPU addresses
W->>C: start_load_kv(...)
A->>C: wait_load_kv(layer_id)
A->>A: attention compute
A->>C: start_store_kv(...)
C->>B: store generated KV chunks
论文还强调这个 API 在 vLLM 中已经运行超过六个月,并被 Dynamo、llm-d、AIBrix、vLLM production stack 等项目或公司 connector 采用。这说明它不是单纯的论文接口,而是已经进入生态协作。
9. Controller API:把KV变成可管理对象
LMCache controller 是集中式元数据管理组件。它维护全局 KV cache 状态,让上层系统可以做 cache-aware routing、migration、P2P sharing、clear、pin、compression 等操作。
论文把 API 分成 internal 和 external 两类。
9.1 Internal API
| API | 作用 |
|---|---|
batched_admit(hashes, inst_id, device) |
某实例把 KV 写入某设备后,批量上报 |
batched_evict(hashes, inst_id, device) |
某实例驱逐 KV 后,批量上报 |
batched_p2p_lookup(hashes) |
某实例本地 miss 后,向 controller 查询其他 peer 是否有 KV |
这些接口主要服务 LMCache 实例之间的元数据同步。
9.2 External API
| API | 作用 |
|---|---|
lookup(tokens) |
查询给定 tokens 的 KV 在哪些实例和设备上存在 |
move(src, dst, tokens) |
把某段 KV 从源实例/设备迁移到目标实例/设备 |
clear(tokens, inst_id, device) |
删除某实例某设备上的 KV |
pin(tokens, inst_id, device) / unpin(...) |
固定或取消固定某段 KV |
compress(tokens, inst_id, device, method) / decompress(...) |
对指定 KV 做压缩或解压 |
这些 API 让上层系统可以把 KV cache 当成一类数据资产。
例如 cache-aware routing:
flowchart LR
A[Incoming Request Tokens] --> B[Router]
B --> C[LMCache Controller lookup]
C --> D{Which instance has best prefix hit?}
D --> E[Instance 1<br/>GPU hit]
D --> F[Instance 2<br/>CPU hit]
D --> G[Instance 3<br/>remote miss]
E --> H[Route to best target]
又如扩缩容迁移:
instance-A 即将下线
controller.lookup 找到重要 KV
controller.move((A, CPU), (B, CPU), tokens)
router 后续把相关请求导向 instance-B
controller.clear(tokens, A, CPU)这类能力是推理引擎内置 GPU prefix cache 通常不提供的,因为它们的目标是单实例内的显存复用,而不是分布式 KV 生命周期管理。
10. 评测结果怎么读
论文评测覆盖三类常见部署:
| 场景 | 说明 |
|---|---|
| CPU Offload | 单节点,KV offload 到 CPU DRAM |
| Central Storage | 通过以太网连接集中式远端存储后端 |
| PD | prefiller 和 decoder 通过 NVLink 连接,做 P/D disaggregation |
模型包括 Llama-3.1-8B、Llama-3.1-70B、Qwen2.5-Coder-32B、Qwen2.5-72B、Qwen3-Coder-480B-A35B-FP8 等。硬件主要是 8xH100,单节点和多节点按模型需要分配 GPU。指标包括:
- TTFT:time to first token,主要反映 prefill 或加载前缀 KV 的延迟。
- ITL:inter-token latency,反映 decode 阶段每 token 间隔。
- Throughput / QPS:在相同 TTFT 下能支撑的请求率。
10.1 单节点CPU Offload
论文用多轮 Q&A / 文档分析 workload 做测试。默认每个 query 含约 10K tokens 上下文和短问题,输出最多 100 tokens;Llama-3.1-8B 因为模型较小,输入设为 20K tokens。LMCache 最大使用 500GB CPU memory。
关键结果:
- 低 QPS 下,LMCache 的 TTFT 比 baselines 小 1.9-8.1 倍。
- 相同 TTFT 下,吞吐比最强 baseline 高 2.3-14 倍。
- ITL 也有改善,论文报告相对最佳 baseline 在 QPS=1 时降低 7%-92%。
原因不是“CPU 比 GPU 快”,而是:
- GPU prefix cache 容量有限,很多可复用 KV 留不住。
- CPU memory 容量大,可以保留更多长上下文 KV,提高 hit ratio。
- LMCache 的 chunk-level transfer 比 vLLM native CPU offloading 的 page-level transfer 更能打满带宽。
10.2 真实Trace
论文还使用公司 F 和公司 G 的真实输入/输出 token 分布构造 trace。由于没有公司 F 的 proprietary model,作者用五个开源模型重放分布,并把原本几天的 trace 拉伸/压缩到一小时内完成。
结果:
- 公司 F trace 上,高 QPS 下 TTFT 至少降低 4.4-6.6 倍,ITL 降低 34%-58%。
- 公司 F/G 多模型结果中,TTFT 降低 3.7-6.8 倍,ITL 降低 19%-58%。
这部分说明 LMCache 不只是 synthetic “固定 prompt” workload 下有效,在真实请求长度分布下也有效。
10.3 远端集中式存储
远端存储通过 15Gbps 网络连接 GPU instance。论文使用 LongBench 的 TriviaQA,并按 vLLM 官方 benchmark 脚本生成 Poisson 到达的请求。
结果是相同 TTFT 下吞吐提升 1.3-3 倍。
这里要注意一个反直觉点:远端存储延迟和带宽都比本地 CPU memory 差,但它可以存更多 KV,因此 hit ratio 更高。是否收益取决于:
收益 = 避免 prefill 的计算时间 - 远端加载 KV 的时间 - 额外调度/传输开销如果上下文很长,prefill 很贵,远端 load 就可能划算。如果上下文很短,模型 prefill 很快,load 可能不划算。
论文在 sensitivity study 里给出一个典型判断:在 32Gbps 网络下,KV loading 只有在输入超过 256K tokens 时才优于 naive prefill;在 64Gbps 或 128Gbps 下,loading 在所有测试长度上都优于 prefill。
10.4 P/D Disaggregation
P/D 场景中,LMCache 与 vLLM native PD disaggregation 对比。输入 8K tokens,输出 200 tokens。
论文结果:
- 95th percentile TTFT 明显更低。
- 平均 TTFT 降低 1.53-1.84 倍。
- 平均 ITL 降低 1.12-1.66 倍。
原因还是传输粒度。vLLM native PD 用 NIXL 按 paged KV memory 地址直接从 prefiller 拷贝到 decoder。当 KV 在 prefiller GPU memory 中分散时,传输粒度细、带宽利用率低。LMCache 先把 chunked prefill 产生的 KV 拷到连续 buffer,再传给 decoder,对端再 scatter 到 paged memory,整体传输更高效。
论文的 component-wise breakdown 也说明,prefill/decode 计算时间在两者中相同,差距主要来自 KV transmission latency。
10.5 SGLang结果
虽然论文主要评测 vLLM,但也给出 SGLang 集成结果。Qwen3-32B 在两张 H100、TP=2 下:
- SGLang + LMCache CPU offloading 相比无 offloading 有更高吞吐、更低平均 TTFT 和端到端延迟。
- 相比 SGLang native CPU offloading,LMCache 性能接近。
- 但 SGLang native CPU offloading 缺少 LMCache 这种分布式、多层级存储后端能力。
这部分说明 LMCache 的价值不完全在单节点 CPU offload 的绝对性能,而在跨引擎、跨后端的统一 KV layer。
11. 一个实用判断:什么时候应该Load KV,什么时候应该Prefill
读完论文后,我认为最实用的工程判断是这个不等式:
只有左边小于右边,加载 KV 才值得。
更具体地说:
应该 load KV:
- 长上下文,例如几十K到几百K tokens
- prefix hit ratio 高
- 后端带宽足够高
- 模型 prefill 成本高
- 请求排队时间可以用于异步预取
- P/D 分离中必须跨 worker 传 KV
应该重新 prefill:
- 上下文短
- 命中 token 少或命中不连续
- 网络带宽低
- 后端 load tail latency 不稳定
- 模型小,prefill 本身很快
- KV layout 转换和 scatter 开销大于收益一个粗略例子:
请求A命中 100K tokens
从远端加载 + scatter 需要 800ms
重新 prefill 100K tokens 需要 4s
=> load 值得
请求B命中 2K tokens
从远端加载 + scatter 需要 120ms
重新 prefill 2K tokens 需要 60ms
=> 重新 prefill 更好这也是为什么 controller、scheduler 和 connector 必须协同。KV cache 系统不能只是存储层单方面决定“命中就加载”,它需要把命中长度、存储位置、带宽、当前队列、SLO 和模型 prefill 成本放在一起判断。
12. 生产经验里最值得记住的点
论文第 9 节很有价值,因为它来自生产部署,不只是 benchmark。
12.1 远端存储不一定慢
传统直觉认为远端对象存储只适合降低成本,不适合降低延迟。但论文提到,随着 S3 Express 等远端存储性能提升,一些用户从自己的远端对象存储加载 KV,相比完整 prefill 获得了 22%-32% 更低 TTFT。
这说明 KV cache 的存储层级不能只按“本地快、远端慢”做静态判断,而要看:
- 远端后端的实际吞吐。
- 上下文长度。
- 模型 prefill 速度。
- hit ratio。
- 是否能预取。
12.2 Context truncation会显著降低prefix hit
很多生产系统为了控制上下文长度,会保留最近 tokens,截断旧 tokens。这个策略对模型上下文窗口友好,但对 prefix cache 不友好。
原因是 prefix cache 依赖从开头开始匹配。如果你把前面的 tokens 删掉,只保留最近上下文,那么新的请求 token 序列不再与之前缓存的前缀对齐。
论文提到公司 F 的真实 trace 中,truncation 让 prefix cache hit ratio 从约 85% 降到约 45%。
这对 agent 和 chat 系统尤其重要。上下文工程不只是“把最相关内容塞进窗口”,还要考虑缓存可复用性。频繁动态插入、删除、重排上下文,都会破坏 KV reuse。
12.3 容器化比源码级集成更符合企业使用
论文提到许多企业用户只使用官方 Docker image,而不会深入改 LMCache 源码。这个观察很现实:推理基础设施在 Kubernetes 上运行,企业更关心稳定镜像、配置、监控和回滚,而不是每次都自己 patch runtime。
这也解释了为什么 LMCache 重视和 vLLM/SGLang 的 connector API 兼容,而不是做一个只适合研究 prototype 的灵活接口。
12.4 Python仍然可以承担控制层
论文提到行业有转向 Rust/C++ 的趋势,但 LMCache 仍选择 Python 加精心优化的底层路径。这不是说 Python 数据搬运本身快,而是把性能关键路径下沉到 CUDA、DMA、传输库、后端存储接口,把 Python 留在 orchestration 和快速迭代层。
这个取舍可以总结为:
- 高频大数据搬运:必须靠底层高性能实现。
- 控制逻辑、connector、生态适配:Python 的迭代速度和 ML 生态优势仍然重要。
13. LMCache与其他方案的边界
论文比较了几类相关工作。
13.1 推理框架内置Prefix Cache
vLLM/SGLang 内置 prefix cache 的优势是和 runtime 紧密结合,GPU 内命中很快。缺点是通常受限于单实例、单节点或 GPU memory,缺少统一的分布式存储与控制 API。
LMCache 不是替代内置 prefix cache,而是扩展它:
GPU 内 prefix cache: 最高速、容量小、生命周期短
LMCache: 层级存储、容量大、可跨实例、可管理13.2 存储系统或分布式对象缓存
Mooncake、Redis、InfiniStore、3FS 等可以提供后端存储能力,但它们本身不理解 vLLM/SGLang 的 paged KV layout、layer-wise loading、GPU page 地址、scheduler hooks 和 attention 前后的时机。
LMCache 的定位是 glue layer:
推理引擎内部KV布局 <-> 高效数据搬运 <-> 多种存储/网络后端13.3 研究型KV优化
很多论文研究 prefix caching、KV compression、PD disaggregation、token dropping 等,但常常是 prototype,和生产引擎/后端生态绑定较弱。LMCache 的特点是更强调工程可落地:connector 稳定、后端多、控制 API 明确、和 vLLM/SGLang 生态协作。
14. 我对这篇论文的理解
这篇论文真正重要的地方不是某一个数字,而是它提出了一个推理系统设计方向:
未来 LLM inference 不会只是“请求进来、模型跑完、请求结束”。它会更像一个持久化、缓存感知、数据可迁移的计算系统。
在这个系统里:
- KV cache 是一类可复用中间数据。
- Router 需要知道 KV 在哪里。
- Scheduler 需要知道 load 和 prefill 哪个更划算。
- Engine 需要暴露标准 connector,而不是封闭 KV layout。
- Storage layer 需要理解 chunk、page、layer、GPU buffer 和异步事件。
- 应用层的 context engineering 会直接影响 KV hit ratio。
换句话说,KV cache 正在从一个 kernel/runtime 优化点,变成一个跨应用、跨调度、跨存储、跨网络的系统抽象。
这也解释了为什么 LMCache 同时强调三件事:
- Performance:没有高带宽 load/store,一切复用都可能被 I/O 拖垮。
- Interface:没有 connector,就跟不上 vLLM/SGLang 的快速演进。
- Control API:没有显式管理,上层 router 和业务系统无法真正利用 KV。
15. 实践建议
如果要在生产系统里评估 LMCache 或类似 KV cache layer,我会按下面顺序看。
15.1 先看 workload 是否真的可复用
需要统计:
- prefix hit ratio。
- matched tokens 分布,而不是只看命中请求比例。
- 命中的 KV 当前在 GPU、CPU、SSD 还是远端。
- 多轮会话、RAG 文档、agent 模板是否稳定。
- context truncation / reranking / prompt rewriting 是否破坏前缀。
15.2 再看load与prefill的交叉点
需要测:
- 不同 context length 的 prefill time。
- 不同后端的 load + scatter time。
- 不同网络带宽下的 tail latency。
- chunk size 对吞吐和延迟的影响。
- 异步预取能隐藏多少 I/O。
最后得到类似这样的策略:
if backend == local_cpu and matched_tokens > 4K:
load
elif backend == remote and bandwidth >= 64Gbps and matched_tokens > 8K:
load
elif backend == remote and matched_tokens > 256K:
load
else:
prefill真实阈值不能照抄,要按模型、硬件、后端、SLO 实测。
15.3 最后看控制面是否接得上
一个 KV cache layer 要在企业环境里有用,不能只提供 runtime 插件。还要回答:
- Router 能不能 lookup KV 位置?
- 扩缩容时能不能 move KV?
- 热门文档能不能 pin?
- 租户或模型切换时能不能 clear?
- 压缩是否可控、可回滚、可观测?
- 命中、加载延迟、失败、驱逐是否有指标?
这些问题比单次 benchmark 数字更决定长期可用性。
16. 总结
LMCache 论文可以浓缩成一句话:
企业级 LLM 推理里,KV cache 已经大到、贵到、可复用到必须被当成一等公民管理。
它的贡献不是简单的 CPU offload,而是一套完整 KV cache layer:
- 通过 chunk-level transfer、layer-wise pipelining、异步预取和最小拷贝提高数据搬运效率。
- 通过 connector API 解耦 vLLM/SGLang 快速变化的内部 KV layout。
- 通过 controller API 让 KV 支持 lookup、move、pin、clear、compress 等显式操作。
- 通过多层级后端支持跨请求、跨实例、跨 P/D worker 的 KV 复用。
这篇论文对做推理系统的人最大的提醒是:不要只问“这个请求怎么最快跑完”,还要问“这个请求产生的中间状态,未来能不能被别人复用”。当答案是肯定的,KV cache 就不再只是显存里的临时张量,而是推理集群里的共享数据资产。
参考
- Yuhan Liu, Yihua Cheng, Jiayi Yao, et al. LMCache: An Efficient KV Cache Layer for Enterprise-Scale LLM Inference, arXiv:2510.09665v2, 2025-12-05.
- 论文 PDF:https://arxiv.org/pdf/2510.09665
- LMCache GitHub:https://github.com/LMCache/LMCache