P2P KV 缓存共享#
概述#
在多节点部署中,每个节点运行自己的 LMCache 服务器进程,每个节点在其本地内存中缓存它所服务请求的 KV。没有共享时,当相同的前缀到达不同节点时,必须从头开始重计算在一个节点上计算的前缀。
点对点 (P2P) KV 缓存共享将每个节点的缓存转变为一个逻辑缓存。 当一个节点查找不在其本地内存中的前缀时,它可以通过数据中心网络使用 RDMA 直接从持有该前缀的对等节点的内存中读取该前缀的 KV。结果是整个系统的有效缓存命中率大大提高,而无需在热路径中使用中央存储层。
传输是从请求节点到其自身 L1 缓冲区的单向 RDMA 读取;拥有数据的节点不会被打断来提供数据。在支持 RDMA 的网络(如 InfiniBand / RoCE)上,这比重新计算前缀或通过共享对象存储进行往返要快得多。
它是如何工作的#
P2P 涉及三个部分:
协调器 — 一个小型 HTTP 服务(每个部署一个),用于跟踪哪些 LMCache 服务器处于活动状态。每个服务器向其注册并发送心跳;协调器回答“我的活动对等体是谁?”的查询。它仅管理成员资格——它从不查看 KV 数据或参与查找。请参见 多服务器协调。
LMCache 服务器 — 每个服务器运行一个 P2P 控制器,定期向协调器请求当前的对等体列表,并为每个活跃的对等体打开一个连接,用于查找和读取该对等体的 KV。
传输通道 — 执行实际远程内存读取的 RDMA 层。每个服务器在启动时注册其 L1 缓冲区,以便对等体可以从中读取。
在缓存未命中时,一个节点请求拥有前缀的对等节点锁定并定位它,接收远程地址,将 KV 读取到自己的 L1 中,并从那里提供请求。对等节点在加入时会自动发现和连接,在离开时会自动断开 — 无需静态对等节点列表。
要求#
一个协调者。 P2P 需要协调者进行对等发现。使用
--p2p-advertise-url启动的服务器如果没有--coordinator-url则拒绝启动。强烈推荐使用支持 RDMA 的网络(InfiniBand / RoCE)以获得生产性能。默认情况下,P2P 使用与 LMCache 一起提供的
nixl传输引擎,因此无需额外安装任何东西。一个单一的、连续的 L1 区域。 传输通道为 RDMA 注册整个 L1 缓冲区,因此 P2P 与 GDS L1 层(
--gds-l1-path)和 Device-DAX L1 层(--l1-devdax-path)不兼容;在这些配置下,服务器拒绝启动。
配置#
P2P 通过 --p2p-advertise-url 标志在每个服务器上启用。相关的 lmcache server 标志包括:
标志 |
描述 |
|---|---|
|
此服务器向对等节点广告的传输通道端点。设置它可以启用 P2P。必须是其他节点可以访问的地址。 |
|
传输通道服务器绑定的地址。默认为 |
|
对等查找的截止时间,超过该时间将视为未命中(默认 |
|
对等 KV 读取的截止时间,超过该时间将视为失败(默认 |
|
传输通道实现(默认 |
P2P 还重用协调器连接标志 (--coordinator-url, --coordinator-advertise-ip, --coordinator-heartbeat-interval);心跳间隔同时作为对等发现轮询间隔。有关完整的标志列表,请参见 lmcache server 和 lmcache coordinator。
小技巧
在参与 P2P 的服务器上,将 L1 缓冲区对齐增加到至少 64 KB (--l1-align-bytes 65536)。更大的对齐可以让传输通道发出更大、更好对齐的 RDMA 读取,并显著提高传输性能。默认值(4 KB)对于非 P2P 部署是可以的。
传输引擎后端#
传输引擎是执行远程内存读取的组件,按服务器选择,使用 --p2p-transfer-engine。
引擎 |
描述 |
|---|---|
|
基于 RDMA 的传输,随 LMCache 一起提供。运行在 InfiniBand / RoCE 网络上。 |
nixl 是目前唯一可用的后端。传输引擎是一个可插拔的抽象,因此可以在未来添加其他后端,而无需更改 P2P 堆栈的其余部分。
运行多节点部署#
下面的示例启动了一个两节点的集群。添加更多节点只需复制每个节点的步骤,所有节点都指向相同的协调器。
步骤 1 — 启动协调器(在所有节点都能访问的主机上,这里是 10.0.0.1):
lmcache coordinator --host 0.0.0.0 --port 9300
步骤 2 — 在每个节点上启动启用 P2P 的 LMCache 服务器。 将 <NODE_IP> 替换为该节点的可路由地址(例如 10.0.0.2、10.0.0.3 等):
lmcache server \
--host 0.0.0.0 --port 5555 \
--http-port 8080 \
--l1-size-gb 100 --eviction-policy LRU \
--l1-align-bytes 65536 \
--coordinator-url http://10.0.0.1:9300 \
--coordinator-advertise-ip <NODE_IP> \
--p2p-advertise-url <NODE_IP>:8500
--coordinator-advertise-ip 是对等节点用来访问该节点控制平面的地址,而 --p2p-advertise-url 是 RDMA 传输端点。这里服务器绑定 0.0.0.0(所有接口)并广播 <NODE_IP>。
步骤 3 — 在每个节点上,通过连接器启动指向本地 LMCache 服务器的 vLLM。vLLM 从不直接与对等节点通信 — 它连接的 LMCache 服务器代表它进行 P2P:
vllm serve <model> \
--port 8000 \
--kv-transfer-config '{"kv_connector":"LMCacheMPConnector","kv_role":"kv_both","kv_load_failure_policy":"recompute","kv_connector_extra_config":{"lmcache.mp.port":5555}}'
一旦两个节点都已注册,当相同的前缀稍后到达节点 3 时,首先在节点 2 上提供的前缀将从节点 2 的缓存中提供——通过 RDMA 读取,而不是重计算。
在单个节点上运行(测试和调试)#
您可以通过在 localhost 上运行两个 LMCache 服务器和两个 vLLM 服务器,以及一个协调器,在单个多 GPU 机器上执行整个 P2P 路径。这是开发和调试 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
CUDA_VISIBLE_DEVICES=0 vllm serve <model> --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
CUDA_VISIBLE_DEVICES=1 vllm serve <model> --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}}'
这两个服务器必须在 每个 端口上有所不同:ZMQ (--port)、HTTP (--http-port) 和 P2P 传输端点 (--p2p-advertise-url)。为每个服务器指定一个独特的 --instance-id,以便于区分。
要测试路径,向 vLLM A(端口 8000)发送一个长提示,然后向 vLLM B(端口 8001)发送相同的提示。B 从未见过该提示,并且它自己的缓存是空的,因此 B 上的任何 LMCache 命中必须是通过 P2P 从 A 读取的。
备注
在单个主机上,localhost 流量通常使用回环/TCP 路径而不是 RDMA,因此延迟并不能代表真实的 RDMA 结构。单节点模式用于 功能 测试和调试;在真实的多节点 RDMA 部署上进行基准性能测试。
验证 P2P 是否正常工作#
查询服务器的状态端点以查看其 P2P 状态和发现的对等节点:
curl -s http://127.0.0.1:7555/status | python3 -m json.tool
查找 p2p_state (一旦加入协调器则为 registered)和 p2p_peer_count (连接的对等节点数量;在两个节点的示例中为 1)。您还可以从协调器列出整个集群:
curl -s http://127.0.0.1:9300/instances | python3 -m json.tool
在 LMCACHE_LOG_LEVEL=DEBUG 下,每个服务器在连接到对等体时记录 Added P2P adapter ... for peer <instance-id>,在对等体离开时记录 Removed P2P adapter ...。
限制#
只读。 节点从其对等节点读取 KV;它永远不会写入对等节点的内存。这使得每个节点都是其 L1 的唯一拥有者。
单跳。 节点直接从持有前缀的对等节点读取;读取不会跨多个对等节点链式进行。