分离式预填充#
概述#
分离式 Prefill (P/D) 在不同的 vLLM 实例上运行 Prefill 和解码:一个 Prefill 实例计算提示的 KV,将其通过 NIXL 交给一个 解码 实例,解码器生成令牌而无需重新计算提示。Prefill 和解码密集型工作可以独立扩展。
LMCache MP 在此基础上增加了跨请求的 KV 重用。 每个 vLLM 实例还将其 KV 卸载到一个共置的 LMCache 服务器,因此在后续请求中,或在从 GPU HBM 逐出后,重复的前缀将从 LMCache 加载,而不是重新计算。预取路径上的 KV 源顺序:本地 GPU 缓存 → LMCache → 重计算。
两者都是通过 vLLM 的 MultiConnector 组合而成,每个实例运行两个连接器:
NixlConnector— 当前请求的 Prefill→解码 KV 移交,通过 NIXL (UCX / RDMA)。LMCacheMPConnector— 卸载/加载到实例的 LMCache 服务器以实现跨请求重用。
在 P/D 模式下,vLLM 路由器 将每个请求发送到一个 Prefill 实例,然后是一个解码实例,并在它们之间线程化 NIXL 握手。
它是如何工作的#
最小部署需要五个进程:
Prefill vLLM (生产者) —
MultiConnector[NixlConnector (kv_producer), LMCacheMPConnector]; 计算提示 KV,将其存储到 LMCache 服务器,并为解码器提供拉取功能。解码 vLLM (消费者) —
MultiConnector[NixlConnector (kv_consumer), LMCacheMPConnector]; 从生产者拉取提示 KV 并生成输出。两个 LMCache 服务器 — 每个 vLLM 实例一个 ``lmcache server``(它们不能共享一个)。
路由器 —
vllm-router --vllm-pd-disaggregation路由 Prefill 然后解码。
为了在 Prefill 和解码池之间共享重用,在顶部逐层 P2P KV Cache 共享 <p2p>`(给两个服务器一个协调器和 `--p2p-advertise-url``)。
要求#
重要
vllm-project/vllm#46865 (MultiConnector 实际块修复)解锁了在 MultiConnector 下的 LMCache 卸载。没有它,卸载将静默地永远不会触发。
NIXL 在 vLLM 环境中可用(
lmcache[nixl]附加项,拉取nixl>=1.3.0)。
配置#
每个 vLLM 实例都需要一个 --kv-transfer-config,选择 MultiConnector,并包含两个子连接器:
键 |
描述 |
|---|---|
|
并行运行 |
|
|
|
Prefill→解码交接。将 |
|
卸载/加载到本地 LMCache 服务器;使用 |
|
实例的 LMCache 服务器的传输主机和 ZMQ ``--port``(每个实例不同)。 |
NIXL 传输通过 vLLM 实例上的环境变量进行调整:VLLM_NIXL_SIDE_CHANNEL_HOST``(用于握手的主机,由对等方可达),``VLLM_NIXL_SIDE_CHANNEL_PORT``(**在同一主机上的实例之间必须不同**;默认值为 ``5600),以及 UCX_NET_DEVICES=all 和 NCCL_CUMEM_ENABLE=1。有关完整的 lmcache server 标志列表,请参见 lmcache server。
运行部署#
下面的示例将 Prefill 池、解码池和路由器放置在不同的主机上。将 <PREFILL_IP> / <DECODE_IP> 替换为可路由地址,并将 <model> 替换为模型路径(在两个实例上相同)。
**步骤 1 — Prefill LMCache 服务器**(在 Prefill 主机上):
lmcache server \
--port 6555 --http-port 8090 \
--l1-size-gb 100 --eviction-policy LRU --chunk-size 256 \
--instance-id prefiller
**步骤 2 — Prefill vLLM(生产者)**(在 Prefill 主机上):
VLLM_NIXL_SIDE_CHANNEL_HOST=<PREFILL_IP> VLLM_NIXL_SIDE_CHANNEL_PORT=5600 \
UCX_NET_DEVICES=all NCCL_CUMEM_ENABLE=1 \
vllm serve <model> \
--port 8001 --tensor-parallel-size 1 \
--kv-transfer-config '{"kv_connector":"MultiConnector","kv_role":"kv_producer","kv_connector_extra_config":{"connectors":[{"kv_connector":"NixlConnector","kv_role":"kv_producer","kv_load_failure_policy":"fail"},{"kv_connector":"LMCacheMPConnector","kv_role":"kv_both","kv_connector_extra_config":{"lmcache.mp.host":"tcp://localhost","lmcache.mp.port":6555}}]}}'
生产者在 <PREFILL_IP>:5600 上宣传其 NIXL 边通道,并将任务卸载到端口 6555 的 LMCache 服务器上。
**步骤 3 — 解码 LMCache 服务器**(在解码主机上):
lmcache server \
--port 6556 --http-port 8091 \
--l1-size-gb 100 --eviction-policy LRU --chunk-size 256 \
--instance-id decoder
**步骤 4 — 解码 vLLM(消费者)**(在解码主机上):
VLLM_NIXL_SIDE_CHANNEL_HOST=<DECODE_IP> VLLM_NIXL_SIDE_CHANNEL_PORT=5558 \
UCX_NET_DEVICES=all NCCL_CUMEM_ENABLE=1 \
vllm serve <model> \
--port 8002 --tensor-parallel-size 1 \
--kv-transfer-config '{"kv_connector":"MultiConnector","kv_role":"kv_consumer","kv_connector_extra_config":{"connectors":[{"kv_connector":"NixlConnector","kv_role":"kv_consumer","kv_load_failure_policy":"fail"},{"kv_connector":"LMCacheMPConnector","kv_role":"kv_both","kv_connector_extra_config":{"lmcache.mp.host":"tcp://localhost","lmcache.mp.port":6556}}]}}'
消费者将任务卸载到端口 6556 的 LMCache 服务器;其侧信道端口 (5558) 与生产者的不同,以便两者可以共享一个主机。
**步骤 5 — 路由器**(在任何可以访问两个 vLLM 实例的主机上):
vllm-router \
--policy round_robin \
--vllm-pd-disaggregation \
--prefill http://<PREFILL_IP>:8001 \
--decode http://<DECODE_IP>:8002 \
--host 0.0.0.0 --port 30000
将请求发送到路由器(http://<ROUTER_IP>:30000/v1/...);它透明地处理 Prefill→解码的分离。
在单个节点上运行(测试和调试)#
在 localhost 上运行相同的五个进程,GPU 6 上进行 Prefill,GPU 7 上进行解码。实例必须在 每个 共享主机端口上有所不同:LMCache 的 --port 和 --http-port,vLLM 的 --port,以及 VLLM_NIXL_SIDE_CHANNEL_PORT。
lmcache server --port 6555 --http-port 8090 \
--l1-size-gb 100 --eviction-policy LRU --chunk-size 256 --instance-id prefiller
CUDA_VISIBLE_DEVICES=6 VLLM_NIXL_SIDE_CHANNEL_HOST=127.0.0.1 VLLM_NIXL_SIDE_CHANNEL_PORT=5600 \
UCX_NET_DEVICES=all NCCL_CUMEM_ENABLE=1 \
vllm serve <model> --port 8001 --enforce-eager --max-model-len 16384 --gpu-memory-utilization 0.4 \
--kv-transfer-config '{"kv_connector":"MultiConnector","kv_role":"kv_producer","kv_connector_extra_config":{"connectors":[{"kv_connector":"NixlConnector","kv_role":"kv_producer","kv_load_failure_policy":"fail"},{"kv_connector":"LMCacheMPConnector","kv_role":"kv_both","kv_connector_extra_config":{"lmcache.mp.host":"tcp://localhost","lmcache.mp.port":6555}}]}}'
lmcache server --port 6556 --http-port 8091 \
--l1-size-gb 100 --eviction-policy LRU --chunk-size 256 --instance-id decoder
CUDA_VISIBLE_DEVICES=7 VLLM_NIXL_SIDE_CHANNEL_HOST=127.0.0.1 VLLM_NIXL_SIDE_CHANNEL_PORT=5558 \
UCX_NET_DEVICES=all NCCL_CUMEM_ENABLE=1 \
vllm serve <model> --port 8002 --enforce-eager --max-model-len 16384 --gpu-memory-utilization 0.4 \
--kv-transfer-config '{"kv_connector":"MultiConnector","kv_role":"kv_consumer","kv_connector_extra_config":{"connectors":[{"kv_connector":"NixlConnector","kv_role":"kv_consumer","kv_load_failure_policy":"fail"},{"kv_connector":"LMCacheMPConnector","kv_role":"kv_both","kv_connector_extra_config":{"lmcache.mp.host":"tcp://localhost","lmcache.mp.port":6556}}]}}'
vllm-router --policy round_robin --vllm-pd-disaggregation \
--prefill http://localhost:8001 --decode http://localhost:8002 --host 0.0.0.0 --port 30000
将 --enable-tracing --otlp-endpoint http://localhost:4317 添加到每个 lmcache server 以将跟踪/指标导出到本地 OpenTelemetry 收集器。
备注
在单个主机上,NIXL 通过 localhost 使用回环/TCP,而不是 RDMA,因此延迟并不具有代表性——单节点模式仅用于 功能 测试。
验证其是否正常工作#
curl -s http://localhost:30000/v1/completions -H "Content-Type: application/json" \
-d '{"model":"<model>","prompt":"The capital of France is","max_tokens":16}'
P/D 路由 — 响应
id被标记为工作节点,例如cmpl-___prefill_addr_localhost:8001___decode_addr_localhost:8002_...。NIXL 传输 — 解码 vLLM 日志
KV Transfer metrics: NixlConnector={'Num successful transfers': N, ...}.LMCache 卸载 —
storage_manager.l1_manager.total_object_count在curl -s http://localhost:8090/status中增长(提示必须至少为--chunk-size个标记才能存储任何内容)。LMCache 命中率 — 阅读 vLLM 的 “外部前缀缓存命中率” 日志行(从 LMCache 提供的 Prefill 令牌的比例),而不是 LMCache 服务器
/status。当工作集适合显存时,它保持为0,一旦重用溢出显存缓存,它就会上升。
限制与调优#
在高并发下保持冷 KV 传输的边界。 vLLM 的原生 NIXL P/D 路径对同时进行的许多大型 冷 传输(完整提示,未缓存)非常敏感。保持解码实例的 GPU KV Cache 足够大,以容纳工作集,从而使传输保持较小并由缓存服务,或者在每个请求的传输较大时减少并发。