<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Pd on Wilson Wu</title><link>https://wilsonwu.me/zh/tags/pd/</link><description>Recent content in Pd on Wilson Wu</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Mon, 29 Sep 2025 00:00:00 -0400</lastBuildDate><atom:link href="https://wilsonwu.me/zh/tags/pd/index.xml" rel="self" type="application/rss+xml"/><item><title>玩转大语言模型：基于 vLLM 框架的大模型推理优化实现参数 / 数据（P/D）分离</title><link>https://wilsonwu.me/zh/blog/2025/vllm-pd-separation/</link><pubDate>Mon, 29 Sep 2025 00:00:00 -0400</pubDate><guid>https://wilsonwu.me/zh/blog/2025/vllm-pd-separation/</guid><description>随着大语言模型规模的不断增长，推理部署面临显存紧张与并发受限的挑战。本文介绍如何借助 vLLM 框架，通过 参数（Parameter）/ 数据（Data）分离（P/D Separation）优化推理性能。我们将从原理、架构思路到配置与代码实例逐步展开，展示如何让参数长期驻留在专用 GPU，而输入序列与 KV Cache 等动态数据独立管理，从而提升吞吐率、降低成本并增强系统扩展性。</description><content:encoded><![CDATA[<p>大模型在推理部署时，往往存在<strong>显存瓶颈</strong>：</p>
<ul>
<li>模型参数（Parameters, P）动辄上百 GB，需要长期驻留显存。</li>
<li>输入/输出数据（Data, D）则随请求动态变化，但往往和参数耦合在同一设备上，导致显存占用不均衡，扩展性受限。</li>
</ul>
<p>为了解决这一问题，可以借助 <strong>vLLM 框架</strong>实现<strong>参数 / 数据（P/D）分离</strong>，提升推理系统的灵活性和吞吐。</p>
<h2 id="大模型推理的资源瓶颈">大模型推理的资源瓶颈</h2>
<p>以一个 70B 规模的 Transformer 模型为例：</p>
<ul>
<li>参数权重（FP16 存储）约需 <strong>140GB 显存</strong>；</li>
<li>每次请求输入的序列数据、KV Cache 会消耗额外显存，并随 batch size 增长而急剧膨胀。</li>
</ul>
<p>如果不加区分地将 P 与 D 放在同一块 GPU：</p>
<ol>
<li>参数长期驻留，挤压了用于动态数据的显存；</li>
<li>多实例并发时，数据显存不足，限制了吞吐。</li>
</ol>
<p>因此，在分布式推理系统中，业界逐渐采用 <strong>参数与数据分离（P/D Separation）</strong> 的架构思路。</p>
<h2 id="vllm-简介">vLLM 简介</h2>
<p><a href="https://vllm.ai/">vLLM</a> 是一个高性能的大模型推理引擎，核心优势包括：</p>
<ul>
<li><strong>PagedAttention</strong>：高效管理 KV Cache，支持大批量并发；</li>
<li><strong>高吞吐率</strong>：相较于 Hugging Face Transformers 推理，吞吐提升数倍；</li>
<li><strong>灵活的分布式支持</strong>：可结合 DeepSpeed、Megatron 等方案，支持参数/数据分布式存储与调度。</li>
</ul>
<p>vLLM 的模块化架构，使其天然适合实现 <strong>P/D 分离</strong>。</p>
<h2 id="pd-分离的实现思路">P/D 分离的实现思路</h2>
<p>在 vLLM 中，推理流程大致分为两个部分：</p>
<ol>
<li>
<p><strong>参数侧（P）</strong></p>
<ul>
<li>模型权重加载与存放；</li>
<li>可通过 <strong>ZeRO-3 / Tensor Parallel</strong> 等策略将参数分布在多 GPU 节点上；</li>
<li>参数在整个推理生命周期中保持常驻，不随请求波动。</li>
</ul>
</li>
<li>
<p><strong>数据侧（D）</strong></p>
<ul>
<li>动态输入序列、KV Cache、采样状态等；</li>
<li>可使用 <strong>PagedAttention</strong> 管理显存分页；</li>
<li>支持把部分 KV Cache 放到 CPU/Host 内存，再动态迁移到 GPU。</li>
</ul>
</li>
</ol>
<h2 id="核心机制">核心机制</h2>
<p>其核心机制是显存资源隔离：</p>
<ul>
<li><strong>权重（P）</strong> 固定驻留在专门的 GPU（参数节点）；</li>
<li><strong>数据（D）</strong> 可以按需分配到其他 GPU（数据节点），甚至利用 CPU 内存 / NVMe。</li>
</ul>
<p>这样：</p>
<ul>
<li>参数显存不与动态数据抢占空间；</li>
<li>数据可以根据请求动态扩展，提升并发能力。</li>
</ul>
<h2 id="在-vllm-中的实现路径">在 vLLM 中的实现路径</h2>
<p>实现 P/D 分离的常见方法：</p>
<ol>
<li>
<p><strong>参数分片（Parameter Sharding）</strong></p>
<ul>
<li>借助 vLLM 对 Megatron-LM / DeepSpeed 的支持，将模型参数按张量维度切分到多 GPU；</li>
<li>仅在推理计算时聚合，不需要在每个设备上完整存放参数。</li>
</ul>
</li>
<li>
<p><strong>KV Cache 分离（Data Sharding）</strong></p>
<ul>
<li>通过 vLLM 的 <strong>PagedAttention</strong>，可以把 KV Cache 存在独立的 GPU 或 Host 内存；</li>
<li>在推理过程中按需调页，类似操作系统的虚拟内存机制。</li>
</ul>
</li>
<li>
<p><strong>Pipeline 并行化</strong></p>
<ul>
<li>将模型计算划分为不同阶段，前几层专门占用参数节点，后几层在数据节点完成；</li>
<li>在流水线中，数据逐层传递，而参数保持分片驻留。</li>
</ul>
</li>
</ol>
<h2 id="基于-vllm-的-pd-分离实战">基于 vLLM 的 P/D 分离实战</h2>
<p>下面给出两部分内容：</p>
<ul>
<li><strong>配置示例</strong>：如何在多 GPU 环境下指定参数和数据分布；</li>
<li><strong>代码示例</strong>：如何加载模型并运行推理，展示参数常驻、数据动态分配。</li>
</ul>
<h2 id="配置示例">配置示例</h2>
<p>假设我们有 4 张 GPU：</p>
<ul>
<li><strong>GPU 0,1</strong>：专门存放模型参数（P）；</li>
<li><strong>GPU 2,3</strong>：主要承载 KV Cache 等动态数据（D）。</li>
</ul>
<p>我们可以在启动 vLLM 时设置：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>CUDA_VISIBLE_DEVICES<span style="color:#f92672">=</span>0,1,2,3 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>python -m vllm.entrypoints.api_server <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --model /path/to/llama-2-70b <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --tensor-parallel-size <span style="color:#ae81ff">2</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --gpu-memory-utilization 0.85 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --enable-chunked-prefill <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --enforce-eager <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --kv-cache-dtype fp16 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>    --max-num-batched-tokens <span style="color:#ae81ff">4096</span>
</span></span></code></pre></div><p>这里的关键点：</p>
<ul>
<li><code>--tensor-parallel-size 2</code>：只在 GPU 0,1 上做参数分片存放；</li>
<li><code>--gpu-memory-utilization</code>：控制参数占用的比例，留出余量给数据；</li>
<li><code>--kv-cache-dtype fp16</code>：降低 KV Cache 精度，减少数据显存占用；</li>
<li><strong>vLLM 内部的 PagedAttention</strong> 会自动把 KV Cache 分配到 GPU 2,3，如果溢出则回退到 CPU 内存。</li>
</ul>
<h2 id="代码示例">代码示例</h2>
<p>下面是一个 Python 调用示例：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">from</span> vllm <span style="color:#f92672">import</span> LLM, SamplingParams
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># -------- 参数（P）分离 --------</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># tensor_parallel_size=2 表示参数分布在前两张 GPU（0,1）</span>
</span></span><span style="display:flex;"><span>llm <span style="color:#f92672">=</span> LLM(
</span></span><span style="display:flex;"><span>    model<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;/path/to/llama-2-70b&#34;</span>,
</span></span><span style="display:flex;"><span>    tensor_parallel_size<span style="color:#f92672">=</span><span style="color:#ae81ff">2</span>,
</span></span><span style="display:flex;"><span>    gpu_memory_utilization<span style="color:#f92672">=</span><span style="color:#ae81ff">0.85</span>,
</span></span><span style="display:flex;"><span>    enforce_eager<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>,       <span style="color:#75715e"># 避免显存碎片</span>
</span></span><span style="display:flex;"><span>    enable_chunked_prefill<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>,
</span></span><span style="display:flex;"><span>    kv_cache_dtype<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;fp16&#34;</span>     <span style="color:#75715e"># 数据（D）使用更轻量的缓存精度</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># -------- 数据（D）分离 --------</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># PagedAttention 会自动管理 KV Cache 的存放位置（GPU2, 3, 甚至 CPU 内存）</span>
</span></span><span style="display:flex;"><span>sampling_params <span style="color:#f92672">=</span> SamplingParams(
</span></span><span style="display:flex;"><span>    temperature<span style="color:#f92672">=</span><span style="color:#ae81ff">0.8</span>,
</span></span><span style="display:flex;"><span>    top_p<span style="color:#f92672">=</span><span style="color:#ae81ff">0.95</span>,
</span></span><span style="display:flex;"><span>    max_tokens<span style="color:#f92672">=</span><span style="color:#ae81ff">256</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>prompt <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;解释一下通过 vLLM 实现 P/D 分离的好处。&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 运行推理</span>
</span></span><span style="display:flex;"><span>outputs <span style="color:#f92672">=</span> llm<span style="color:#f92672">.</span>generate([prompt], sampling_params)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;模型输出：&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> output <span style="color:#f92672">in</span> outputs:
</span></span><span style="display:flex;"><span>    print(output<span style="color:#f92672">.</span>outputs[<span style="color:#ae81ff">0</span>]<span style="color:#f92672">.</span>text)
</span></span></code></pre></div><h2 id="验证方式">验证方式</h2>
<p>运行时可以用 <code>nvidia-smi</code> 观察：</p>
<ul>
<li>GPU 0,1 显存几乎恒定，主要承载参数；</li>
<li>GPU 2,3 的显存随请求数量/序列长度波动，主要承载 KV Cache；</li>
<li>如果输入过大，KV Cache 会溢出到 CPU 内存，但仍可继续推理。</li>
</ul>
<h2 id="实际效果与收益">实际效果与收益</h2>
<p>通过 P/D 分离，可以得到以下好处：</p>
<ul>
<li><strong>提升 GPU 利用率</strong>：参数和数据分工明确，避免显存冲突；</li>
<li><strong>支持更大 batch size</strong>：数据显存可扩展，吞吐率提升显著；</li>
<li><strong>降低成本</strong>：参数可驻留在高端 GPU，而数据可放到中低端 GPU 或 CPU 内存，形成分层存储；</li>
<li><strong>灵活性</strong>：适配多种硬件资源组合（A100 + H100, GPU + CPU, GPU + NVMe）。</li>
</ul>
<p>在实践中，结合 vLLM 的 PagedAttention，P/D 分离的效果尤为明显，可让大模型推理在同样硬件下实现 <strong>数倍并发提升</strong>。</p>
<h2 id="总结">总结</h2>
<p>vLLM 框架凭借其高效的 KV Cache 管理和分布式能力，为 <strong>大模型推理的 P/D 分离</strong> 提供了坚实基础。
通过将 <strong>参数（P）</strong> 和 <strong>数据（D）</strong> 解耦，可以更好地利用硬件资源，提升推理性能与扩展性。</p>
<p>未来，随着 <strong>混合精度存储、分层缓存（GPU+CPU+NVMe）</strong> 的发展，P/D 分离架构将逐渐成为大模型推理的主流范式。</p>
]]></content:encoded></item></channel></rss>