XpYd#
X 预填充器,Y 解码器 (XpYd) 示例#
此示例演示了如何在单个节点上使用 NIXL 运行带有分离式 Prefill 的 LMCache,使用多个 Prefiller 和 Decoder 实例。此配置允许对计算密集型的 Prefill 操作和解码操作进行横向扩展,从而实现更好的资源利用和更高的吞吐量。
架构概述#
XpYd 设置由多个可以独立扩展的组件组成:
多个 Prefiller 服务器 - 处理推理的预填充阶段(初始提示处理)
多个解码器服务器 - 处理推理的解码阶段(令牌生成)
代理服务器 - 使用轮询负载均衡协调预填充器和解码器之间的请求
示例 2p2d 架构:
┌─────────────┐
│ Client │
└─────┬───────┘
│
┌───────▼────────┐
│ Proxy Server │
│ Port 9100 │──────────────────────|
│ (Round-Robin) │ |
└───▲────────▲───┘ |
│ │ |
┌────────▼───┐ ┌─▼──────────┐ |
│ Prefiller1 │ │ Prefiller2 │ |
│ Port 7100 │ │ Port 7101 │ |
│ GPU 0 │ │ GPU 1 │ |
└─────▲──────┘ └─────▲──────┘ |
│ │ |
│ NIXL transfer | |
│ │ |
┌────▼───────┐ ┌────▼──────┐ |
│ Decoder 1 │ │ Decoder 2 │ |
│ Port 7200 │ │ Port 7201 │ |
│ GPU 2 │ │ GPU 3 │ |
└────▲───────┘ └────▲──────┘ |
│ │ |
└───────────────┴──────────────────────|
先决条件#
LMCache: 使用
pip install lmcache安装NIXL: 从 NIXL GitHub 仓库 安装
硬件:至少需要 4 个 GPU(2 个用于 Prefillers + 2 个用于 2p2d 设置中的解码器)
模型访问:有效的 Hugging Face 令牌 (HF_TOKEN) 用于 Llama 3.1 8B Instruct
快速开始 (2p2d 示例)#
设置你的 Hugging Face 令牌:
export HF_TOKEN=hf_your_token_here导航到示例目录:
cd examples/disagg_prefill/xpyd_experimental运行示例:
bash disagg_example_xpyd.sh
该脚本将自动:
在端口 8200 和 8201 启动两个解码器实例(GPU 2 和 GPU 3)
在端口 7100 和 7101 上启动两个 Prefill 实例(GPU 0 和 GPU 1)
在9100端口启动一个代理服务器,并使用轮询负载均衡。
等待所有服务器准备就绪
按 Ctrl+C 停止所有服务器。
配置#
重要:为了正确的 KV Cache 传输,确保所有进程使用相同的 PYTHONHASHSEED 以保持 KV Cache 在进程间的一致性:
export PYTHONHASHSEED=0
分离式 Prefill 配置#
所有 Prefiller 通过 configs/lmcache-prefiller-config.yaml 共享相同的配置:
local_cpu: False
max_local_cpu_size: 0
max_local_disk_size: 0
enable_pd: True
transfer_channel: "nixl"
pd_role: "sender"
pd_proxy_host: "localhost"
pd_proxy_port: 7500
pd_buffer_size: 1073741824 # 1GB
pd_buffer_device: "cuda"
关键设置:
pd_role: "sender"- 配置这些实例以发送 KV Cache 数据pd_buffer_size: 1073741824 # 1GB- PD 传输缓冲区大小的上限(以字节为单位),与块大小对齐pd_buffer_device: "cuda"- 使用显存进行缓冲
解码器配置#
解码器通过 configs/lmcache-decoder-x-config.yaml 进行配置:
local_cpu: False
max_local_cpu_size: 0
enable_pd: True
transfer_channel: "nixl"
pd_role: "receiver"
pd_peer_host: "localhost"
pd_peer_init_port: 730x
pd_peer_alloc_port: 740x
pd_buffer_size: 2147483648 # 2GB
pd_buffer_device: "cuda"
pd_backends: [UCX]
关键设置:
pd_role: "receiver"- 配置这些实例以接收 KV Cache 数据pd_buffer_size: 2147483648 # 2GB- PD 传输缓冲区大小的上限(以字节为单位),与块大小对齐pd_buffer_device: "cuda"- 使用显存进行缓冲
组件深入分析#
代理服务器 (disagg_proxy_server.py)#
代理服务器协调多预填充器的分离式工作流程:
请求处理:在 9000 端口接收客户端请求
负载均衡:使用轮询将请求分配到多个预填充器
Prefill Coordination: 将请求发送到使用
max_tokens=1的 Prefill 处理器Prefill Response: 接收预填充器,表示 nixl 转移已完成
响应流:从解码器流式传输完整响应
性能监控: 跟踪首次令牌时间(TTFT)统计信息
主要特性:- 轮询分配:在 --num-prefillers 实例之间平衡负载 - 容错:优雅地处理预填充器故障 - 监控:为每个预填充器提供详细的 TTFT 统计信息
支持的端点: - /v1/completions - /v1/chat/completions
vLLM 服务器启动器 (disagg_vllm_launcher.sh)#
此脚本启动具有适当配置的单个 vLLM 服务器:
Prefiller1 启动命令:
UCX_TLS=cuda_ipc,cuda_copy,tcp \
LMCACHE_CONFIG_FILE=$prefill_config_file \
VLLM_ENABLE_V1_MULTIPROCESSING=1 \
VLLM_WORKER_MULTIPROC_METHOD=spawn \
CUDA_VISIBLE_DEVICES=0 \
vllm serve $MODEL \
--port 7100 \
--disable-log-requests \
--enforce-eager \
--no-enable-prefix-caching \
--kv-transfer-config \
'{"kv_connector":"LMCacheConnectorV1","kv_role":"kv_producer","kv_connector_extra_config": {"discard_partial_chunks": false, "lmcache_rpc_port": "producer1"}}'
Prefiller2 启动命令:
UCX_TLS=cuda_ipc,cuda_copy,tcp \
LMCACHE_CONFIG_FILE=$prefill_config_file \
VLLM_ENABLE_V1_MULTIPROCESSING=1 \
VLLM_WORKER_MULTIPROC_METHOD=spawn \
CUDA_VISIBLE_DEVICES=1 \
vllm serve $MODEL \
--port 7101 \
--disable-log-requests \
--enforce-eager \
--no-enable-prefix-caching \
--kv-transfer-config \
'{"kv_connector":"LMCacheConnectorV1","kv_role":"kv_producer","kv_connector_extra_config": {"discard_partial_chunks": false, "lmcache_rpc_port": "producer2"}}'
解码器1 启动命令:
UCX_TLS=cuda_ipc,cuda_copy,tcp \
LMCACHE_CONFIG_FILE=$decode_config_file \
VLLM_ENABLE_V1_MULTIPROCESSING=1 \
VLLM_WORKER_MULTIPROC_METHOD=spawn \
CUDA_VISIBLE_DEVICES=2 \
vllm serve $MODEL \
--port 7200 \
--disable-log-requests \
--enforce-eager \
--no-enable-prefix-caching \
--kv-transfer-config \
'{"kv_connector":"LMCacheConnectorV1","kv_role":"kv_consumer","kv_connector_extra_config": {"discard_partial_chunks": false, "lmcache_rpc_port": "consumer1", "skip_last_n_tokens": 1}}'
Decoder2 启动命令:
UCX_TLS=cuda_ipc,cuda_copy,tcp \
LMCACHE_CONFIG_FILE=$decode_config_file \
VLLM_ENABLE_V1_MULTIPROCESSING=1 \
VLLM_WORKER_MULTIPROC_METHOD=spawn \
CUDA_VISIBLE_DEVICES=3 \
vllm serve $MODEL \
--port 7201 \
--disable-log-requests \
--enforce-eager \
--no-enable-prefix-caching \
--kv-transfer-config \
'{"kv_connector":"LMCacheConnectorV1","kv_role":"kv_consumer","kv_connector_extra_config": {"discard_partial_chunks": false, "lmcache_rpc_port": "consumer2", "skip_last_n_tokens": 1}}'
与 1p1d 的主要区别: - 每个 Prefiller 获取一个唯一的 ``lmcache_rpc_port``(producer1、producer2 等) - 每个 Prefiller 在不同的 GPU 上运行(CUDA_VISIBLE_DEVICES) - 每个 Prefiller 使用不同的端口(7100、7101 等) - 每个解码器使用不同的端口(7200、7201 等)
基本测试#
一旦所有服务器都在运行,您可以使用简单的 curl 命令进行测试:
curl -s -N -X POST http://127.0.0.1:9100/v1/completions -H "Content-Type: application/json" -d '{
"model": "meta-llama/Llama-3.1-8B-Instruct",
"prompt": "What date is today?",
"max_tokens": 20,
"temperature": 0.0
}'
性能基准测试#
要进行全面的性能测试,请使用 vLLM 的基准测试工具:
vllm bench serve --port 9100 --seed $(date +%s) \
--model meta-llama/Llama-3.1-8B-Instruct \
--dataset-name random --random-input-len 7500 --random-output-len 200 \
--num-prompts 30 --burstiness 100 --request-rate 1 --ignore-eos
预期的性能提升与 2p2d 相关:- 更高的吞吐量:多个预填充器可以处理更多的并发请求 - 更好的 TTFT:负载均衡减少排队延迟 - 更好的利用率:多个设备之间更好的 GPU 利用率
示例基准测试结果:
============ Serving Benchmark Result ============
Successful requests: 30
Benchmark duration (s): 31.34
Total input tokens: 224970
Total generated tokens: 6000
Request throughput (req/s): 0.96
Output token throughput (tok/s): 191.44
Total Token throughput (tok/s): 7369.36
---------------Time to First Token----------------
Mean TTFT (ms): 313.41
Median TTFT (ms): 272.83
P99 TTFT (ms): 837.32
-----Time per Output Token (excl. 1st token)------
Mean TPOT (ms): 8.84
Median TPOT (ms): 8.72
P99 TPOT (ms): 11.35
---------------Inter-token Latency----------------
Mean ITL (ms): 8.84
Median ITL (ms): 8.61
P99 ITL (ms): 11.43
==================================================
日志文件和监控#
该示例生成多个日志文件以进行全面监控:
prefiller1.log- 第一个 Prefill 服务器的日志和错误prefiller2.log- 第二个 Prefill 服务器日志和错误decoder1.log- 第一个解码器服务器日志和错误decoder1.log- 第一个解码器服务器日志和错误proxy.log- 代理服务器日志和 TTFT 统计信息
代理服务器为每个 Prefiller 提供详细的统计信息:
===============================
Num requests: 20
Prefiller 1 TTFT stats:
- Average (ms): 42.3
- Median (ms): 40.1
- 99th Percentile (ms): 48.7
Prefiller 2 TTFT stats:
- Average (ms): 43.8
- Median (ms): 41.5
- 99th Percentile (ms): 52.1
===============================
这有助于识别不同 Prefill 实例之间的性能差异并优化负载均衡。
故障排除#
常见问题#
显存:确保每个 GPU 具有足够的内存以支持模型
NIXL 安装: 验证 NIXL 是否正确安装并可访问
端口冲突:检查所有必需的端口是否可用
HF Token: 确保您的 Hugging Face 令牌可以访问 Llama 模型
GPU 分配: 验证 CUDA_VISIBLE_DEVICES 的分配是否存在冲突
多实例特定问题#
不均匀负载:监控 Prefill 统计数据以确保负载均衡分配
资源争用:监控多个实例的显存压力
网络瓶颈:监控实例之间的 NIXL 传输性能
启动时机:错开 Prefill 启动以避免资源冲突