文章介绍用于 vLLM 预填充与解码分离架构的推送式 NIXL 连接器,将 KV 缓存传输协调移出关键路径。在搭载 EFA 的 EC2 P5en 基准测试中,首 token 时间最高改善 3 倍,每输出 token 时间改善约 30%。
vLLM 是一个面向大语言模型(LLM)的开源推理与服务引擎。LLM 推理分为两个不同的阶段:prefill 负责处理输入 prompt,并构建键值(KV)缓存;decode 则利用这份缓存,每次生成一个 token。这两个阶段在计算、内存和扩展方式上各有特点。Prefill-decode(P-D)分离将它们放到独立的引擎实例上运行,让每个阶段都能独立扩容和优化。这可以改善负载下的资源利用率、吞吐量和延迟。不过,这也意味着必须先把 KV 缓存从 prefill 实例传输到 decode 实例,才能开始生成 token。因此,高效的 KV 传输对首 token 延迟(time-to-first-token,TTFT)至关重要。
vLLM 实现这种传输的方式之一,是使用 NVIDIA Inference Xfer Library(NIXL)connector,通过支持远程直接内存访问(Remote Direct Memory Access,RDMA)的互连网络传输 KV block,例如 AWS Elastic Fabric Adapter(AWS EFA,即 AWS 服务器之间使用的低延迟 RDMA 网络)。在受支持的实例类型上,AWS EFA 支持 GPUDirect RDMA 和 Neuron Direct RDMA。因此,NIXL 可以直接在 GPU 或 AWS Trainium 设备内存之间传输 KV block,无须经过主机内存中转。NIXL connector 默认采用 pull 模式:prefill 实例完成 prompt 的 KV 缓存计算后,router 将源端元数据(包括 prefill block ID)转发给 decode 实例。接着,decode 实例分配目标 block,准备 NIXL 传输,并发起 NIXL READ,从 prefill 实例拉取 KV block。由于这些协调和准备步骤都在 prefill 结束后、decode 开始前依次执行,因此会拉长 TTFT 的关键路径,如下面的时间线所示。
图 1:pull 模式的 KV 传输时间线。router 转发、decode 端分配和 NIXL READ 在生成首个 token 的路径上串行执行。
本文介绍一种基于 push 的 NIXL connector,将 prefill 完成后的传输协调和准备工作移出关键路径。如果你正在使用 vLLM 运行 prefill/decode 分离推理,push 模式可以通过降低 TTFT 和 TPOT 来改善应用响应速度,尤其是在请求速率较高时。
我们在通过 EFA 互连的 Amazon Elastic Compute Cloud(Amazon EC2)P5en 实例上,以 1P1D 部署运行 Qwen3-32B 模型进行基准测试。与 pull 模式相比,push 模式的 TTFT 最多改善至约 3 倍,单个输出 token 的耗时(time-per-output-token,TPOT)最多降低 30%(参见性能数据部分)。实际结果取决于工作负载和配置。
我们最初在通过 EFA 互连的 AWS Trainium 实例集群上构建并验证了这一功能,随后将其作为不依赖特定加速器的功能贡献给 vLLM。本文展示的结果来自使用 EFA 的 Amazon EC2 P5en(GPU)集群,说明这些收益同样适用于 GPU。接下来,我们会介绍基于 push 的架构、传输流程、线程模型、性能结果,以及如何复现基准测试。
本节介绍基于 push 的设计,包括架构、传输协议,以及如何通过线程模型将 KV 传输移出关键路径。
基于 push 的传输之所以能改善 TTFT 和 decode 吞吐量,主要有两个原因:
最终效果是降低 TTFT,并提高 decode 实例的输出 token 吞吐量。
push connector 协调三个参与方:decode 实例提前注册目标 block,prefill 实例匹配每个请求并发起传输,专用后台线程负责执行 KV 写入,使其脱离引擎的前向计算循环。下图展示了这些部分如何协同工作。
图 2:基于 push 的 KV 传输架构,展示 decode 实例、prefill 实例和后台传输线程如何协调。
这一协议包含三个逻辑步骤。
注册(decode 端)。 当 decode 实例为需要远程 prefill 的请求分配本地 block 时,会准备一份注册信息,说明 prefill 实例应该将 KV 写到哪里:其中包含 decode 实例自身的身份信息,以及刚刚预留的 block 位置。在同一个引擎执行步内,worker 通过 NIXL 通知将这份注册信息发送给 prefill 实例。由于注册发生在分配时,connector 在 prefill 实例完成 prefill 之前就已经知道目标 block 的位置。
会合(prefill 端)。 decode 端的注册信息与生产端的 prefill 完成事件,可能以任意顺序到达,connector 会处理这两种情况。如果注册信息先到,生产端就暂存它,直到自身完成 prefill。如果 prefill 先完成,生产端则保留该请求的 block,直到注册信息到达。两个事件中后发生的那个会触发传输。connector 通过 ID 配对请求,匹配逻辑也考虑了重新发出的请求,能够正确关联被抢占或重试的请求。
传输(prefill 端)。 请求匹配成功后,生产端会与 decode 实例建立连接(如果连接尚不存在),然后发起 NIXL WRITE,将 KV block 直接复制到 decode 端已注册的内存中,并定位到每个目标 tensor-parallel rank。生产端和消费端可以使用不同的内部 block 布局。传输时,两端分别将 block 位置映射到各自的物理布局,因此允许两端使用不同的 block 大小。每次 WRITE 都附带完成通知,数据到达后,NIXL 会将通知送达 decode 实例。decode 端会统计这些通知,每个向其写入数据的生产端 rank 对应一条通知。只有收到全部预期通知后,它才会认为 KV 已接收完成,随后该请求进入 decode 阶段。
push connector 在每个 prefill worker 上使用一个专用的后台传输线程,将模型执行与 KV 传输准备工作分开。请求开始时,decode 主循环分配目标 KV block,并向 prefill 实例注册;与此同时,prefill 主循环计算 prompt 的 KV 缓存。目标注册信息和 prompt KV 都就绪后,prefill 主循环将 KV block 交给后台线程,然后迅速返回,继续执行模型计算。
后台线程负责建立或复用 NIXL 连接,将请求的 KV block ID 映射为内存描述符,把连续的 block 合并成更大的传输区域,并向 decode 实例提交 NIXL WRITE。NIXL 完成传输后,会向 decode 实例发送完成通知,后者随即开始生成 token。将描述符准备和传输提交移出延迟敏感的主循环,可以避免这些逐请求执行的工作拖慢模型计算。下图展示了单个请求在主循环与后台线程之间的交接过程。
图 3:基于 push 的线程模型中,单个请求在主循环与后台线程之间的交接。
下图汇总了前面介绍的 pull 和 push 传输流程,并突出展示了它们对 TTFT 的影响。虽然两种模式使用相同的 NIXL 和 EFA/RDMA 数据通路,但 pull 会在 prefill 完成后才进行元数据转发和 decode 端准备,而 push 则让 decode 端准备工作与 prefill 重叠执行。因此,push 将这些操作移出了 prefill 之后的关键路径,使 decode 可以更早开始。
图 4:pull 与 push 传输流程对比,重点展示 push 如何让 decode 端准备工作与 prefill 重叠执行。
这两种模式在路由上也存在取舍。pull 模式允许 router 在 prefill 完成后再选择 decode 实例,为负载感知路由和服务质量管理提供更大的灵活性。push 模式则提前选择 decode 实例,让目标端准备工作可以与 prefill 重叠执行。作为回报,在负载相当的情况下,prefill 实例可以更早释放 KV block,因为 prompt KV 和目标注册信息一旦都就绪,传输就会立即开始。
评估时,我们在通过 EFA 互连的 Amazon EC2 P5en 实例上,以 1P1D 部署运行 Qwen3-32B 模型。为了单独衡量 P→D 传输性能,我们在基准测试配置中使用 --no-enable-prefix-caching 禁用了前缀缓存。这一配置会让 decode 节点为每个请求都从 prefill 节点获取完整的 KV 缓存。否则,存在重叠内容的 prompt 可能复用 decode 节点的本地缓存,绕过传输,从而导致对比结果失真。
下图对比了 push 与 pull KV 传输的 TTFT。我们测试了 1k–16k 的输入长度,以及每秒 1–8 次查询(QPS)的请求速率。在低负载、短 prompt 下,两种模式的表现几乎相同。随着 QPS 和输入长度增加,pull 模式的表现明显恶化:在 8 QPS、16k 输入长度时,TTFT 达到约 1,744 ms。push 模式则维持在低得多的约 540 ms,降至约三分之一。差距出现的原因是,pull 模式在 decode worker 的引擎循环内同步、内联地提交 NIXL 描述符,这项工作会阻塞 token 生成。push 模式将其移到后台线程,避开了关键路径。
图 5:不同输入长度和请求速率下,push 与 pull KV 传输的 TTFT 对比(1P1D 拓扑,输出 128 个 token,decode 节点禁用前缀缓存)。
下图展示了相同条件下的 TPOT,变化趋势与 TTFT 一致。轻负载下,push 和 pull 比较接近,约为 5.6–5.8 ms。随着负载和输入长度增加,在 4–8 QPS、16k 输入长度时,pull 上升到约 10.4 ms,而 push 保持在约 7 ms。原因在于,push 将描述符准备和传输提交工作从 decode worker 中移走,让 token 生成能够持续获得所需的计算资源,因此即使在高并发、长上下文下,每个 token 的延迟也能保持平稳。
图 6:不同输入长度和请求速率下,push 与 pull KV 传输的 TPOT 对比(1P1D 拓扑,输出 128 个 token,decode 节点禁用前缀缓存)。
如果想复现我们的 1P1D 基准测试配置,即在通过 EFA 互连的 Amazon EC2 P5en 实例上,分别运行一个 prefill 节点和一个 decode 节点,请参阅《复现我们的结果》指南中的详细说明。该指南涵盖环境设置、启动 vLLM prefill 和 decode 服务器、运行基准测试,以及收集结果。这项功能也可以在其他受支持的配置上运行,但性能结果会随硬件、模型、工作负载和网络配置而变化。
本文展示了基于 push 的 KV connector 如何让 prefill 计算与 decode 端的 block 分配和注册重叠执行,将 prefill 完成后的 router 往返交互和 decode 端传输准备工作移出关键路径。这降低了 TTFT 和 TPOT,尤其是在请求速率较高时。在通过 EFA 互连的 Amazon EC2 P5en 实例上,以 1P1D 部署运行 Qwen3-32B 的基准测试中,与 pull 模式相比,push 模式的 TTFT 最多改善至约 3 倍,TPOT 最多降低 30%。实际结果会随工作负载和配置而变化。要使用 push 模式,请将 prefill 和 decode 服务器都配置为使用 NixlPushConnector,替代 NixlConnector。
如果可以接受提前选择 decode 节点,并且优先考虑降低 TTFT,就选择基于 push 的 connector。对于需要双向 KV 传输,或者为了实现负载感知路由而需要等到 prefill 完成后再选择 decode 实例的工作负载,基于 pull 的 connector 仍然更合适。vLLM 同时支持基于 push 和 pull 的 KV connector,你可以选择最适合自身部署的方式。
作为后续改进,我们正在探索将 NIXL 后台线程模型引入 pull 模式的 connector,把 NIXL 准备工作移出关键路径,让 pull 模式的工作负载也能受益于同样的优化。我们已经提交了一个 pull request 来探索这一方案。
vLLM 分离式服务指南
push 模式 NIXL KV connector
感谢 vLLM 社区对维护这项功能的支持。