LMCache: An Efficient KV Cache Layer for Enterprise-Scale LLM Inference

Paper: arXiv:2510.09665 Code: LMCache/LMCache Code reference: dev @ f9ce8cbe (2026-05-19)

1. Motivation(研究动机)

这篇论文要解决的不是“Transformer 怎么缓存 KV”这个已知问题,而是一个更贴近企业级服务的系统问题:KV cache 已经从单个请求内部的 GPU-local 加速结构,变成需要跨请求、跨引擎、跨存储层长期管理和移动的数据资产。传统推理系统把每个请求视为相互独立:prefill 阶段生成输入 prompt 的 中间状态,decode 阶段在同一引擎实例的 GPU memory 中复用这些状态,生成结束后缓存基本随请求生命周期消失。这个假设在短上下文、低并发、单实例服务中可行,但在长文档问答、多轮对话、RAG、coding assistant 和 agentic workflow 中会失效,因为很多请求共享相同长前缀、系统 prompt、检索文档或历史上下文;如果每次都重新 prefill,同一批 token 的注意力状态会被反复计算,TTFT 和 GPU-hours 都被浪费。

论文给出的关键动机来自真实使用统计,而不是单纯的合成 benchmark。作者观察到,用户存储的 KV cache 总量快速增长,并且越来越多缓存已经超过 GPU memory 可容纳范围;同时,被放到 GPU 外部的 token 被复用的频率也在增加。换句话说,KV cache 不只是“偶尔溢出 GPU 的冷数据”,而是会被反复访问的热数据。若系统只能把 KV 保存在 GPU 上,那么可保存的前缀很少,命中率受 GPU 容量限制;若系统把 KV 放到 CPU、disk、remote object store 或其他节点,又会遇到小粒度 page 传输、格式兼容、缓存定位和迁移控制的问题。LMCache 的研究价值就在于:把这些散落的问题抽象成一个可复用的 KV caching layer,让上层 inference engine 不需要为每个后端、每个网络、每个部署场景手写一次脆弱的数据搬运逻辑。

Figure 1 解读:这张图说明 LMCache 的使用规模、缓存数据规模和 Docker image 拉取量都在增长。它不是方法细节图,而是论文论证“KV cache 管理已经成为生产问题”的证据。图中最重要的信息是趋势:LMCache 被更多用户采用、承载更大规模缓存,因此一个只为单机实验设计的缓存原型很难满足真实服务的稳定性、兼容性和可运维性需求。

Figure 2 解读:论文把“KV cache 离开 GPU”分成两个主场景。第一类是 cross-query context caching:把某个请求生成的前缀 KV 持久化,后续共享前缀的请求直接加载这些 KV,避免重复 prefill。第二类是 prefill–decode disaggregation:prefill GPU 负责吞吐导向的长输入处理,decode GPU 负责延迟敏感的逐 token 生成,中间必须把 prefill 生成的 KV 传给 decode 侧。LMCache 试图用同一套抽象同时覆盖这两类场景。

Figure 3 解读:这张图展示了真实用户中“无法放进 GPU memory 的 KV cache”随时间增长。它支撑了论文的第一个动机判断:GPU memory 不是长期 KV cache 的充分存储层。即使 vLLM/SGLang 等引擎已经有 GPU 内部 prefix caching,一旦上下文规模和用户并发扩大,缓存容量瓶颈仍然会把大量可复用上下文挤出 GPU。

Figure 4 解读:这张图关注 GPU 外缓存的 reuse per token。左侧趋势说明被存储到 GPU 外的 token 正在被更频繁复用;右侧分布显示超过 19% 的用户会让已存储 token 被复用超过 1.5 次。对系统设计而言,这意味着外部 KV 不是“写一次、几乎不读”的归档数据,而是需要高吞吐、低延迟读回的服务路径。

论文指出现有方案的瓶颈主要有三类。第一是 I/O inefficiency under paged memory:现代推理引擎为了 batching 和 GPU memory utilization 使用 paged attention,page 通常很小;论文举例 vLLM 在 Llama-3.1-8B-Instruct 上的 page 大约 62.5 KB,而 page 级别传输很难打满 PCIe、NVLink、RDMA 或 Ethernet 带宽。论文表 1 引用的 RCCL 传输数据说明,64 KB 消息只有约 4 GBps,256 KB 约 13 GBps,1 MB 约 30 GBps,直到 16 MB 或 100 MB 才接近 49 GBps。

第二是 compatibility:2025 年每周有大量新模型或硬件配置出现,inference engine 会不断改变 attention kernel、KV layout、MLA/SWA 等实现细节,如果 caching layer 和某个引擎内部布局强耦合,维护成本会非常高。第三是缺少 management APIs:生产系统中的 router、scheduler、autoscaler、operator 需要知道某段 token 的 KV 在哪里、能否 pin、能否 move、能否 compress、何时 evict;没有统一控制面就会导致重复存储、错误路由和不可预测的 eviction。

2. Idea(核心思想)

LMCache 的核心思想可以压缩成三句话:把 KV cache 从 inference engine 的内部临时状态提升为可显式管理的数据对象;用 standardized KV connector 屏蔽 vLLM、SGLang 等引擎的快速变化;用 chunk-level batched transfer、compute-I/O overlap 和 zero-copy/reference-counting 把跨 GPU/CPU/storage/network 的 KV 数据移动做得足够快,快到 prefix reuse 和 PD disaggregation 在生产部署中值得开启。

这篇论文的创新点不是提出新的 attention 数学结构,也不是改变模型权重,而是在系统边界上重新划分职责。inference engine 仍然负责调度、prefill、decode 和 paged attention;LMCache 插在引擎与异构存储/网络之间,负责 KV 的抽取、存储、查找、加载、迁移、压缩和控制。这样的分层使它既能服务单机 CPU offloading,也能服务 remote backend、P2P sharing、PD disaggregation、cache-aware routing 等更复杂场景。对企业部署来说,最有价值的是统一抽象:同一个上层请求可以先查 LocalCPUBackend,再查 LocalDiskBackend 或 RemoteBackend,也可以通过 controller 查其他实例;同一个 connector API 可以让 vLLM scheduler 把外部 cache hit token 当成 prefix-cached token 参与调度,让 model runner 在 attention 前后插入 load/store hook。

Figure 5 解读:LMCache 位于 LLM inference engines 和 heterogeneous storage/network devices 之间。上层是 vLLM、SGLang 等快速演进的引擎,下层是 CPU DRAM、local disk、remote storage、network transport 等不同速度和容量的后端。图的重点是“中间层”:LMCache 不是单个 cache backend,而是一个把引擎内 paged KV 转换成可搬运数据对象的层。

Figure 6 解读:端到端流程由 KV connector、token processor、event manager、storage manager、transfer channel 和 cache controller 等组件组成。store 路径中,connector 准备 tokenized prompt 和 GPU page 地址,token processor 判断哪些 token 尚未存入后端,storage manager 经 transfer channel 写入后端。retrieve 路径中,connector 准备加载元数据,token processor 找 prefix hit,event manager 复用已有异步加载事件或触发 layer-wise loading,storage manager 查找并返回 memory objects。

从系统设计角度看,LMCache 的主要直觉是“不要用引擎的 page 粒度做系统级数据移动”。vLLM 的小 page 对 GPU 内部 memory utilization 有利,但对外部 I/O 不友好。LMCache 因此用 configurable chunk size 聚合多个 page 和多个 layer 的 KV,把散乱 paged memory 先拷贝到连续 streaming GPU buffer,再以 chunk 为单位走 DMA、network 或 storage backend。这个思路解释了为什么它能同时改善 CPU offloading 和 PD disaggregation:二者本质上都需要把大量 KV 从一个位置搬到另一个位置,瓶颈都是小粒度 copy kernel 启动开销、带宽利用率和计算-I/O 串行化。

另一个核心直觉是“cache hit 不应只由 serving engine 私下判断”。在 LMCache 设计中,lookup、pin、move、clear、compress 等 API 被提升为 first-class control plane。这样 higher-level router 可以根据全局 token pool 找到最可能命中的实例;autoscaler 可以在缩容前 move 热 KV;金融文档等热点上下文可以被 pin;agent 系统可以先 compress 再跨节点转移。论文把这些管理接口视为长期生产价值的一部分,而不是只看单次请求的 latency。

3. Method(方法)

3.1 总体架构:从 GPU paged KV 到可管理 memory object

LMCache 的 data path 可以理解为四个层次。第一层是 inference engine connector,负责和 vLLM/SGLang 的 scheduler、model runner 对接,拿到 token、slot mapping、block/page 地址、attention metadata 和 layer 信息。第二层是 cache engine,公开 store、retrieve、lookup、move、compress、clear 等核心操作,把 token 序列映射为 cache keys/chunks,并协调 GPU connector 与 storage manager。第三层是 storage manager 和 storage backends,它们真正执行 batched put/get/contains/pin/unpin,可以面向 LocalCPUBackend、LocalDiskBackend、RemoteBackend、PDBackend、P2PBackend、GdsBackend 或插件后端。第四层是 controller/control API,维护跨实例 metadata,支持全局 lookup、cache-aware routing、P2P KV cache sharing 和显式迁移。

Store 的语义是:给定 tokens 或 hashes、KV tensors、mask 和 request metadata,把匹配 mask 的 token 对应 KV 从 serving engine 的 paged GPU buffer 抽取出来,转成 LMCache 的 memory objects,再写入一个或多个后端。Retrieve 的语义是:给定 tokens 或 hashes,找出 prefix hit 的 chunks,把对应 memory objects 从后端读出,经 GPU connector 放回引擎的 paged memory。Lookup 的语义是:不实际搬运 KV,只返回有多少 prefix token 已存在于指定 search range 中;当 pin=True 时还会记录 lookup_id 对应 keys,后续 retrieve/move/compress 完成后再 unpin,避免并发 eviction 破坏正确性。

3.2 性能优化一:chunk-level batched operations

论文首先解决的是 I/O 粒度问题。现代 paged attention 的 page 很小,论文提到 popular models 中 page 通常约 20 KB–63 KB,vLLM footnote 中 page 默认为单层 16 tokens。直接逐 page transfer 会产生大量 CUDA memcpy launch、metadata preparation 和 completion signaling 开销,也难以打满网络或 PCIe 带宽。LMCache 改用 configurable chunk size,论文默认示例为 256 tokens per chunk。一个 chunk 可以包含多层、多页的 KV;store 时先用自定义 CUDA kernel 把散落在 paged GPU memory 中的 KV 收集到连续 streaming GPU buffer,再按 chunk offload 到 CPU DRAM、disk 或 remote backend;load 时先把 chunk 从后端读到 GPU buffer,再拆回 paged memory。

这种 chunking 有两个效果。第一,它把大量小 copy 合并为少量大 copy,使传输大小进入更高带宽区间;论文表 1 的数据说明消息从 64 KB 到 16 MB 时吞吐从 4 GBps 提升到约 49 GBps。第二,它让 storage manager 可以同时对多个 tier 发起 batched store/load,例如一边把 hot context 从 GPU 写到 CPU,一边把 cold context 从 CPU offload 到 disk,或者在 full-duplex interconnect 上同时利用两个方向带宽。注意 chunk size 不是越大越好:过大 chunk 会造成碎片和 cache miss 放大,过小 chunk 则恢复 page-level I/O 的开销;论文强调 chunk size 是 configurable,需要根据模型 KV size、设备带宽和延迟 SLO 调整。

3.3 性能优化二:compute-I/O overlapping 与 layer-wise pipelining

KV cache movement 与模型计算如果串行执行,命中缓存也可能因为加载延迟抵消收益。LMCache 因此使用 layer-wise pipelining:为 inference compute 和 data movement 分配不同 CUDA streams,在第 层计算时异步加载第 层的 KV,或在某层计算完成后异步 store 该层生成的新 KV。这样 GPU 上只需要一个固定大小的 streaming buffer,大小接近单层 KV,而不是一次性为所有层加载完整 KV。论文中描述的 retrieve_layer 过程是:先从 backend 取第一层 memory objects,把它们放入 GPU;每一层计算前等待当前层加载完成,同时触发下一层加载;最后一层加载后结束 generator。store_layer 则反向流水:当前层从 GPU 复制到 CPU memory objects,同时上一层 memory objects 被写入后端。

LMCache 还利用 scheduler queue 中的等待时间做 asynchronous compute/prefetch。如果某个 request 在排队期间已经知道有外部 KV hit,那么系统可以提前把 KV 从慢层移动到快层,例如从 remote disk 到 local CPU 或 GPU buffer。论文把这个优化视为 request asynchronization:不让 cache hit request 必须等到真正执行时才开始 I/O,而是把等待队列中的空隙变成 prefetch 窗口。实验中 Figure 13 表明开启 request asynchronization 后,loading 与 prefill/decode 可以重叠,端到端 delay 减少 1.46 倍。

3.4 性能优化三:minimum data copy 与 dynamic offloading

如果 KV 在 GPU、CPU、disk、remote object store 之间移动时每一步都创建新 copy,内存占用和 copy latency 会快速放大。LMCache 用 reference counter 实现 zero-copy style 的并发读写:当同一份 KV 需要同时写到 local disk 和 remote store,或者多个 transfer task 共享同一 memory object 时,系统增加引用计数而不是复制数据;每个 read/write 完成后减少计数,归零后释放。这个设计类似操作系统中对共享 buffer 的生命周期管理,目标是减少冗余 memory pressure。

Figure 7 解读:dynamic offloading 只复制部分 GPU free pages 到 CPU memory,而不是把所有 free pages 全量备份。start pointer、current pointer、end pointer 描述了待 offload 区间、已完成区间和目标区间。窗口过小会减少重复存储但增加 allocation stall;窗口过大则提高内存开销。这个机制体现了 LMCache 的系统权衡:低延迟分配和低冗余存储不能同时无限优化,需要通过窗口大小控制。

3.5 标准化 KV connector:把 cache hit 纳入 scheduler,把搬运 hook 放入 model runner

LMCache 的 connector API 是论文的第二个主要贡献。vLLM/SGLang 会快速适配新模型和 attention kernel,如果 LMCache 直接依赖某个内部 KV layout,就会频繁 break。论文定义的 connector 分成 scheduler-side 和 model-runner-side 两组接口。

Scheduler 侧包括 get_num_new_matched_tokens(query)update_state_after_alloc(query, blocks, num_external_blocks)build_connector_meta(scheduler_output):前者返回外部 backend 中命中的 token 数,让 vLLM 把这些 token 当作 prefix-cached tokens 参与调度;中间接口在 vLLM KV manager 分配 blocks 后决定哪些 page 需要从外部加载;后者构造 tokenized input、slot mapping 和 GPU page 地址等 metadata。

Model runner 侧包括 start_load_kvwait_load_kvstart_store_kvwait_store_kv,分别在 inference/attention 前后触发非 layerwise 或 layerwise 的 KV 加载与存储。

released code 中这一设计主要落在 lmcache/integration/vllm/vllm_v1_adapter.pyLMCacheConnectorV1Impl 从 vLLM config 读取 kv_transfer_config.kv_role、tensor parallel size、device,并创建 LMCacheManagerget_num_new_matched_tokens 对 request id 做 lookup/pin 逻辑,处理 preempted request,并支持 producer/consumer 角色差异。

build_connector_meta 遍历 scheduler output,把可加载请求和需要保存的请求转成 LMCacheConnectorMetadatastart_load_kv 从 forward context 初始化 kv_caches 和 attention metadata,按 request 的 load_spec 调用 engine retrieve;save_kv_layer 在 layerwise 模式中把当前 layer 的 paged KV buffer 交给 LMCache;wait_for_save 负责推进 layerwise storer,并在请求结束后 lookup_unpin

3.6 Controller API:把 KV cache 变成可路由、可迁移、可 pin 的对象

LMCache controller 由 centralized controller manager 和 per-instance workers 组成。每个 LMCache instance 在 store/evict 时通过 internal API 更新全局 token pool;外部 router 或 operator 可以调用 controller 查询某段 tokens 在哪些 instance/device 上命中。论文表 3 列出两类接口:internal APIs 包括 batched_admit/batched_evict(hashes, inst_id, device)batched_p2p_lookup(hashes);external APIs 包括 lookup(tokens)move((src_inst_id, src_device), (dst_inst_id, dst_device), tokens)clear(tokens, inst_id, device)pin/unpin(tokens, instance, storage_device)compress/decompress(tokens, instance, device, method)

这些接口的意义在于让上层 orchestration 能直接使用 KV cache 信息。cache-aware routing 可以把请求发到已有最大 hit_chunks 的实例;缩容或负载均衡时,controller 通过 move 让源实例把 KV 迁移到目标实例;P2P sharing 中,本地 miss 后 worker 可以问 controller 哪个 peer 持有 chunks,再从 peer 加载;业务方可以在切换模型或回收资源时 clear 某些 cache;金融文档等热点上下文可以 pin,避免被 eviction。论文特别强调,这些控制接口来源于真实企业需求,而不是为了论文完整性添加的 API。

3.7 代码映射与伪代码

Paper ConceptSource FileKey Class/Function
统一 KV cache engine,负责 store/retrieve/lookup/move/compresslmcache/v1/cache_engine.pyLMCacheEngine.store, retrieve, lookup, move, compress, lookup_unpin
vLLM connector,把 scheduler hit 和 model runner load/store hook 接入 vLLMlmcache/integration/vllm/vllm_v1_adapter.pyLMCacheConnectorV1Impl, get_num_new_matched_tokens, build_connector_meta, start_load_kv, save_kv_layer, wait_for_save
legacy/dynamic vLLM connector wrapperlmcache/integration/vllm/lmcache_connector_v1.pyLMCacheConnectorV1Dynamic.save_kv_layer
SGLang 集成lmcache/integration/sglang/sglang_adapter.pyLMCacheConnector.load_kv, store_kv, LMCacheLayerwiseConnector
多后端 storage / P2P backendlmcache/v1/storage_backend/, lmcache/v1/storage_backend/p2p_backend.pyStorageBackendInterface, P2PBackend.batched_async_contains, batched_get_non_blocking, batched_submit_put_task
异步 lookup/prefetch 文档化路径docs/source/kv_cache/async_loading.rstscheduler lookup、worker-side prefetch、backend async contains/get
后端插件扩展docs/source/developer_guide/extending_lmcache/storage plugin、native connector、remote storage connector
def lmcache_request_path(request, vllm_scheduler, model_runner):
    # Scheduler side: decide how many prefix tokens can be loaded from LMCache.
    matched = connector.get_num_new_matched_tokens(
        request=request,
        num_computed_tokens=request.local_prefix_tokens,
    )
    if matched is None:
        # LMCache may ask vLLM to keep this request waiting so I/O can overlap.
        vllm_scheduler.defer(request)
        return
 
    blocks = vllm_scheduler.allocate_gpu_blocks(request, matched)
    connector.update_state_after_alloc(request, blocks, num_external_blocks=matched)
    metadata = connector.build_connector_meta(vllm_scheduler.output)
 
    # Model runner side: load old KV before attention and store newly produced KV.
    connector.start_load_kv(model_runner.forward_context(metadata))
    for layer_id, layer in enumerate(model_runner.layers):
        connector.wait_load_kv(layer_id)
        layer.compute_attention()
        connector.start_store_kv(layer_id)
        connector.wait_store_kv(layer_id)
    connector.wait_for_save()

这段伪代码对应论文中的 scheduler/model runner API,而不是模型算法。关键点有三个:第一,cache hit token 数会改变 vLLM 的调度和预填充 token 数;第二,load/store hook 与 layer 计算交错,而不是在请求开始和结束时做一次巨大的同步 I/O;第三,保存结束后要 unpin lookup 中锁定的 keys,否则后端 eviction/control API 可能被阻塞。

def cache_engine_store_retrieve(tokens, gpu_pages, storage_manager, gpu_connector):
    # Store path: scattered paged GPU memory -> contiguous buffer/memory objects -> backends.
    chunks = token_processor.chunk(tokens, chunk_size=engine_config.chunk_size)
    memory_objs = gpu_connector.copy_from_paged_gpu_memory(gpu_pages, chunks)
    storage_manager.batched_put(chunks.keys, memory_objs, locations=active_backends)
 
    # Lookup path: return prefix length; optional pin protects objects from eviction.
    hit_chunks = storage_manager.batched_contains(chunks.keys, search_range=None, pin=True)
    hit_tokens = token_processor.prefix_tokens(hit_chunks)
 
    # Retrieve path: backends -> memory objects -> contiguous GPU buffer -> paged GPU memory.
    memory_objs = storage_manager.batched_get(chunks.keys[:hit_chunks])
    gpu_connector.copy_to_paged_gpu_memory(memory_objs, gpu_pages)
    storage_manager.batched_unpin(chunks.keys[:hit_chunks])
    return hit_tokens

这段伪代码对应 LMCacheEngine.store/retrieve/lookup 的核心语义。released code 中 store 要求存在 gpu_connector,并检查 mask 形如 FFFFFTTTTTTT,其中 True 表示需要匹配/存储的 token;retrieve 返回与 tokens 等长的 boolean mask,表示哪些 token 被成功取回;lookup 返回 prefix hit token 数,并可在 pin=True 时把 keys 记录到 lookup_pins 中;movecompress 都先调用 lookup(pin=True),再对命中 memory objects 执行跨位置迁移或 CacheGen 序列化压缩。

3.8 论文描述与 released code 实现差异

论文评测明确使用 LMCache v0.3.6,而本笔记的代码搜索锚定的是 GitHub 当前 dev@f9ce8cbe(2026-05-19)。因此,代码映射用于理解 released code 的当前结构,而不是声称实验曲线完全由该 commit 复现。当前代码已经包含论文未作为主评测对象展开的额外能力,例如 TensorRT-LLM integration、P2PBackend、GDS/NIXL 配置、更多 storage plugin 文档、multiprocess 和 observability 组件。论文公式与 released code 实现差异:本文没有训练 loss 或模型公式需要逐项对齐;可核验的差异主要是版本演进范围,论文中的核心语义(store/retrieve/lookup、vLLM connector、layerwise save/load、pin/unpin、move/compress)在当前代码中仍有对应实现,但实验数值应以论文的 v0.3.6 设置为准。

4. Experimental Setup(实验设置)

论文评测覆盖三个部署场景。第一是 CPU Offload:single-node,没有网络介质,用于评估单机 CPU offloading。第二是 Central Storage:single-node 推理实例连接 Ethernet remote storage server,用于评估集中式远程存储。第三是 PD:single-node 视角下的 prefiller/decoder 通过 NVLink 连接,用于评估 prefill–decode disaggregation。论文曾在表格中保留 P2P 多节点场景的注释,但正式评测集中在上述三类。

模型包括多种工业常见 open models:meta-llama/Llama-3.1-8B-InstructSao10K-L3-8Bmeta-llama/Llama-3.1-70B-InstructQwen/Qwen2.5-Coder-32B-InstructQwen/Qwen3-Coder-480B-A35B-Instruct-FP8Qwen/Qwen2.5-72B-Instruct。数据集包括 emulated multi-round question answering、LongBench 中的 long-context QA,以及 vLLM 官方 benchmark script 生成的 random dataset。硬件方面,single-node evaluation 使用 GMI Cloud 提供的 8×H100 server;不同模型按照能启动的最小 H100 数量分配 GPU。multi-node/central storage 使用与 single-node 相同 GPU 数,remote storage backend 用 CPU memory 存 KV。PD disaggregation 中 prefiller 和 decoder 都配置成与 single-node 相同 GPU 数,并通过 NVLink 连接。

指标主要是 TTFT(time-to-first-token,prefill delay)和 ITL(inter-token latency,连续输出 token 之间的平均延迟)。component-wise analysis 还单独拆分 CPU offloading 或 PD disaggregation 的传输、prefill、decode 等组成。Baselines 包括 basic vLLM v0.10.2(默认开启 GPU 内 prefix caching,但只保留 GPU memory 中的 KV)、basic vLLM CPU Offloading v0.11.0、若干 commercial inference APIs,以及 vLLM native PD disaggregation。论文评测的 LMCache 版本是 v0.3.6,这一点与本笔记代码锚点不同,阅读实验数值时应避免把当前 dev 分支新增功能误认为实验设置的一部分。

CPU Offload workload 模拟多轮文档问答:默认每个 LLM query 有 10K tokens,包含约 12 页 PDF 的 document context 和一个 unique short question;Llama-3.1-8B-Instruct 的输入设置为 20K tokens;输出最多 100 tokens。chat session 从 40 个 users 开始,随后按指定 arrival rate/QPS 加入新用户。LMCache 可用于 offload KV cache 的 CPU memory 上限设为 500 GB。

Central Storage 实验通过 15 Gbps 带宽连接 GPU instance 与 centralized remote server,使用 LongBench 的 TriviaQA 数据,并遵循 vLLM 官方 benchmark script,让请求按 Poisson distribution 到达。PD disaggregation 实验使用 official benchmarking script 的 random input/output workload,输入 8K tokens,输出 200 tokens。Sensitivity study 评估不同 context lengths 和网络带宽下 loading delay 与 naive prefill delay 的交叉点;SGLang 结果使用 Qwen3-32B,在两张 H100 GPU 上 TP=2,并开启 LMCache CPU offloading。

5. Experimental Results(实验结果)

Figure 8 解读:单机 CPU offloading 是论文最主要的结果图之一。与 basic vLLM、basic vLLM CPU offloading 和两个 commercial alternatives 相比,LMCache 在低 QPS(例如 QPS=1)下 TTFT 小 1.9–8.1 倍;在相同 TTFT 下,query processing rate/throughput 比最强 baseline 高 2.3–14 倍;ITL 相对最佳 baseline 低 7%–92%。论文解释这些收益来自两个因素:CPU memory 可以容纳远多于 GPU memory 的 KV,使 cache hit ratio 更高;LMCache 的 chunk-level transfer 和 CUDA kernel 比 vLLM native CPU offloading 的 per-layer/per-16-token 传输更能利用带宽。

Figure 9 解读:这张图使用 Company F 的真实 trace,在多个模型上比较 LMCache 与 basic vLLM。论文报告在高 QPS 下,LMCache 至少把 TTFT 降低 3.7–6.8 倍,并把 ITL 降低 19%–58%。真实 trace 结果重要,因为它说明收益不只来自人为构造的重复 prompt;真实生产流量中也存在足够多可复用前缀和动态上下文模式。

Figure 10 解读:这张图补充了更多模型和 trace 结果。它的作用是证明 LMCache 的优势不是某一个小模型或某一个上下文长度的偶然现象,而是在 Qwen2.5-Coder-32B、Sao10K-L3-8B 等不同模型和负载下仍然成立。读图时应关注趋势而不是单个点:LMCache 曲线通常在同等 QPS 下有更低 TTFT/ITL,或在同等 latency 下支持更高 QPS。

Figure 11 解读:centralized remote backend 实验显示,在 15 Gbps remote server 设置下,LMCache with remote backend offloading 在相同 TTFT 下比 basic vLLM 有 1.3–3 倍 throughput 提升。论文特别提醒,remote backend 比 CPU memory latency 更高,因此短输入或低带宽时不一定总是更好;它的价值在于容量更大、cache hit ratio 更高,适合存放 GPU/CPU 放不下但仍会复用的上下文。

Figure 12 解读:PD disaggregation 实验比较 LMCache 与 vLLM native PD。输入 8K tokens、输出 200 tokens 时,LMCache 在 tail latency 和 mean latency 上都更好。论文给出的具体结论是:LMCache 将 mean TTFT 降低 1.53–1.84 倍,将 mean ITL 降低 1.12–1.66 倍。原因不是 prefill 或 decode compute 更快,而是 KV transfer 更有效率:LMCache 把 chunked prefill 生成的 KV 先复制到 prefiller GPU buffer,再传到 decoder GPU buffer,避免 vLLM native PD 在 scattered paged memory 上逐 page 调用 NIXL copy 导致带宽不足。

Figure 14 解读:component-wise breakdown 表明,LMCache 与 vLLM native PD 的 prefill/decode compute 时间相同,差异集中在 KV cache transmission。论文表 5 进一步给出 CPU loading bandwidth:LMCache 约 400 Gbps,vLLM native CPU offloading 约 88 Gbps。这个数值支撑了前文 chunk-level transfer 的设计动机:同样是搬 KV,粒度不同会导致有效带宽相差数倍。

Figure 13 解读:request asynchronization 的时间线展示了计算与加载的重叠。不开启时,prefill/decode computation 和 KV loading 基本串行;开启后,排队期间即可开始加载,实际执行时只需等待更少 I/O。论文报告该优化把 end-to-end delay 降低 1.46 倍。这个结果说明 LMCache 的收益不只来自更快 copy kernel,也来自把 I/O 调度到更合适的时间窗口。

Figure 15 解读:sensitivity study 展示不同 context length 和网络带宽下,KV loading 与 naive prefilling 的交叉点。在 32 Gbps 低带宽下,LMCache loading 只有当输入超过 256K tokens 时才比 naive prefill 更快;在 64 或 128 Gbps 下,loading 在所有测试 context lengths 上都低于 naive prefill。这说明生产系统不应盲目开启 remote KV loading,而应根据 bandwidth、context length、cache hit ratio 和 SLO 自适应决策。

Figure 16 解读:SGLang 结果证明 LMCache 不只适配 vLLM。Qwen3-32B 在 2×H100、TP=2 设置下,LMCache CPU offloading 相比无 CPU offloading 的 SGLang 有更高 throughput、更低 mean TTFT 和 mean end-to-end latency;相比 SGLang native CPU offloading,性能接近。论文强调,SGLang native CPU offloading 虽然在该单机实验中接近,但缺少 LMCache 这种可跨 local disk、remote CPU/disk、distributed backend 的 hierarchical storage layer。

真实部署经验给出几条额外结论。第一,remote storage loading 在新硬件和对象存储性能提升后可能比 full prefill 更快:论文提到 S3 Express 级别吞吐从历史上约 100 MBps 提升到接近 1 GBps,Company C 使用自己的 remote object store 加载 KV 时,相比 full prefill 降低 22%–32% TTFT。第二,context truncation 会显著伤害 prefix cache hit ratio:Company F 的真实 trace 中,如果只保留最新 tokens,命中率大约从 85% 降到 45%。这对长上下文系统很重要,因为滑动窗口虽然解决 context length/GPU memory 限制,却会破坏前缀一致性,让可复用 KV 失效。第三,生产用户偏好 containerized deployment;很多用户只使用官方 Docker image 而不深入源码,因此 LMCache 的工程稳定性、配置兼容和镜像分发比论文原型中常见的“可改 attention 机制”更关键。第四,用户在生产中发现 prefix cache hit rate 比预期更高,例如某公司达到约 50%,因为现代应用有大量 dynamically reusable contexts,而不只是固定 system prompt。

总体来看,LMCache 的实验结论可以概括为:当 workload 有明显前缀复用或需要 PD disaggregation 时,把 KV cache 从 GPU-only 结构升级为跨 tier 可管理对象能带来数量级级别吞吐/延迟收益;但收益依赖上下文长度、网络带宽、cache hit ratio、backend latency 和请求调度。论文最强的地方是把系统机制、真实 trace 和 released code 结合起来;主要 caveat 是评测版本为 v0.3.6,而当前 GitHub dev 已经继续演进,复现实验时必须锁定论文版本、配置和硬件,而不能只用最新代码默认参数。