跳到正文
vLLM Blog· Inferact, InferMatrix, and IBM·· 12 小时前AI 评分58

vLLM 发布 Encoder Disaggregation 进展:用 EPD 扩展多模态推理服务

Encoder Disaggregation in vLLM: Scaling Multimodal Serving with EPD

AI 导读

vLLM 公布 Encoder/Prefill/Decode(EPD)分离部署的多项工程进展,核心能力随 vLLM v0.32.0 发布,DeepSeek V4.1 Flash 的 metadata-only 支持已在 main 分支合入。

正文 · AI 翻译

多模态推理在语言模型执行之前增加了一个编码阶段。图像、视频帧和音频必须先被处理成嵌入,语言模型才能使用它们。这一阶段的需求随媒体大小、模态和请求组合而变化,并且可以独立于预填充和解码需求发生变化。

编码器/预填充/解码(EPD)分离将这些阶段拆分为独立部署的 vLLM 实例。vLLM 社区近期的工作在模型执行、远程嵌入传输、共享缓存、多模态输入处理和请求路由等方面扩展了 EPD。

DeepSeek 的推理部署问答描述了一种单独部署视觉编码器的 E/P/D 架构。vLLM 自我们 2025 年 12 月的架构文章起就支持编码器分离,并持续改进这一支持。vLLM main 现在也支持 DeepSeek V4.1 Flash 的仅元数据 EPD。

本文解释这一进展背后的工程实现,重点介绍五项改动:

  • 仅元数据请求,让下游实例能够消费远程嵌入,而无需重复媒体预处理。
  • 专用编码器执行,包括独立的 Model Runner V2 路径、仅编码器的 CUDA Graph 支持,以及减少检查点加载 I/O。
  • 远程嵌入传输,通过 NIXL 和 Mooncake 集成实现。
  • 跨编码器输出复用,通过 Mooncake Store 实现。
  • 降低编排开销,在 Python EPD 代理中,以及通过 vLLM Router、SMG 和 llm-d 进行 EPD 编排时。

EPD 跟踪器汇总了正在进行的工作。下文描述的核心 vLLM 能力,包括 MiMo 和 XPU 支持,已包含在 vLLM v0.32.0 中。DeepSeek V4.1 Flash 的仅元数据支持已通过#62802 在 main 上提供。下文的 vLLM Router 和 SMG 集成使用各自的主分支。

为什么要分离编码器?

一个多模态请求通常经过三个阶段:

  1. 编码器(E):预处理媒体并生成多模态嵌入。
  2. 预填充(P):处理提示词和多模态嵌入,构建生成所需的状态。
  3. 解码(D):使用该状态生成输出 token。

EPD 支持两种部署模式:E+PD,即单独的编码器加上合并的预填充/解码实例;以及 E/P/D,即三个阶段全部单独部署。

Encoder, prefill, and decode deployment patterns
编码器、预填充和解码部署模式

图 1。E+PD 将编码与语言模型执行分离。E/P/D 进一步分离预填充和解码。EC 连接器传递编码器嵌入;KV 连接器在预填充和解码之间传输状态。

图像密集型工作负载可能需要更多编码器容量,而解码容量无需相应增加。响应较长的工作负载可能需要相反的情况。分离这些阶段使运维人员能够独立调整实例数量和执行设置,同时将编码器计算从服务语言模型的 GPU 上移开。

传输嵌入只解决了交接的一部分问题。消费方还需要足够的信息来重建多模态输入布局:占位符长度、图像网格、特征长度,以及将这些位置与所传输嵌入关联起来的标识符。

一种直接的 EPD 实现会将原始媒体发送给编码器,通过 EC 连接器传输生成的嵌入,并将原始请求转发给 P/PD。这样做可以正常工作,但消费者仍可能获取、解码、调整大小、归一化或分块处理媒体,以恢复其输入布局。而编码器已经完成了这些工作。

仅元数据请求会用包含编码器侧已生成的结构信息的引用替换下游媒体项。消费者根据该元数据重建占位符,并通过 EC 连接器接收实际嵌入。

只有多模态项发生变化:文本提示和生成设置保持不变,嵌入仍通过 EC 数据路径传输。

The router forwards raw-media or metadata-only requests while EC connectors deliver embeddings separately
路由器转发原始媒体或仅元数据请求,而 EC 连接器单独传递嵌入

图 2. 路由器/代理将媒体分发给 E,并将请求转发给 P/PD。在仅元数据路径中,E 将布局元数据返回给路由器,路由器替换下游媒体项。在两条路径中,嵌入都通过 EC 连接器单独传输。箭头表示逻辑数据流,而非执行时间线。

例如,Qwen 系列图像输入需要诸如 image_grid_thw 的网格信息来构建适当的多模态布局。编码器可以发布其已计算出的网格,使 P 能够直接使用。

比较转发给 P 的原始媒体仅元数据
下游媒体项图像 URL、base64 数据或其他原始媒体输入输入标识和模型所需的结构元数据
输入布局重建媒体处理,或复用缓存预处理结果根据编码器发布的元数据构建占位符
嵌入来源EC 连接器;可用的远程嵌入可避免再次进行编码器前向计算EC 连接器
主要开销或要求可能重复的获取、解码和预处理与传输的嵌入和输入布局一致的元数据契约

如果没有仅元数据转发,编码器不一定运行两次,因为远程嵌入已经覆盖了它。重复在于媒体处理,而转发 URL 也可能使消费者再次获取媒体。

跨图像、视频和音频的元数据

Python EPD 路径在支持的模型中支持对图像、视频和音频进行仅元数据转发。三者使用相同的交接方式:消费者根据编码器发布的元数据重建输入布局,同时通过 EC 连接器接收嵌入。所需字段取决于模型和模态:

  • 图像:诸如 image_grid_thw 的图像网格让消费者无需再次处理像素即可构建视觉占位符范围。
  • 视频:前端接受 video_embeds 引用,并附带重建视频布局所需的元数据。对于 Qwen3-VL,这包括 video_grid_thw 和编码器发布的时间戳,它们会影响提示的 token 布局。
  • 音频:音频 token 数量和所需特征长度让消费者无需重复音频预处理即可重建占位符。

元数据序列化是一项单独的优化。模型声明的元数据字段现在可以表示为数值 JSON 数组,避免对小结构字段进行 PyTorch 张量序列化和 base64 转换。这也为路由器和其他前端提供了语言中立的表示。

下面的音频评测展示了这种交接带来的好处。该音频改动覆盖了纯音频 Chat Completions 请求相关的 Qwen2-Audio、AudioFlamingo3、Ultravox、Qwen2.5-Omni 和 Qwen3-Omni 路径;实时输入和音视频组合输入不在其范围内。

Audio metadata-only throughput and latency results
仅音频元数据的吞吐量与延迟结果

图 3. 在 MMAU test-mini 上的仅音频元数据评测:两轮共 1,000 个样本,Qwen3-Omni-30B-A3B-Instruct,BF16,TP=1,Model Runner V2,Mooncake TCP,1E+1PD,并发 64,并启用 CUDA Graphs。

在这次音频评测中,仅元数据转发将吞吐量从 62.21 提升到 73.13 请求/秒,并将平均延迟从 980 降低到 853 ms。准确率从 75.90% 变为 75.65%;这一差异仍有待调查。该对比衡量的是 EPD 内部的两条音频输入路径,而非 EPD 与同机部署的对比。

编码器实例的专用执行路径

仅编码器实例不运行语言模型,不维护语言模型 KV 缓存,也不产生采样 token。其执行生命周期应反映这些差异。

Model Runner V2 将仅编码器执行隔离在专用的 MMEncoderModelRunner 中。它不依赖 token 采样来管理 GPU 完成,从而避免在编码器工作仍在进行时过早复用缓冲区。

相关改动使编码器执行更加一致:

  • 配置规范化:仅生产者 EC 实例会自动进入仅编码器模式。
  • 正确的异步执行:仅编码器步骤跳过 token 采样,包括没有 EC 连接器的独立 --mm-encoder-only 实例。
  • 减少启动 I/O:当权重布局可以安全分类时,仅编码器加载会跳过纯语言模型检查点分片。在裁剪后的 Kimi-K3 检查点上,编码器只读取了 96 个分片中的 2 个,权重加载时间从 683 s 降至 0.31 s。
  • 编码器 CUDA Graph 捕获:仅编码器 V2 runner 支持可选的 ViT 图捕获,使用现有的预算选择、重放和 eager 回退机制。

GPU 视频处理

GPU 预处理和 NVDEC 硬件解码加速了 E 上的媒体处理。在一次30 帧 MMVU 评测中,端到端吞吐量在三次运行中提升了约 94–115%,每次运行都在单独的 4-GPU GB200 节点上进行。每次运行使用 Qwen3.5-35B-A3B、1E+3PD 拓扑(带共享存储 EC 连接器)、eager 执行和并发 64。这些提升同时包含 GPU 预处理和硬件视频解码;随着编码器变快,PD 层成为瓶颈。

远程嵌入传输与故障隔离

ECExampleConnector 提供了用于验证 EPD 的共享文件系统路径。跨节点部署需要网络传输、目标缓冲区管理以及对在途输出的明确所有权。

两条远程传输路径已经落地:

后端能力文档
NIXL + CPU EC使用 NIXL 在已注册的 CPU mmap 区域之间传输,在实例间共享 CPU 卸载的编码器输出CPU EC 指南
Mooncake EC通过 Mooncake TransferEngine 传递编码器输出,支持 TCP 和 RDMA 配置路径Mooncake EC 指南

传输形状必须与编码器输出匹配

传输缓冲区可能需要在项目编码之前分配。其大小无法可靠地从单个语言模型隐藏层大小字段推断:不同模态和模型架构可能产生不同的编码器输出宽度。

例如,测试的 Qwen3-Omni checkpoint 产生的视觉输出宽度为 8192,包含 DeepStack 信息,音频输出宽度为 2048。EC 生产者现在在初始化时按模态测量输出宽度,并通过传输协议携带实际形状。NIXL EC 协议版本也已更新,要求生产者和消费者一起升级。

传输失败应使受影响的请求失败

仅元数据请求使故障处理尤为重要。持有原始媒体的消费者可能能够回退到本地编码。仅持有元数据的消费者则缺少该输入。

消费者现在显式处理这一区别:当远程编码无法到达且本地恢复不可用时,它将受影响的请求报告为不可用,而不是采取可能终止 EngineCore 的不安全回退路径。资源生命周期修复,包括在 NIXL 设置失败后释放共享内存,进一步改进了故障处理。

跨编码器实例复用输出

实例本地缓存留下了复用缺口。由 E0 编码的图像在负载均衡、扩缩容或恢复后可能被路由到 E1。如果 E1 没有缓存输出,它可能重复另一个实例已经完成的编码器前向计算。

Mooncake Store 支持跨编码器输出复用。本地未命中时,编码器检查共享 Store。命中则无需再次前向计算即可提供嵌入;未命中则按正常编码进行并异步发布输出。向选定的 P 实例的交付继续通过 Mooncake P2P 进行。

Cross-encoder embedding reuse with Mooncake Store
使用 Mooncake Store 的跨编码器嵌入复用

图 4. Mooncake Store 提供跨编码器的共享输出复用。ECMooncakeConnector 保留到选定消费者的 P2P 交付路径。

共享复用会验证输入身份、模型身份和输出张量契约。初始支持包括图像、Model Runner V2、编码器 TP=1,且不支持动态 LoRA。发布是异步的,因此并发的冷请求仍可能重复计算。有关配置和支持的组合,请参阅跨编码器复用指南。

一项受控重新分配实验使用了六块 H20 GPU、InternVL3_5-38B-HF、2E+4PD 拓扑和同节点 Mooncake TCP。在缓存填充后,图像在编码器之间重新分配以制造本地未命中。在包含 512 张图像的 128 个测量请求中,共享复用将重复图像编码从512 次减少到零。TTFT p50 从1.209 秒降至 0.819 秒,p90 从1.656 秒降至 0.891 秒,TPOT 基本不变。

这是对每张图像都被重新分配的工作负载的单次运行,因此它展示了共享复用的最佳情况。生产环境的节省取决于输出被复用的频率,以及 Store 的内存和 I/O 成本。

EPD 编排与路由

计算阶段的独立扩缩容需要能够跟上节奏的编排路径。随着编码变快或缓存命中消除编码,分发媒体、收集编码器元数据和转发请求可能成为瓶颈。

仅元数据请求减少了下游媒体流量,而 JSON 元数据避免了结构字段的张量序列化。Python EPD 代理通过更快的 JSON 编码和解码以及直接转发最终响应字节(包括流式响应)而无需解码和重新编码,进一步降低了 CPU 开销。

vLLM Router 中的 EPD 编排

vLLM Router 支持 HTTP E+PD 和 E/P/D 部署、仅元数据转发、跨多个编码器的多图像分发,以及数据并行的 P/PD 消费者。编码器和消费者的数量可以独立配置。支持的 EC 后端包括 Mooncake、NIXL 和共享文件系统示例连接器。有关使用静态 worker 的原生 CLI 配置,请参阅路由器 EPD 指南。

SMG 中的 EPD 编排

SMG 支持基于 gRPC 的 vLLM E/P/D 部署,包括仅元数据转发、编码器侧媒体预处理,以及 Mooncake 和 NIXL EC 后端。它支持Python 和 Rust 两种 servicer,并且可以在单个仅图像请求内将图像分发到多个 E worker,将分配给同一编码器的图像进行批处理。

llm-d 中的 EPD 编排

llm-d 在 Kubernetes 上运行 vLLM EPD,其多模态服务指南中提供了 E+PD 和 E/P/D 布局。每个角色作为独立的 pod 层级运行,因此编码器可以独立于 prefill 和 decode 进行扩展,并使用不同的硬件。llm-d Router(端点选择器,即 EPP)选择编码器 pod,而路由 sidecar 或协调器将编码请求分发到这些 pod 上,以并行处理多张图像。嵌入通过基于 NIXL 的 EC 连接器到达消费者,纯文本请求则跳过编码。

llm-d 团队与 vLLM 社区合作,为 EPD 提供了一条经过验证的 Kubernetes 原生部署路径。有关架构以及何时分离编码的更多信息,请参阅 llm-d 的多模态服务博客。

DeepSeek V4.1 Flash:EPD 实践

我们在 GB200 上使用 DeepSeek V4.1 Flash 和 512 个 MP-DocVQA 验证问题,比较了 PD 服务与 EPD,每个问题包含四到六页原始文档页面。请求平均包含 4,905 个输入 token 和 5.2 个输出 token。每个并发级别运行三次,并启用了缓存和 CUDA Graphs。

PD 基线使用四块 GPU 用于 P,四块用于 D。EPD 增加了四个单 GPU 编码器实例,同时保持相同的 P/D 配置:TP=1、DP=4、专家并行,以及跨 DP rank 分片的 GPU 驻留 Engram 表(DEP4)。EPD 使用 Mooncake GPU RDMA 传输嵌入;两种部署均使用 NIXL 进行 KV 传输。两者均运行 main 分支的 5314c58281 加上 DeepSeek 仅元数据扩展。

DeepSeek V4.1 Flash throughput per GPU versus P90 time to first token, comparing PD and EPD
DeepSeek V4.1 Flash 每 GPU 吞吐量与 P90 首 token 时间对比,比较 PD 和 EPD

图 5。吞吐量越高、P90 TTFT 越低越好。C 表示客户端并发数。吞吐量统计输入加输出 token 数,除以经过时间和所有已部署 GPU:PD 为八块,EPD 为十二块,包括全部四块编码器 GPU。数据点汇总三轮;P90 汇总每个并发级别的全部 1,536 个请求。绘图数据。

在并发 64–128 时,EPD 的每 GPU 输入加输出吞吐量比八 GPU PD 基线高 6.6–9.5%,统计了全部 12 块 EPD GPU,包括编码器。P90 TTFT 降低了 44–47%。然而在并发 16 时,EPD 的每 GPU 吞吐量较低;在 32 时大致持平。收益取决于负载和资源分配,而非仅靠分离。

快速开始

先从 E+PD 开始,在添加独立的 decode 层之前验证从 encoder 到 consumer 的交接。Disaggregated Encoder 指南 链接了示例部署和集成测试。

在具备兼容的 vLLM 检出、已安装 Mooncake 以及两块可用 GPU 的情况下,以下集成脚本会运行基线,并与 disaggregated 部署的输出进行比较:

GPU_E=0 GPU_PD=1 MOONCAKE_EC_PROTOCOL=tcp \
  bash tests/v1/ec_connector/integration/run_epd_mooncake_ec_full_pipeline.sh

使用 Mooncake EC 指南 了解传输依赖和 consumer 控制地址配置,或使用 CPU EC 指南 了解 CPU 卸载和基于 NIXL 的共享。根据目标媒体组合、缓存行为和延迟要求选择 E/PD 资源划分。

下一步

近期工作已将 EPD 扩展到其核心执行和传输路径之外。MiMo-V2-Omni 现在声明了图像 EPD 所需的 embedding 字段,而 XPU 支持则包括 EC 集成和仅 encoder 的 producer runner。

进一步的工作包括可观测性、更多传输和缓存后端、前端集成,以及更清晰的上游错误传播。运维人员需要区分计算 embedding、加载缓存输出和远程传输所花费的时间。

另一个机会是协调 encoder 工作与 prefill 侧复用。在 E-first 流水线中,编码可以在 P 发现其 KV 缓存已覆盖某个多模态出现,或本地已有可复用 embedding 之前就开始。一项正在进行的设计讨论探讨 P 的需求如何抑制不必要的上游工作。该设计必须在节省的计算和交付与额外的查找、协调和冷请求延迟之间取得平衡。

对 EPD 有疑问或有兴趣参与?加入 vLLM Slack 上的 EPD / EC-connector 频道,讨论用例、分享反馈并开展协作。

致谢

我们感谢以下贡献者的工作和反馈:

实现参考

来源:vLLM Blog · vllm.ai