通过在预填充与解码节点间双向传输 KV 缓存,vLLM 可复用多轮对话中已计算的上下文。在配备 EFA 的 EC2 P5en 实例上,Qwen3-32B 和 GLM-5-FP8 的首字响应分别获得最高 1.6 倍和 3.3 倍加速。
多轮 Agent 工作负载会随着 Agent 推理、调用工具、整合先前结果,不断积累上下文。每一轮交互中,客户端都会重新发送对话历史,包括系统指令、之前的 prompt、工具结果和模型响应,导致持续增长的上下文被反复处理。在 Prefill-decode(P-D)分离架构中,每个请求都会分配给两类专用节点:prefill 节点处理 prompt 并构建键值(KV)cache,随后 decode 节点获取这些 KV 块并生成响应。
然而,传统的分离式推理服务只沿一个方向传输 KV cache:从 prefill 节点传到 decode 节点。这种单向流动会给多轮工作负载带来两方面的低效。首先,生成响应后,decode 节点同时持有 prompt 和新生成 token 的 KV cache 条目。处理下一轮请求时,prefill 节点无法获取这些条目,因此必须重新计算上一轮响应 token 的 KV 投影。其次,decode 节点维护着会话状态,而 prefill 节点没有,这使得活跃会话在 prefill 节点上的 cache 更容易被逐出。一旦 cache 完全未命中,prefill 节点就必须为整个对话历史重新计算 KV 投影,包括系统指令、之前的用户请求、工具结果和模型生成的响应。随着对话增长,这些重复工作会消耗更多加速器算力,并增加首 token 延迟(TTFT)。
构建对话助手和 Agent 应用时,交互历史越长,重复的 prefill 计算就越可能增加延迟、消耗加速器算力。本文将介绍,双向 KV cache 传输如何让 prefill 和 decode 节点跨轮次复用已经计算过的上下文,从而提高加速器利用率,并帮助应用在对话不断增长时维持较低的 TTFT。
在使用 Amazon Elastic Compute Cloud(Amazon EC2)P5en 实例的 1P1D 部署上,我们进行了多轮基准测试。双向 KV 传输使 Qwen3-32B 的 TTFT 最多缩短至原来的约 1/1.6,使 zai-org/GLM-5-FP8 的 TTFT 最多缩短至原来的约 1/3.3,具体见性能数据部分。结果会随工作负载和配置而变化。这些实例通过 AWS Elastic Fabric Adapter(AWS EFA)连接。AWS EFA 是 AWS 服务器之间使用的低延迟远程直接内存访问(RDMA)网络。在支持的实例类型上,AWS EFA 支持 GPUDirect RDMA 和 Neuron Direct RDMA,可直接在 prefill 和 decode 节点的 GPU 或 AWS Trainium 设备内存之间传输 KV 块。
我们最初在通过 EFA 连接的 AWS Trainium 实例集群上构建并验证了这项功能,随后将其作为一项不依赖特定加速器的能力贡献给 vLLM。本文展示的结果来自配备 EFA 的 Amazon EC2 P5en GPU 集群,说明这项功能同样能让 GPU 受益。本文将介绍双向 KV 传输架构、router 元数据流、KV 块保留与重计算控制、性能结果,以及如何复现基准测试。
双向 KV 传输是一种让 decode 节点在后续对话轮次中,将先前计算好的 KV 返回给 prefill 节点的机制。它避免了 prefill 节点对共享上下文的重复计算,并通过可配置的 KV 重计算阈值控制复用行为。它能缓解前面提到的两种重复计算问题。要实现这一点,KV 必须在生成它的轮次结束后继续保留,并在能够感知 KV cache 的 router 协调下反向传输。
我们扩展了 NVIDIA Inference Xfer Library(NIXL)KV connector,让 prefill 节点也能使用远端 KV 块,类似于 decode 节点原本就能使用 prefill 节点提供的 KV 块。这并不是一个简单的改动,因为两类节点在架构上有所不同:prefill 节点为新 prompt token 计算 KV 表示,可以提供完整的 prompt KV 块;decode 节点不具备生成完整 KV 表示的能力,或者无法高效完成这项工作,只能提供已经缓存的 KV 块。因此,prefill 和 decode 节点仍各自承担不同职责,并使用各自的逻辑计算远端和本地 KV 块的元数据。不过,实际的 KV 传输机制,也就是 NIXL READ,在 P→D 和 D→P 两个方向上是通用的。
下图先用蓝色展示现有流程。router 将请求发送给 prefill 节点,prefill 节点计算 prompt KV 并返回块元数据,router 再将请求和元数据转发给 decode 节点,执行标准的 P→D NIXL READ。带编号的红色路径展示了双向 KV 传输新增的步骤:(1)decode 节点在生成结束后保留符合条件的 KV 块;(2)通过流式响应返回这些块的元数据;(3)router 缓存这些元数据,供下一次 prefill 请求使用。随后,prefill 节点会(4)先匹配本地 cache,再检查远端可复用的 KV;(5)当可复用的 token 数量达到阈值时,将异步接收操作加入队列;(6)执行 D→P NIXL READ,然后计算剩余的后缀部分。
图 1:标准的 prefill 到 decode 流程(蓝色),以及双向 KV 传输新增的流程(红色)
vLLM 的 connector 端已经支持双向 KV 传输,而要协调不同对话轮次之间的 KV 复用,还需要一个能够感知 KV cache 的 router。vLLM GitHub 仓库提供了一个示例 proxy,实现了本文使用的流程,你可以根据自己的环境进行调整。
这个能够感知 KV cache 的 router,是位于客户端、prefill 节点和 decode 节点之间的轻量级、有状态控制平面组件。它在内存中维护客户端提供的 conversation_id 与 decode 节点 kv_transfer_params 之间的映射。router 只存储块位置元数据,不存储 KV tensor;这些元数据来自响应流,无须单独查询 decode 节点。
下图展示了 router 如何跨对话轮次管理 KV 元数据。第一轮中,cache 查询未命中,请求走标准的 P→D 流程。随后,router 从响应流中保存 decode 节点的元数据。后续轮次中,router 使用 conversation_id 获取这些元数据,让 prefill 节点能够复用 decode 节点上的 KV。每次生成结束后,它都会刷新元数据,供下一轮使用。
图 2:能够感知 KV cache 的 router 如何跨对话轮次传递元数据
通常,decode 实例会在生成结束后立即释放请求占用的块。启用双向 KV 传输模式后,它会将这些块保留一段可配置的时间(decoder_kv_blocks_ttl,默认 480 秒),并发布它们的位置和过期时间。这样,对话的 KV,包括 prompt 前缀和生成的响应,就能保留下来供下一轮使用。如果下一轮请求在存活时间(TTL)到期前到达,prefill 节点就会复用这些 KV;如果到达时已经过期,则回退到正常的 prefill 流程。由于这个 TTL 是固定的,不会续期,因此需要根据预期的轮次间隔进行调整,在 KV 复用和加速器内存占用之间取得平衡。一个顺理成章的后续改进,是将通过心跳续期的租约机制扩展到 decode 端。这样,decode 实例就能在对话进入空闲状态时立即回收块,而不必等到固定计时器结束。
复用 KV 并不总比本地重计算更快。如果可复用的前缀很短,互连通信和协调的开销可能超过重新计算这些 token 所需的时间。kv_recompute_threshold 设置用于控制这一决策,默认值为 64 个 token。当需要计算的 token 数量低于阈值时,prefill 节点执行重计算;达到或超过阈值时,则从 decode 节点拉取 KV。部署时可以根据模型计算时间、KV 大小和互连性能调整这个值。
评估中,我们使用 Qwen3-32B 和 GLM-5-FP8 模型,在通过 EFA 连接的 Amazon EC2 P5en 实例上进行 1P1D 部署。为了模拟 prefill 节点已经逐出本地 cache,或者 router 选中了另一个不持有该对话 KV 块的 prefill 节点的情况,我们通过 --no-enable-prefix-caching 禁用了 prefill 节点的前缀缓存。
两个模型的结果都表明,启用双向 KV 传输可以减少多轮对话中的 prefill 计算,而且收益通常会随着 prompt 长度增加而扩大。关闭这项功能时,prefill 实例每一轮都要重新计算不断增长的完整上下文,因此 TTFT 会快速上升。Qwen3-32B 的 TTFT 从 2k 时的 158 ms 上升到 24k 时的 778 ms;GLM-5-FP8 则从 2k 时的 500 ms 上升到 20k 时的 2,334 ms。启用后,系统会复用 decode 实例保留的 KV,无须重新计算,因此 TTFT 的增长平缓得多。在相同长度范围内,Qwen3-32B 仅从 122 ms 上升到 460 ms,在最长 prompt 下约缩短至原来的 1/1.6;GLM-5-FP8 则从 358 ms 上升到 702 ms,在长 prompt 下最多缩短至重计算路径的约 1/3.3。prefill 耗时的分项数据解释了其中的原因。
双向传输会增加少量 KV 传输时间,Qwen3-32B 约为 30 ms 至 130 ms,GLM-5-FP8 约为 29 ms 至 110 ms,但它省去的 prefill 计算时间远超这部分传输开销。因此,启用传输后的总 prefill 耗时(Qwen3-32B 为 128 ms 至 414 ms;GLM-5-FP8 为 316 ms 至 541 ms)明显低于仅执行重计算的路径(Qwen3-32B 为 153 ms 至 744 ms;GLM-5-FP8 为 500 ms 至 2,331 ms)。规模更大的 GLM-5-FP8 在绝对收益和相对收益上都更明显,因为它每个 token 的重计算耗时更高,随着模型和上下文规模扩大,KV 复用带来的回报也就更大。
下图展示了 Qwen3-32B 的 TTFT 随 prompt 长度变化的情况,对比了启用和关闭双向 KV 传输的结果。
图 3:启用和关闭双向 KV 传输时,Qwen3-32B 的 TTFT 随 prompt 长度的变化
下一张图将同一组运行中的 prefill 耗时拆分为重计算和 KV 传输两部分。
图 4:Qwen3-32B 的 prefill 耗时,分为重计算和 KV 传输两部分
这两张图共同表明,启用这项功能后,TTFT 曲线保持相对平缓;关闭时,TTFT 则会随着上下文增长而快速上升。原因在于,少量 KV 传输耗时远小于它省去的重计算耗时。
下面以相同的两个视角展示 GLM-5-FP8 的结果。第一张图对比了启用和关闭这项功能时,TTFT 随 prompt 长度的变化。
图 5:启用和关闭 KV 复用时,zai-org/GLM-5-FP8 的 TTFT
第二张图将 prefill 耗时拆分为重计算和 KV 传输两部分。
图 6:zai-org/GLM-5-FP8 的 prefill 耗时分解
GLM-5-FP8 的差距比 Qwen3-32B 大得多:在长 prompt 下,TTFT 最多缩短至原来的约 1/3.3。这是因为它每个 token 的重计算耗时更高,随着上下文长度增长,KV 复用的收益也更加明显。
要在配备 EFA 的 Amazon EC2 P5en 实例上复现我们的 1P1D 基准测试配置,也就是一个 prefill 节点和一个 decode 节点,请按照《复现我们的结果》指南中的步骤操作。该指南涵盖环境设置、启动 vLLM prefill 和 decode 服务器、运行基准测试以及收集结果。这项功能也可以在其他受支持的配置上运行,但性能结果会因硬件、模型、工作负载和网络配置而有所不同。
本文展示了双向 KV cache 传输如何在 Agent 对话历史不断增长时降低 TTFT。在 P-D 分离式部署中,这项功能允许 prefill 节点跨轮次复用 decode 节点保留的 KV 块,从而减少重复计算。可配置的块保留时间和重计算阈值决定了何时进行复用。在使用 EFA 连接 Amazon EC2 P5en 实例的 1P1D 部署上,在测试的最长 prompt 长度下,我们观察到 Qwen3-32B 的 TTFT 缩短至原来的约 1/1.6,zai-org/GLM-5-FP8 的 TTFT 缩短至原来的约 1/3.3。结果取决于工作负载和部署配置。要在自己的环境中实现这一方案,请使用 vLLM v0.21.0 或更高版本及 NixlConnector,在 prefill 和 decode 服务器上都启用 bidirectional_kv_xfer,使用能够感知 KV cache 的 router,并按照 GitHub 复现指南操作。
感谢 vLLM 社区在维护这项功能过程中提供的支持。