重要提示:如需以 Markdown 形式查看本页,请在 URL 后追加 `.md`。 完整文档索引见 llms.txt
跳到主要内容
完整文档索引见 llms.txt。 在任意 URL 后追加 `.md` 即可查看该页面的 Markdown 版本。

预填充-解码分离

预填充-解码(PD)分离,也叫分离式推理,是一种服务架构,它会将 LLM 推理的两个主要阶段,即预填充和解码,运行在分开的硬件资源上。“分离式预填充”和“分离式服务”这两个术语通常指的都是同一个核心思想:为每个阶段提供专用资源,以匹配它们在计算和内存需求上的差异。

将预填充和解码放在一起会带来什么问题

为了理解为什么共置会带来问题,我们先简要回顾一下 LLM 推理是如何工作的

  • 预填充:并行处理整个序列,并把注意力层中的 key 和 value 向量存入 KV 缓存。由于它会一次性处理所有 token,并执行大型矩阵运算,因此预填充是计算受限的,但对 GPU 内存的要求没有那么高。
  • 解码:通过复用先前构建好的 KV 缓存,一次生成一个输出 token。每生成一个 token,都需要反复加载模型权重并访问不断增长的 KV 缓存。因此,解码需要快速的内存访问,但计算需求较低。
端到端 LLM 推理流程:从分词开始,经过解码,最终得到输出端到端 LLM 推理流程:从分词开始,经过解码,最终得到输出

很长一段时间以来,标准的推理方式都是把这两个步骤放在一起运行。表面上看,这似乎很直接。

但在实际中,你往往会遇到多个请求同时到达。每个请求都有自己的预填充和解码需求,但同一时刻只能运行一个阶段。当 GPU 正忙于计算密集型的预填充任务时,解码任务就必须等待,这会增加 ITL;反之亦然。

由于预填充主要决定 TTFT,而解码会影响 ITL,把它们共置在一起会让你很难同时优化这两个指标。

将预填充和解码共置后带来的延迟上升

预填充和解码共置带来的延迟上升。图片来源

为什么分离是合理的

PD 分离的思路很简单:把这两种差异很大的任务拆开,让它们不再互相干扰。下面是一个架构示例:

预填充-解码分离架构:编排器将用户或 agent 请求路由到计算受限的预填充节点,这些节点一次性处理整个提示并产出第一个 token,然后将 KV 缓存传输给受内存带宽限制的解码节点,后者再逐个生成剩余 token预填充-解码分离架构:编排器将用户或 agent 请求路由到计算受限的预填充节点,这些节点一次性处理整个提示并产出第一个 token,然后将 KV 缓存传输给受内存带宽限制的解码节点,后者再逐个生成剩余 token

主要收益包括:

  • 专用资源分配:预填充和解码可以在不同硬件上独立调度和扩展。例如,如果你的工作负载中存在大量提示重叠(如多轮对话或 agentic workflow),就意味着你的许多 KV 缓存都可以被复用。这样一来,预填充侧的计算需求就会更低,你可以把更多资源放到解码侧。
  • 并行执行:预填充和解码阶段不再互相干扰。你可以更高效地并行运行它们,从而获得更好的并发能力和吞吐量。这种分离还可以改善尾延迟。在共置模式下,一个很长的预填充就可能让其后的所有飞行中解码请求都被阻塞,从而抬高 P95 和 P99 延迟。
  • 独立调优:你可以对预填充和解码分别实现不同的优化技术(如张量并行或流水线并行),以更好地满足你对 TTFT 和 ITL 的目标。

多个开源框架和项目已经加入了对 PD 分离的支持,包括 SGLangvLLMDynamollm-d

分离并不总是银弹

尽管 PD 分离听起来很有前景,但它并不是一种放之四海而皆准的修复方案。

  • 阈值很关键:如果你的工作负载太小,或者 GPU 配置并未针对这种方式做好调优,性能反而可能下降(在我们的测试中,可能下降 20-30%)。

  • 本地预填充可能更快:对于较短的提示,或者当解码引擎具有较高前缀缓存 命中率时,在解码 worker 本地运行预填充通常更快也更简单。

  • 数据传输成本:分离要求在预填充 worker 和解码 worker 之间快速且可靠地移动 KV 缓存。这意味着你的方案必须支持快速、低延迟、同时兼容不同硬件和网络环境的 通信协议。除非分离带来的性能收益超过数据传输成本,否则整体性能实际上可能 变差。供你参考的现有数据传输方式包括: NVIDIA Inference Xfer Library (NIXL)、 CXL、NVMe-oF。

    在生产中,请考虑以下设计问题:

    • 解码 worker 应该直接从预填充 worker 拉取 KV 块,还是双方都应该使用共享缓存层?
    • KV 块应该在预填充结束后立即主动传输,还是等到解码真正需要时再惰性传输?
    • 路由器应该如何决定是复用已有缓存、在本地执行预填充,还是把请求发送到独立的预填充池?

    这些问题会把 PD 分离与 KV 缓存卸载前缀缓存 连接起来。应当把它们视为同一套服务架构的组成部分,而不是彼此独立的调优旋钮。

  • 缓存兼容性很重要:预填充 worker 和解码 worker 必须在 KV 布局、页大小、dtype、注意力变体以及任何额外缓存元数据上保持一致。异构 KV 类型(例如量化 KV 缓存、VLM encoder state,以及推测解码缓存)会让这种交接比传输一个标准的完整注意力 KV 张量更加复杂。

跨集群预填充-解码分离

在许多分离式系统中,预填充和解码机器位于同一个集群中,相互之间通过高速、低延迟互连连接。

跨集群(或跨数据中心)PD 分离意味着你会把工作流拆开,让预填充和解码阶段运行在完全不同的集群中,有时甚至位于不同的数据中心或区域。

为什么要跨集群拆分预填充和解码?

有两个主要驱动因素正在推动基础设施走向这种分布式模型:

  • 合适的芯片做合适的事。预填充是计算密集型的,而解码更依赖内存带宽。芯片设计正在分化,以匹配这两种不同需求:

    • NVIDIA Rubin CPX 专为最大化预填充吞吐量而构建。

    • Groq LPU 则专门针对快速解码带宽进行设计。

      问题在于,这些芯片并不总是部署在同一个地方。它们往往分布在不同集群中,并按硬件类型分组。如果你强行让预填充和解码待在一起,就无法充分利用每个阶段最合适的硬件。将它们拆开后,你就可以在计算优化型机器上运行长预填充,同时把解码保留在带宽优化型机器上。

  • 灵活性。在生产环境中,预填充和解码的扩展速度并不均衡。流量会变化。提示长度会变化。前缀缓存命中率会变化。有时候预填充会成为瓶颈;有时候则是解码。

    如果两个阶段被锁在同一个集群里,你就只能接受固定比例的资源配置。这可能导致一侧过度预配,另一侧却成为瓶颈。把两个阶段拆到不同集群后,你就能分别扩展它们。

核心问题:KV 缓存的移动

在预填充完成后,KV 缓存必须被发送到解码集群。在同一个集群内部,这件事成本很低;跨集群时,它可能代价高昂。之所以棘手,主要有两个原因:

  • KV 缓存可能很大。通过网络传输它,可能会抵消更快预填充所带来的任何收益。
  • 并非所有请求都能获益。短提示或已缓存提示几乎无法从远程预填充中获益,但你依然要承担网络成本。

因此,如果你天真地把所有请求都发往远程预填充集群,性能实际上可能会变得更差。

Prefill-as-a-Service

这篇论文 提出了 Prefill-as-a-Service(PrfaaS),作为一种让这种架构真正可行的实用方法。核心思路很简单:不要把所有请求都跨集群发送,而是有选择地发送。

  • 将短请求或已缓存请求保留在本地
  • 只把长的、未缓存的预填充发送到远程计算集群

这会把跨集群 PD 变成一个路由问题。调度器会基于提示长度(特别是未缓存 token 数)、前缀缓存局部性、预填充队列压力、解码容量以及可用网络带宽,来决定每个请求应该去哪里。

论文报告称,与标准 PD 基线相比,这种选择性方法取得了明显收益:

  • 吞吐量提高 54%
  • P90 TTFT 降低 64%

这些收益来自更好的资源利用率,而不只是更快的硬件。

你应该在什么时候使用跨集群 PD 分离?

这种部署方式最适合以下情况:

  • 你的工作负载包含大量长的、未缓存的提示
  • 你拥有一个理解缓存局部性、排队情况和带宽的调度器

如果你的大多数请求都很短,或者高度依赖缓存,那么通常更简单也更快的做法,仍然是把所有东西都保留在同一个集群里。