Files
tech/知识/CSDN博客/小徐炸酱面/2025-12-01_pd分离在vllm中用法.md
arno c0ba3fb853 文档(金鹏): 2026-08-06 章节 48 篇文章摘要归档
- 46 篇原文+摘要双文件归档(按 来源/作者 分层,复用本地归档 20 篇+新抓取 26 篇)
- 即梦生成 9 组主题配图(大图+列表缩略图)存入 知识/金鹏/20260806/
- 章节重组为 9 个主题分组并挂接摘要引用
2026-08-06 18:00:50 +08:00

7.7 KiB
Raw Permalink Blame History

pd分离在vllm中用法

来源CSDN 博客(博主:小徐炸酱面)
作者:小徐炸酱面(qq_44319972
发布日期2025-12-012025-12-02 修改)
原文链接https://blog.csdn.net/qq_44319972/article/details/155456852


1. 背景:为什么需要 PD 分离

大模型在线服务一般包含两种阶段:

  • Prefill 阶段
    • 输入:完整的 prompt
    • 动作:一次性跑完所有输入 token 的前向,构建 KV Cache
    • 特点:
      • 计算密集GEMM-heavy,吃算力)
      • 对显存需求大(一次性占用大量 KV
      • 对带宽要求相对没那么极端(一次性大算子)
  • Decode 阶段
    • 输入:每轮只处理很少的新 token(一般 1~几 token
    • 动作:基于已有 KV Cache 不断迭代生成新 token
    • 特点:
      • 单步计算量相对小
      • 访问 KV Cache 频繁,更吃显存带宽 / 内存带宽 / 通信带宽
      • 很多小 kernel,调度频率高

PD 分离(Producer / Decoder Separation 的目标就是:

  • 把 prefill 和 decode 拆到不同的进程 / 节点 / 设备
  • 利用不同节点的算力/带宽特性,整体提升吞吐、降低延迟

2. PD 分离的基本概念与架构

2.1 角色定义

  • P 节点(Producer
    • 负责:prefill 阶段
    • 对应操作:
      • 处理新请求的 prompt
      • 运行大批量 GEMM
      • 把得到的 KV Cache 写入某种共享介质(或通过 KV 通道发送给 D 节点)
    • 资源特征:
      • 更看重:算力、GPU FLOPs
      • 对长尾小请求和频繁通信不是特别敏感
  • D 节点(customer
    • 负责:decode 阶段
    • 对应操作:
      • 不断从 KV 中取数据
      • 迭代生成每个 request 的后续 token
    • 资源特征:
      • 更看重:显存带宽 / 网络带宽 / 延迟
      • 关注 tail latencyTPOT、e2e

2.2 KV Cache 的流转

PD 分离的核心就是:prefill 生成的 KV,如何让 decode 能够用起来

一般数据流可以抽象成:

  1. 请求进来,P 节点接收并执行 prefill
  2. P 节点为该 request 分配 KV Block(或 Block 列表)
  3. P 节点把 KV 的位置信息 + request 的 metadata 通过某种 KV Connector / RPC 通道 告诉 D 节点
  4. D 节点接管该 request 的 decode
    • 知道对应 KV 在哪里
    • 在后续 decode 中,始终通过 KV Connector 访问这些 KV

典型注意事项:

  • 如果有 DP/TP,P/D 侧的并行策略也要匹配(否则会出现 KV 维度不一致、block id 对不上等问题)
  • 如果跨机器,需要额外考虑网络带宽、RDMA、压缩等问题

3. vLLM 中的 PD 分离思路(概念层面)

这里以 "vLLM + 自研 PD 功能" 的思路来整理,只写通用逻辑,不绑死具体实现细节。

3.1 与调度器(scheduler)的关系

vLLM 的 serving 里,有一个统一的 scheduler 管理:

  • 新请求的进入(prefill 阶段)
  • 已有请求的继续 decode
  • 每一轮 step,要算多少 token(num_scheduled_tokens
  • 每个 request 一共已经算了多少 tokennum_computed_tokens

PD 分离后,逻辑可以拆解为:

  1. Producer 侧 scheduler:只负责 prefill
  2. Decoder 侧 scheduler:只负责 decode
  3. 通过某种方式保证两边对 request 状态的约定一致,例如:
    • request id
    • 已生成 token 数
    • KV block id 列表
    • 当前是否已经结束(EOS / abort)

4. 在 vLLM 中使用 PD 分离的典型启动方式(示意)

下面用伪代码/伪命令展示结构,变量名你可以换成你们环境里的 VLLM_USE_V1 / vllm_PD_ROLE / kv_transfer_config 等。

4.1 不分离时(baseline

# 单机单进程示意
python3 -m vllm.entrypoints.openai.api_server \
  --model /data/models/Qwen3-32B \
  --tensor-parallel-size 2 \
  --dtype bfloat16 \
  --max-model-len 32768 \
  --port 8000 \
  --host 0.0.0.0

Benchmark 示意:

python3 benchmarks/benchmark_serving.py \
  --backend openai-chat \
  --base-url http://<host>:8000 \
  --endpoint /v1/chat/completions \
  --model Qwen3-32B \
  --tokenizer /data/models/Qwen3-32B \
  --dataset-name random \
  --random-input-len 2000 \
  --random-output-len 200 \
  --request-rate 5 \
  --num-prompts 100 \
  --percentile-metrics ttft,tpot,e2el

4.2 1P1D(单机或双机)

4.2.1 Producer 节点

export VLLM_USE_V1=1
export vllm_PD_ROLE=producer

python3 -m vllm.entrypoints.openai.api_server \
  --model /data/models/Qwen3-32B \
  --tensor-parallel-size 2 \
  --max-model-len 32768 \
  --max-num-batched-tokens 8192 \
  --max-num-seqs 32 \
  --port 8209 \
  --host 0.0.0.0 \
  --kv-transfer-config \
'{"kv_connector":"P2pNcclConnector",
  "kv_role":"kv_producer",
  "kv_rank":0,
  "kv_parallel_size":2,
  "kv_buffer_size":1,
  "kv_port":9109,
  "kv_connector_extra_config":{
    "proxy_ip":"0.0.0.0",
    "proxy_port":30001,
    "http_port":8109
  }}' \
  &> Qwen3-32B-producer.log &

4.2.2 Decoder 节点(Dkv_consumer

⚠️ 原文此处的代码块与 Producer 节点完全相同(vllm_PD_ROLE=producerkv_role:"kv_producer"),疑为作者复制粘贴遗漏修改。按原文忠实保留如下,实际部署时 Decoder 侧应改为 vllm_PD_ROLE=decoder / kv_role:"kv_consumer"kv_rank 相应调整。

export VLLM_USE_V1=1
export vllm_PD_ROLE=producer

python3 -m vllm.entrypoints.openai.api_server \
  --model /data/models/Qwen3-32B \
  --tensor-parallel-size 2 \
  --max-model-len 32768 \
  --max-num-batched-tokens 8192 \
  --max-num-seqs 32 \
  --port 8209 \
  --host 0.0.0.0 \
  --kv-transfer-config \
'{"kv_connector":"P2pNcclConnector",
  "kv_role":"kv_producer",
  "kv_rank":0,
  "kv_parallel_size":2,
  "kv_buffer_size":1,
  "kv_port":9109,
  "kv_connector_extra_config":{
    "proxy_ip":"0.0.0.0",
    "proxy_port":30001,
    "http_port":8109
  }}' \
  &> Qwen3-32B-producer.log &

kv-transfer-config 参数解释:

  • kv_connector:用什么通道传 KV。现在用:P2pNcclConnector,表示走 NCCL P2P 通信。
  • kv_role:这个进程是发 KV 的还是收 KV 的
    • P"kv_producer"
    • D"kv_consumer"
  • kv_rank:在这组通道里的编号,从 0 开始。多个并行通道时,用它来区分是谁↔谁。
  • kv_parallel_size:一共有几路 KV 通道(也可以理解为这组里有多少 rank)。一般设成和 TP/world size 一致。P 和 D 必须一样。
  • kv_buffer_size:通道里最多允许多少块 KV "在路上"。值越大,流水越深、占用显存可能越多;不确定就先用 1,稳一点。
  • kv_port:KV 通道用的基础端口。P / D 两边要一致,别和其他服务冲突。

kv_connector_extra_config 里的:

  • proxy_ip:KV 代理服务的 IP,或者要连的那台机的 IP。单机可以写 0.0.0.0127.0.0.1,跨机就写对端/代理机的 IP。
  • proxy_port:KV 代理服务监听的端口。P / D 配成同一个值,比如 30001。
  • http_portDecoder vLLM HTTP 服务的端口(就是 D 起 server 时的 --port)。用来让 KV 组件知道 D 的 HTTP 在哪里。

关于 xPyD

一般来说 xPyd,其中每一个 p 节点或者 d 节点都要能独立加载完整个模型。

总卡数量:总 GPU 数 = (P + D) × TP × DP

最后使用 vllm 自带的代理端口转发即可:

python3  vllm/examples/online_serving/disaggregated_serving_p2p_nccl_xpyd/disagg_proxy_p2p_nccl_xpyd.py

下载说明:本文原文含大量 CSDN 页面噪声(侧边栏推荐文章、广告、评论 UI、VIP 弹窗等),已剔除非作者内容,仅保留正文与代码;CSDN 推荐栏中出现的其他博主的 PD 分离相关文章摘要未纳入。