About us

Categories

Tags

Follow us on: X, LinkedIn

Initiated and Officially Supported by Tensormesh

当开源遇见开源:LMCache 与 Mooncake 的一次双向奔赴

By

Baolong

and

LMCache Team

一段关于 lmcache multiprocess mode + MooncakeStore 的协作故事 —— 从 0 到 1,从能用到好用。


一、写在前面

最近,LMCache 社区和Mooncake 社区围绕 LMCache MP (multiprocess) 模式下的 Mooncake Store L2 adapter 做了一系列相当有价值的开源合作。

主要参与的小伙伴有:

  • maobaolong(LMCache 社区维护者):从 0 到 1,初版打通 LMCache(MP mode) -> MooncakeStore。
  • fangchizheng(fcczzz)(来自 Mooncake 开源社区):贡献了 MooncakeStore RDMA 预注册、batch 操作支持、per-op worker pools 等一连串关键优化。
  • chunxiaozheng(LMCache 社区维护者):在每一次 PR 中提出关键 review意见,把控代码质量。

这不是某个公司单方面的产品交付,而是两个开源社区之间真正意义上的”联合开发”—— 一边出框架与场景,一边出底层引擎与性能优化。本文就来回顾这段合作,并介绍一下最终落地的技术成果。


二、先认识一下两位主角

2.1 LMCache 是什么?

LMCache 是 vLLM 生态里专注于 KV Cache 复用与持久化 的一层中间件。 它的核心目标是:把 LLM 推理过程中的 KV Cache “沉淀” 下来, 让相同前缀的 prompt 不必重复计算 prefill,直接复用历史 KV, 显著降低 TTFT(Time-To-First-Token)和算力消耗。

LMCache 提供两层存储:

  • L1:CPU 共享内存中的 KV Cache 池(高速、易失)。
  • L2:可持久化的二级存储(NVMe / 文件系统 / 分布式 KV 存储 / 对象存储等),由各种 L2 Adapter 接入。

2.2 Mooncake Store 是什么?

Mooncake 是一个面向分布式大模型推理场景的开源infra。其中,Mooncake store 是分布式 KV 缓存存储引擎,原生支持 RDMA、零拷贝传输、多副本元数据管理,定位是为 “PD 分离 / KV-centric 推理架构” 提供高性能存储底座。

简单一句话总结两者的定位:

项目定位
LMCacheKV Cache 的 管理者与调度器(决定谁存、谁取、放哪一层)
Mooncake StoreKV Cache 的 高性能远端存储引擎(决定如何高速、可靠地存取)

三、LMCache MP Mode 整体架构

在介绍这次合作的具体内容之前,先简单看一下 LMCache MP 模式的整体形态。

MP(multiprocess)模式:LMCache 不再以”库”的形式被 vLLM in-process 加载,而是作为一个独立进程通过 RPC(ZMQ)与 vLLM worker 通信,把 KV Cache 的管理彻底独立出来。这样做带来三大好处:

1. 进程隔离:LMCache 故障不会影响 vLLM 主进程。

2. 资源解耦:可以独立分配 CPU、内存、I/O 资源给 LMCache。

3. 跨实例共享:多个 vLLM 实例可以共用同一个 LMCache 服务, 形成机器级甚至集群级的 KV Cache 池。

3.1 架构图

Diagram depicting the overall architecture of LMCache in MP mode, showcasing the vLLM worker processes, LMCache MP server, and data and control paths within a structured layout.

几个核心点:

  1. L1 Manager 维护一个 CPU 共享内存池,所有 vLLM worker 通过 RPC读写同一份 L1。
  2. StoreController 在后台异步把 L1 中新产生的 KV chunk 复制到 L2。
  3. PrefetchController 在 LOOKUP 命中 L2 时,异步把数据从 L2 拉回 L1,再让 vLLM 通过 RETRIEVE RPC 拿走。
  4. L2 Adapter 层是完全插件化的:每一种存储后端都是一个独立的 adapter,通过统一的 L2AdapterInterface 接入。本文的主角 mooncake_store adapter 就是这一层的一个新成员。

四、合作之前:LMCache 已经铺好的路

这次 LMCache × Mooncake 合作能这么顺畅,离不开 LMCache 社区在前期搭好的基础设施。简单说就是 三块拼图,缺一不可。

4.1 拼图一:Native Connector 框架(#2642

*[Southbound]: Create a Native Protocol for MP and non-MP* —— by Samuel Shen (TensorMesh)

LMCache 早期只有 Redis 一个 C++ connector,代码混在 csrc/redis/ 里、 和 RESP 协议强耦合。这次 PR 做了一次彻底的抽象重构

  • csrc/redis 提升为通用的 csrc/storage_backends/
  • 抽出了 IStorageConnector 接口和 ConnectorBase<T> CRTP 模板。
  • 提供了 connector_pybind_utils.h 一组 pybind 宏,让新后端只需写几十行就能完成 Python 绑定。
  • 在 Python 侧引入了 NativeConnectorL2Adapter,作为”任何 native connector”到”L2AdapterInterface”的通用桥接器。
  • 同时支持 MP 和非 MP 模式 —— 一份 C++ 代码,两个场景复用。

简单说,LMCache 给所有未来的 native 后端留好了一个标准的插槽

4.2 拼图二:第一个 Native FS Connector(#2779

*Introduce native fs connector* —— by maobaolong

Native 框架抽象出来后,第一个适配的 L2 Native Adapter —— 本地文件系统

  • csrc/storage_backends/fs/ 下实现了一个 C++ 文件系统 connector。
  • 它既是个能用的功能(在没有 NIXL/Mooncake 等依赖的环境里,FS adapter

足以跑通整个 L2 链路),也是一个最小工作示例:后人想接入新后端,可以提供参考。此外,一些分布式文件系统,都能用 nfs 或 mount fs 方式配合这个 fs adapter 接入 lmcache。

4.3 拼图三:动态加载第三方 Native Connector(#2851

*[MP] Refactor l2 plugin framework to support dynamic load third-party native l2 connector* —— by maobaolong

最后一块拼图,也是最关键的一块:让第三方 connector 不必合入 LMCache 主仓也能被加载

这个 PR 做了:

  • 把 L2 plugin 框架改造成基于 registry + 动态发现 的模式。
  • 提供了一个完整的”外部插件包”模板(lmc_external_native_connector/),

外部用户可以把自家的 C++ 后端打成独立的 pip 包,安装后被 LMCache 自动发现并加载。

  • 配套的设计文档 docs/design/l2_adapters/plugin.md 也一并完善。

在这次重构之前,添加一个新后端要在工厂函数里改 if/elif 链、改注册表、改 import。重构之后是真正的”零修改扩展”—— 这才是开源插件该有的样子。

至此,”地基”全部铺好。下一步就是请重量级嘉宾上场了。

五、合作正片:MooncakeStore L2 Adapter 从 0 到 1

5.1 第一步:把 Mooncake 接进来(#2911

*[MP] Introduce l2 mooncake adapter* —— by maobaolong, 2026-04-03

这是合作的第一发子弹。基于上一节铺好的 native 框架,新增了 `csrc/storage_backends/mooncake/`:

  • C++ 侧:connector.cpp / connector.h / pybind.cpp 实现了对Mooncake 客户端的封装,整个文件加起来才 140 行左右。
  • Python 侧:`mooncake_store_l2_adapter.py`

提供了 MooncakeStoreL2AdapterConfig 和工厂函数。

关键设计亮点 —— 配置透传:除了 LMCache 自己关心的几个配置项(如 num_workers),所有 Mooncake 自家的配置(local_hostname, master_server_addr, protocol, rdma_devices, …)都被作为整体字典 透传给 mooncake sdk 侧的setup_internal(ConfigDict)。这样 Mooncake 后续增删配置项时, LMCache 这边一行代码都不用改。

这个设计哲学非常重要:不替第三方做翻译,只做透传 这是开源协作里最难得的”边界感”——不感知具体底层,让对方保留演进自由。

落地效果:跑通了 mp mode + Mooncake Store (TCP) 的完整 store/lookup/load 链路。第一版还不快,但它通了

5.2 第二步:Mooncake 社区的回礼 —— RDMA 预注册(#3018

*[Feat] Add RDMA L1 memory preregistration support for MooncakeStore L2 adapter* —— by Chizheng Fang, 2026-04-23

第一版接进来之后,Mooncake 社区的 fcczzz出手了。 他抓住了一个核心问题:Mooncake RDMA 模式下,LMCache 的 L1 内存必须预先注册给 RDMA 网卡,否则每次 I/O 都会触发 page pinning,性能损失巨大。

这一发 PR 做了:

  • 在 C++ MooncakeConnector 里加上了 preregister_l1_memory 路径。
  • 让 LMCache 的 L1 共享内存指针在 adapter 创建时就被一次性注册到Mooncake 的 transfer engine。
  • 同时清理掉了第一版里多余的 owned_real_client_ 复杂逻辑,简化为单client 实例。
  • preregister_l1_memory 提升为 MooncakeStoreL2AdapterConfig 的独立开关,并补全测试。

这次 PR 的微妙之处在于:fcczzz 不只是”调用方报个 bug”,他直接读懂了 LMCache 的 L1 Manager、写出了改动 LMCache C++ 层和 Python 层的 patch, 还顺手优化了一版 native connector 的代码组织。这就是开源协作里最有营养的状态——不分彼此,大家都是社区的主人

5.3 第三步:Batch 操作(#3172

*[MP]: Add batch operations to Mooncake L2 adapter* —— by Chizheng Fang, 2026-05-07

RDMA 通了,下一步是榨性能。fcczzz 又来了一发:

  • MooncakeConnector 里实现了 批量 store / lookup / delete

把原来的循环 single-op 升级成一次性下发的 batch 调用。

  • 修复了一个边界 case:Mooncake 的 “key exists” 错误应当被当作 miss 处理,

而不是抛出异常。

  • 把 RDMA 集成测试隔离开来,避免和默认 TCP adapter 抢同一段 segment。
  • 补齐了 batch_delete 测试,覆盖 mixed existing/missing 的场景。

为什么 batch 重要?因为 LMCache 一次 store 经常涉及成百上千个 KV chunk (每个 chunk 对应一个 token block),逐个走 RPC 的延迟是不可接受的。 batch 之后,吞吐量直接上一个台阶。

5.4 第四步:Per-Op Worker Pools(#3227

*[MP]: Add per-operation dedicated worker pools to Mooncake L2 connector* —— by Chizheng Fang

这个改进解决的场景问题:

在共享 worker pool 下,一个 store 突发就能把 lookup 拖垮。

为什么?因为 lookup/retrieve/store/delete 这四种操作的延迟完全不同——lookup 极快但极敏感,store 慢但可以容忍。把它们塞进同一个线程池,store 突发期到来时,那些只想做一次”几十微秒”的 lookup 必须排队 在几十毫秒级的 store 后面,p99 直接爆炸。

#3227 的解法:

  • ConnectorBase 里引入 WorkerPoolConfig,把 lookup / retrieve

/ store / delete 四条 lane 各自分配独立的 worker。

  • 请求入队时按 op 类型路由到对应 lane;未配置的 lane fallback 到共享池。
  • Python 配置里暴露 per_op_workers: dict[str, int],并做完整校验。
  • 这个 lane 机制做在 ConnectorBase 上,所有 native connector 都能受益

——其他后端只要在 pybind 里接住 WorkerPoolConfig 就行。

实测数据(Mooncake RDMA, 1024 keys, 1 MiB values, 4 stores/round):单位均是ms

配置Lookup avgLookup p99Load avgLoad p99Store avgStore p99
shared 4 workers0.94716.8323.39941.1655.29251.390
per-op (1/1/2)0.2660.4832.5903.5063.21667.434
  • Lookup p99 下降 ~35×(16.8 ms → 0.48 ms)
  • Load p99 下降 ~12×(41.2 ms → 3.5 ms)

这不是”调参调出来的”性能,是架构性的红利——隔离不同 SLO 的负载, 本来就是高性能存储栈里最朴素也最有效的设计。

六、把这条时间线铺平来看

A timeline chart illustrating the collaboration between LMCache and Mooncake, featuring various components and their corresponding dates, like Native Connector, Native FS Connector, and MooncakeStore L2 adapter.

短短一个多月,从”能跑”到”跑得快”,再到”跑得稳”。每一步都不是某一方单独完成的——LMCache 出框架与 review,Mooncake 社区出底层与优化思路, 中间还有 chunxiaozheng 这样耐心的 reviewer 在每一次 PR 里把质量盯下来。

七、聊聊这次合作教给我们的事

7.1 好的”边界感”是开源合作的前提

LMCache 没有把 Mooncake 的配置一项一项翻译成自家的 dataclass, 而是选择透传 Dict[str, str]。这看起来”偷懒”,本质上是 给协作伙伴留够演进空间。开源合作里,边界划得越清楚,越省心。

7.2 插件化是社区可持续协作的基石

如果没有事先的 native connector 框架重构 + 动态加载机制, Mooncake 这次合作的代价会高得多——可能要往 LMCache 主仓里塞一堆 if/elif、修改一堆工厂函数、忍受一个又一个的 merge conflict。 Zero-Modification Extension 是开源协作的”减摩擦剂”, 谁先把它做好,谁就更容易被合作。

7.3 WinWin,是开源最朴素的逻辑

  • LMCache 来说:集成了一个生产级的 RDMA 分布式 L2 后端,

以及一连串可以受用于所有 native connector 的优化(per-op pools、batch、 L1 预注册思路)。

  • Mooncake 来说:打通了一个面向 vLLM 用户的导流入口,让它的 RDMA 能力真正被 LLM 推理用户用上。
  • 用户 来说:一个开箱即用的最简实践 lmcache mp + mooncakestore 方案,简单且高性能。

开源的意义把自己擅长的东西做好, 再把它免费送出去,最后自己得到的反而更多。

八、给读到这里的小伙伴们的一点点请求

如果你在做:

  • 大模型推理服务
  • 想搭建跨实例共享 KV Cache 的推理集群
  • 对 RDMA / 分布式 KV 存储有性能要求
  • 或者只是想看看一个 well-designed 的开源插件框架长什么样

强烈建议你试试 `LMCache MP Mode + Mooncake Store` 这套组合

# 1. 编译时启用 Mooncake 扩展
BUILD_MOONCAKE=1 pip install -e . --verbose
# 2. 启动 LMCache 服务
lmcache server --l1-size-gb 100 --eviction-policy LRU \
--l2-adapter '{
"type": "mooncake_store",
"num_workers": 4,
"per_op_workers": {"lookup": 1, "retrieve": 1, "store": 2},
"preregister_l1_memory": true,
"local_hostname": "node01",
"metadata_server": "http://localhost:8080/metadata",
"master_server_addr": "localhost:50051",
"protocol": "rdma",
"rdma_devices": "mlx5_0",
"global_segment_size": "3221225472"
}'

具体的 benchmark 数据、配置说明、文档链接都在 #3227docs/source/mp/l2_storage.rst 里。

也欢迎你给两个社区都点个 star、提个 issue、发个 PR—— 开源协作没有围墙,每一个 contributor 都让这条路更宽一点。

发表评论

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理

了解 LMCache Blog 的更多信息

立即订阅以继续阅读并访问完整档案。

继续阅读