欢迎光临
我们一直在努力

PD 分离架构:将 LLM 的预填充与生成阶段放在不同显卡上跑的收益分析

大型语言模型(LLM)的推理过程通常分为两个截然不同的计算阶段:预填充(Pre-fill)和生成(Decode)。这两个阶段的计算和资源需求特性存在巨大差异,如果在同一块GPU上混合执行,往往会导致资源利用率低下,尤其是在高并发的服务环境中。

PD分离架构(Pre-fill/Decode Separation)旨在将这两个阶段解耦,并将其调度到最适合的硬件资源上运行,特别是在多显卡环境中,可以分别部署在不同的GPU上,以实现资源匹配与性能优化。

1. Pre-fill(预填充)阶段的特性与需求

特性:
* 处理用户的输入提示(Prompt)。
* 计算量与输入序列长度成正比,通常是带宽密集型(Bandwidth-bound)。
* 需要一次性计算所有输入Token的KV Cache。
* 通常以较大的“逻辑批次”(即输入序列长度)运行。

理想硬件: 拥有高显存带宽(HBM)的GPU,能够快速处理大批量数据写入KV Cache。

2. Decode(生成)阶段的特性与需求

特性:
* 迭代地生成单个或少数几个新Token。
* 计算量与模型参数量成正比,通常是计算密集型(Compute-bound),且延迟敏感。
* 批次大小通常较小(高并发下除外,但单次迭代的Token数通常为1)。

理想硬件: 拥有高核心频率、为低延迟计算优化的GPU。

3. 不同显卡分离运行的收益分析

当我们将Pre-fill任务分配给GPU A,将Decode任务分配给GPU B时,可以获得以下核心收益:

3.1 提高资源利用率与吞吐量

在传统的单GPU架构中,一个请求必须等待其Pre-fill完成后,才能进行Decode。而当Pre-fill和Decode分离后,GPU A(Pre-fill)可以并行处理多个新进入系统的长Prompt,快速完成KV Cache的构建;与此同时,GPU B(Decode)可以持续地处理已完成Pre-fill请求的Token生成任务。

这种并行化消除了阶段间的等待时间,使得两块GPU都能达到更高的平均利用率,从而显著提升系统的总吞吐量(Tokens/s)。

3.2 优化用户感知延迟

LLM推理的用户体验主要取决于首个Token生成时间(TTFT, Time-To-First-Token)后续Token生成时间(TPOT, Time-Per-Output-Token)

  • TTFT 主要受限于Pre-fill阶段。通过专用GPU A快速处理Pre-fill,可以缩短TTFT。
  • TPOT 主要受限于Decode阶段。通过专用GPU B运行Decode,可以确保该阶段不受GPU A上Pre-fill的突发高负载干扰,保证低且稳定的TPOT,从而提升用户等待体验。

4. 架构实现:调度器逻辑示例

实现PD分离的关键在于一个智能的调度器,如vLLM等现代推理框架所采用的机制。调度器需要维护两个独立的任务队列,并将任务动态地分配给Pre-fill GPU集群和Decode GPU集群。

下面是一个简化的Python伪代码,展示了任务如何在两个分离的“资源”间流转:

import time
import threading
from queue import Queue

# 定义任务队列
prefill_queue = Queue()  # 待处理的Prompt
decode_queue = Queue()   # 已完成Pre-fill,待生成的请求

class Request:
    def __init__(self, prompt_len, req_id):
        self.prompt_len = prompt_len
        self.req_id = req_id
        self.k_v_cache = None

# 模拟 Pre-fill GPU (GPU A) Worker
def prefill_worker(gpu_id):
    while True:
        request = prefill_queue.get()
        if request is None: break

        # 模拟 Pre-fill 过程:耗时取决于Prompt长度 (带宽密集)
        process_time = request.prompt_len * 0.005 
        time.sleep(process_time)

        # 假设生成了KV Cache
        request.k_v_cache = f"Cache_{request.req_id}"
        print(f"[GPU {gpu_id} P] Request {request.req_id}: Prefill completed in {process_time:.3f}s")

        # 任务转移到 Decode 队列
        decode_queue.put(request)
        prefill_queue.task_done()

# 模拟 Decode GPU (GPU B) Worker
def decode_worker(gpu_id):
    while True:
        request = decode_queue.get()
        if request is None: break

        # 模拟 Decode 过程:低延迟,连续生成10个Token (计算密集)
        for token_idx in range(1, 11):
            # 假设每个Token耗时固定,且稳定
            token_time = 0.02
            time.sleep(token_time)
            print(f"[GPU {gpu_id} D] Request {request.req_id}: Generated Token {token_idx}")

        decode_queue.task_done()

# --- 运行模拟 ---
if __name__ == '__main__':
    # 启动 Pre-fill Worker (可以启动多个,体现并行处理长Prompt)
    p_thread = threading.Thread(target=prefill_worker, args=(0,))

    # 启动 Decode Worker (可以启动多个,保证低延迟生成)
    d_thread = threading.Thread(target=decode_worker, args=(1,))

    p_thread.start()
    d_thread.start()

    # 提交任务 (长Prompt任务)
    requests = [
        Request(prompt_len=200, req_id=101),
        Request(prompt_len=500, req_id=102),
        Request(prompt_len=300, req_id=103),
    ]

    for req in requests:
        prefill_queue.put(req)

    # 等待所有任务完成
    prefill_queue.join()
    decode_queue.join()

    # 停止Worker
    prefill_queue.put(None)
    decode_queue.put(None)
    p_thread.join()
    d_thread.join()

总结

PD分离架构是提高LLM推理服务效率的关键手段之一。通过将带宽敏感的Pre-fill阶段与延迟敏感的Decode阶段分配给不同的、优化的硬件资源(不同显卡),我们可以实现更精细化的资源调度,最终达到更高的系统吞吐量和更稳定的低延迟用户体验。这种架构对于构建高并发、低延迟的LLM生产环境至关重要。

【本站文章皆为原创,未经允许不得转载】:汤不热吧 » PD 分离架构:将 LLM 的预填充与生成阶段放在不同显卡上跑的收益分析
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址