
LMCache P2P KV Cache Sharing基于 RDMA 的多节点 KV 缓存共享部署指南【免费下载链接】LMCacheLMCache: Supercharge Your LLM with the Fastest KV Cache Layer项目地址: https://gitcode.com/GitHub_Trending/lm/LMCache导读在多节点推理集群中每个节点运行自己的 LMCache server只缓存自己服务过的请求的 KV。P2P KV Cache Sharing多进程模式下的点对点 KV 缓存共享把这些分散的节点级缓存整合成一个逻辑缓存当某个节点查找本地不存在的前缀时它通过数据中心网络RDMA直接从持有该前缀的对等节点内存中读取 KV而不是重新计算前缀。本指南基于仓库中的 examples/p2p/README.md 与 docs/source/mp/p2p.rst完整讲解 P2P 的工作原理、全部配置参数、单机测试与多节点部署步骤、日志解读与验证方法并深入对应的源码实现帮助你在真实集群上落地这一能力。背景为什么需要 P2P KV 缓存共享在没有共享机制时一个前缀在某节点上计算后当相同前缀到达另一个节点时只能从头重新计算recompute跨节点的前缀复用率很低。P2P KV cache sharing 解决了这个问题当一个节点查找本地没有的前缀时直接通过 RDMA 从持有该前缀的对等节点内存中读取 KV。这是一次单边 RDMA 读one-sided RDMA read——从请求节点读取到它自己的 L1 缓冲区数据持有方无需被打断来提供服务。在支持 RDMA 的网络InfiniBand / RoCE上这比重新计算前缀或往返共享对象存储要快得多最终效果是整个集群的有效缓存命中率大幅提升且热路径上没有集中式存储层。工作原理三个组成部分P2P 由三个部分组成见 docs/source/mp/p2p.rst 与 lmcache/v1/multiprocess/modules/p2p_controller.pyCoordinator协调者——每个部署一个小型 HTTP 服务lmcache coordinator跟踪哪些 LMCache server 存活。每个 server 向它注册并发送心跳coordinator 回答我有哪些存活的对等节点这类查询。它只管理成员关系从不接触 KV 数据也不参与查找。其成员接口实现在 lmcache/v1/mp_coordinator/http_apis/instances_api.pyPOST /instances注册、PUT /instances/{id}/heartbeat心跳、DELETE /instances/{id}注销、GET /instances列出。LMCache server——每个 server 运行一个 P2P controller周期性向 coordinator 查询当前对等节点列表并为每个存活对等节点建立一个连接L2 adapter用于查找并读取该对等节点的 KV。对等节点自动发现、自动连接、自动断开无需静态节点列表。vLLM——只与本地LMCache server 通信通过LMCacheMPConnector。P2P 拉取发生在 LMCache server 内部对 vLLM 完全透明。完整的读路径为缓存未命中时节点向持有该前缀的对等节点发起lock and locate锁定并定位请求拿到远程地址后通过传输通道transfer channel把 KV RDMA 读到自己的 L1再从这个 L1 服务请求。前置要求Requirements部署 P2P 需要满足以下条件每台节点都安装完整版 LMCache含 CUDA与 vLLM。一个所有节点可达的 coordinator URL——没有--coordinator-url时 P2P 拒绝启动。**支持 RDMA 的网络InfiniBand / RoCE**用于生产环境性能。默认 P2P 使用nixl传输引擎它是可选扩展lmcache[nixl]启用 P2P 前需要先安装uv pip install lmcache[nixl]或pip install lmcache[nixl]。单一、连续的 L1 区域——P2P 与 GDS L1 tier--gds-l1-path和 Device-DAX L1 tier--l1-devdax-path不兼容在这些配置下 server 会拒绝启动。推荐在 P2P server 上设置--l1-align-bytes 6553664 KB以获得更大、对齐更好的 RDMA 读取。第 4 点的拒绝启动有明确的源码校验依据lmcache/v1/multiprocess/http_server.py 在启动时检查P2P requires a coordinator for peer discovery与P2P requires a single L1 memory region ... incompatible with GDS L1 (--gds-l1-path) and Device-DAX L1 (--l1-devdax-path)。P2P 配置参数一览P2P 由lmcache server的--p2p-advertise-url参数按 server 启用。相关参数及其默认值如下源自 docs/source/mp/p2p.rst 的官方参数表命令行解析实现见 lmcache/v1/multiprocess/config.py参数说明默认值--p2p-advertise-url HOST:PORT该 server 向对等节点公布的传输通道端点。设置即启用 P2P必须是其他节点可达的地址空禁用 P2P--p2p-listen-url HOST:PORT传输通道 server 绑定的地址。缺省时与--p2p-advertise-url相同多网卡节点常绑定0.0.0.0同时公布可路由 IP同 advertise-url--p2p-lookup-timeout SECONDS对等节点查找结果的截止时间超时视为 miss30--p2p-load-timeout SECONDS对等节点 KV 读取的截止时间超时视为失败30--p2p-transfer-engine ENGINE传输通道实现nixl--coordinator-url URLcoordinator 地址P2P 启动前提空禁用注册--coordinator-advertise-ip IP对等节点访问本节点控制平面的地址由 registrar 解析出口 IP--coordinator-heartbeat-interval SECONDS心跳间隔同时作为对等节点发现的轮询间隔5.0上述默认值在 lmcache/v1/multiprocess/config.py 的P2PConfig与CoordinatorConfig中有完整定义lookup_timeout 30.0、load_timeout 30.0、transfer_engine nixl、heartbeat_interval 5.0enabled属性即advertise URL 非空。关于 L1 对齐的实用提示文档特别建议 P2P 参与者将 L1 缓冲对齐至少提高到 64 KB--l1-align-bytes 65536。更大的对齐让传输通道能发起更大、更对齐的 RDMA 读取明显改善传输性能默认的 4 KB 对齐对非 P2P 部署足够但对 P2P 建议调高。传输引擎后端Transfer Engine Backends传输引擎是执行远程内存读取的组件通过--p2p-transfer-engine按 server 选择引擎说明nixl默认基于 RDMA 的传输随可选扩展lmcache[nixl]提供uv pip install lmcache[nixl]运行在 InfiniBand / RoCE 网络上mooncake_te使用 Mooncake Transfer Engine 进行 P2P 传输。默认走 TCP可通过环境变量启用 RDMAMooncake Transfer Engine 的 RDMA 配置选择--p2p-transfer-engine mooncake_te后默认使用 TCP要使用 RDMA必须在每个 server 启动时同时设置MC_TE_PROTOCOLrdma和MC_TE_DEVICERDMA 设备名MC_TE_PROTOCOLrdma MC_TE_DEVICEmlx5_0 \ lmcache server \ argument-list \ --p2p-transfer-engine mooncake_te将mlx5_0替换为节点上实际的 RDMA 设备名。两个变量必须同时设置才会走 RDMA缺省时 Mooncake Transfer Engine 使用 TCP。单机部署测试与调试在一台双 GPU 机器上通过localhost运行两个 LMCache server、两个 vLLM server 和一个 coordinator即可在无真实网络的情况下跑通完整的 P2P 路径这是官方推荐的开发与调试方式。打开五个终端# Terminal 1 — coordinator lmcache coordinator --host 0.0.0.0 --port 9300 # Terminal 2 — node A: LMCache server lmcache server \ --host 127.0.0.1 --port 6555 --http-port 7555 \ --l1-size-gb 50 --eviction-policy LRU \ --l1-align-bytes 65536 \ --instance-id node-a \ --coordinator-url http://127.0.0.1:9300 \ --coordinator-advertise-ip 127.0.0.1 \ --p2p-advertise-url 127.0.0.1:8555 # Terminal 3 — node A: vLLM on GPU 0, connector - local LMCache (port 6555) CUDA_VISIBLE_DEVICES0 vllm serve Qwen/Qwen3-14B --port 8000 \ --kv-transfer-config {kv_connector:LMCacheMPConnector,kv_role:kv_both,kv_load_failure_policy:recompute,kv_connector_extra_config:{lmcache.mp.port:6555}} # Terminal 4 — node B: LMCache server lmcache server \ --host 127.0.0.1 --port 6556 --http-port 7556 \ --l1-size-gb 50 --eviction-policy LRU \ --l1-align-bytes 65536 \ --instance-id node-b \ --coordinator-url http://127.0.0.1:9300 \ --coordinator-advertise-ip 127.0.0.1 \ --p2p-advertise-url 127.0.0.1:8556 # Terminal 5 — node B: vLLM on GPU 1, connector - local LMCache (port 6556) CUDA_VISIBLE_DEVICES1 vllm serve Qwen/Qwen3-14B --port 8001 \ --kv-transfer-config {kv_connector:LMCacheMPConnector,kv_role:kv_both,kv_load_failure_policy:recompute,kv_connector_extra_config:{lmcache.mp.port:6556}}两个 LMCache server 必须在每个端口上不同ZMQ--port、HTTP--http-port以及 P2P 传输端点--p2p-advertise-url。同时给每个 server 一个不同的--instance-id便于区分。注意单机上localhost流量走 loopback/TCP 路径而非 RDMA因此延迟不代表真实 RDMA 网络的性能。单机模式用于功能性测试——性能基准请在真实多节点 RDMA 部署上运行。测试它# 1. 填充 node A 的缓存冷启动——预期 LMCache 命中约 0 python send_request.py --port 8000 # 2. 向 node B 发送相同的 prompt。B 从未服务过它且自身缓存为空 # 所以 B 上任何 LMCache 命中都必然是通过 P2P 从 A 读取的。 python send_request.py --port 8001第二次调用应打印非零的num_lmcache_cached_tokens。测试脚本 examples/p2p/send_request.py 的工作原理构造一个足够长跨越多个默认 256 token 的 LMCache chunk的 prompt通过 OpenAI 兼容的/v1/chat/completions接口发送并在请求体里带上kv_transfer_params: {cached_token_stats: True}以开启逐请求的缓存命中统计最后打印响应中的cached_token_stats其中num_lmcache_cached_tokens即本次请求中命中 LMCache 缓存的 token 数。多节点部署在所有节点可达的主机上启动 coordinator这里为10.0.0.1然后每个节点运行一个 LMCache server 一个 vLLM。添加更多节点只是重复每节点这一组命令全部指向同一个 coordinator。# Coordinator 主机 (10.0.0.1) lmcache coordinator --host 0.0.0.0 --port 9300在每个节点上将NODE_IP设置为该节点的可路由地址并运行NODE_IP10.0.0.2 # 本节点地址每个节点不同 COORDINATOR10.0.0.1 # UCX 的 RDMA 调优根据你的网络调整 transports/rails。 export UCX_TLSrc,sm,self export UCX_MAX_RMA_RAILS8 # 1. 启用 P2P 的 LMCache server。绑定 0.0.0.0公布 NODE_IP。 lmcache server \ --host 0.0.0.0 --port 6555 --http-port 7555 \ --l1-size-gb 100 --eviction-policy LRU \ --l1-align-bytes 65536 \ --instance-id lmcache-${NODE_IP} \ --coordinator-url http://${COORDINATOR}:9300 \ --coordinator-advertise-ip ${NODE_IP} \ --p2p-advertise-url ${NODE_IP}:8555 \ --p2p-listen-url 0.0.0.0:8555 # 2. vLLMconnector 指向本地 LMCache server端口 6555。 vllm serve Qwen/Qwen3-14B --port 8000 \ --kv-transfer-config {kv_connector:LMCacheMPConnector,kv_role:kv_both,kv_load_failure_policy:recompute,kv_connector_extra_config:{lmcache.mp.port:6555}}--coordinator-advertise-ip是对等节点访问本节点控制平面的地址--p2p-advertise-url是它的 RDMA 传输端点。当节点有多个网卡时通常的模式是绑定0.0.0.0通过--p2p-listen-url同时公布NODE_IP。两个节点都起来后向一个节点发送长 prompt再向另一个节点发送相同 prompt——第二个节点通过 RDMA 从第一个节点服务它python send_request.py --host 10.0.0.2 --port 8000 python send_request.py --host 10.0.0.3 --port 8000预期日志Coordinator会记录每个实例的注册Registered instance node-a at 127.0.0.1:7555 Registered instance node-b at 127.0.0.1:7556每个 LMCache server在启动以及发现对等节点时INFO 级别Started PeriodicThread: p2p-controller-thread (levelmedium, interval5.0s, init_wait0.0s) Registered with coordinator as node-a Added L2 adapter 0 (p2p) # 发现对等节点时创建设置LMCACHE_LOG_LEVELDEBUG后P2P controller 还会给出对等节点的名字Added P2P adapter 0 for peer node-b (127.0.0.1:8556)当对等节点离开或连续约 3 次发现轮询未出现时会看到 adapter 被拆除Deleted L2 adapter 0 Removed P2P adapter for peer node-b # DEBUG日志中interval5.0s对应的正是 coordinator 心跳/发现轮询周期--coordinator-heartbeat-interval默认 5 秒。连续约 3 次未出现才拆除也有源码依据lmcache/v1/multiprocess/modules/p2p_controller.py 定义_MAX_MISSES 3_reconcile中每轮对缺失的 peer 累加consecutive_misses超过 3 次才调用_remove_adapter。验证 P2P 正在工作查询某个 server 的状态端点查看其 P2P 状态与已连接的对等节点curl -s http://127.0.0.1:7555/status | python3 -m json.tool # 查找: p2p_state: registered, p2p_peer_count: 1, p2p_peers: [node-b]从 coordinator 列出整个集群curl -s http://127.0.0.1:9300/instances | python3 -m json.tool一次成功的 P2P 读取会表现为在从未服务过该 prompt 的节点上send_request.py打印出非零的num_lmcache_cached_tokens见上文测试步骤。这些状态字段由 P2P controller 的report_status()方法生成lmcache/v1/multiprocess/modules/p2p_controller.py包括p2p_enabled、p2p_stateregistered/disconnected/unregistered见_P2PState枚举、p2p_peer_count与p2p_peers按 instance-id 排序的已连接对等节点列表以及active_p2p_lookup_jobs活动查找任务数。这些指标同时以 OTel gaugelmcache_mp.active_p2p_lookup_jobs暴露。源码视角P2P 的控制与数据面从 lmcache/v1/multiprocess/modules/p2p_controller.py 可以清楚看到 P2P 的两个面控制面发现与生命周期P2PController启动时调用initialize_transfer_channel_context注册 L1 内存描述供对等节点 RDMA 读取并启动名为p2p-controller-thread的周期线程按 heartbeat 间隔轮询 coordinator 的/instances。_poll_cycle每次拉取存活实例列表先确认自己已注册_is_self_registered再排除自身得到对等节点集合交给_reconcile与本地 adapter 列表对齐新 peer 建 adapter_add_adapter配置lookup_timeout_s/load_timeout_s、消失的 peer 计数 miss 超过 3 次后拆除_remove_adapter、连接信息变化的 peer 重建 adapter。数据面lookup-and-lock 与 RDMA 读P2P adapter 的实现见 lmcache/v1/distributed/l2_adapters/p2p_l2_adapter.py。查找侧通过 RPC 向对端提交p2p_lookup_and_lock对端在自身 L1 中锁定命中的对象并返回其共享内存地址见_build_addresses本地按lookup_timeout_s轮询结果加载侧通过传输通道客户端submit_read(local_addresses, remote_addresses)发起 RDMA 读取按load_timeout_s轮询完成状态超时或 RPC 超时均优雅降级为 miss/failure配合 vLLM 侧的kv_load_failure_policy: recompute回退到重算。限制Limitations只读Read-only节点从对等节点读取 KV从不写入对等节点的内存。这保证了每个节点是其 L1 的唯一所有者。单跳One hop节点直接从持有该前缀的对等节点读取读取不会跨多个对等节点链式转发。小结P2P KV Cache Sharing 以 coordinator 每节点 L2 adapter 传输通道三层结构把多节点各自的本地缓存合并为一个逻辑缓存在 RDMA 网络上以单跳、只读的方式实现跨节点前缀复用。生产部署时请记住三个关键点启用 P2P 必须同时提供--p2p-advertise-url与--coordinator-url确保 L1 是单一连续区域避开 GDS / Device-DAX将--l1-align-bytes提高到 65536 以获得更好的 RDMA 传输性能。更完整的官方参考文档见 docs/source/mp/p2p.rst。【免费下载链接】LMCacheLMCache: Supercharge Your LLM with the Fastest KV Cache Layer项目地址: https://gitcode.com/GitHub_Trending/lm/LMCache创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考