多模态 LLM Serving 生产实战:用 Parallel Media Decode、Encoder Cache 与 Encoder Disaggregation 压住视觉请求尾延迟
多模态大模型上线后,很多团队会惊讶地发现:明明 GPU 利用率不高,视觉请求的 P95/P99 首 token 延迟却持续恶化。问题往往不在语言模型本身,而在图片或视频进入模型之前的那段”隐形前置链路”——远程拉取、解码、resize/normalize、视觉编码以及 embedding 传输。本文结合 NVIDIA Dynamo、vLLM 与 EPD 实践,梳理一条可落地的视觉关键路径治理方法。
为什么多模态请求不能继续按”普通 LLM 请求”治理
纯文本 LLM 的在线路径相对清晰:请求进入调度器,完成 prefill,然后进入逐 token decode。视觉语言模型(VLM)则多了一段容易被忽视的前置链路:
HTTP / Base64 media
↓ fetch / decode / decompress
↓ resize / normalize / multimodal processor
↓ vision encoder
↓ multimodal embeddings
↓ LLM prefill + decode
这意味着一个看起来只是”多传一张图片”的请求,实际可能同时消耗网络带宽、CPU 解码线程、主机内存、视觉 Encoder GPU 算力和语言模型 GPU 算力。图片数量、分辨率、视频帧数和媒体来源一旦失控,P95/P99 TTFT 会先恶化,而平均吞吐未必马上暴露问题。
NVIDIA Dynamo 当前的多模态 Serving 文档已经把这条链路拆成若干可独立优化的环节,包括 Parallel Media Decoding、Embedding Cache、Multimodal KV Routing 与 Encoder Disaggregation。本文聚焦与”视觉输入前处理 / 编码阶段”直接相关的部分。
核心原则:先把视觉关键路径拆开,再决定优化手段
多模态性能治理的第一步不是改架构,而是把关键路径拆成可独立度量的阶段。只有先看清瓶颈在哪一段,才能决定是优化 CPU、加缓存,还是拆 Encoder。
1. 把图片获取与解码从推理 Worker 的关键路径前移
远程图片请求通常包含 URL fetch、Base64 decode、JPEG/PNG/WebP 解压等 CPU 与 I/O 工作。如果这些操作全部发生在模型 Worker 内部,GPU 可能在等待 CPU 准备输入,同时模型进程还要承担不可预测的网络延迟。
Dynamo 的 Parallel Media Decoding 将图片拉取、Base64 解码和图像解压移动到前端 CPU worker pool,并发完成后再把 decoded pixel buffer 交给推理后端。这个设计的价值不是”让视觉 Encoder 更快”,而是把不应该占据 GPU Worker 请求线程的媒体工作移出去。
适合优先启用的场景通常包括:
- 请求经常携带 HTTP/HTTPS 图片;
- 单请求含多张图片;
- 图片压缩率高、解码开销明显;
- 后端 CPU 已成为请求路径瓶颈;
- GPU 利用率不高,但 TTFT 仍然长且波动大。
工程上不要只测总 TTFT,至少要独立记录以下指标:
media_fetch_ms
media_decode_ms
mm_processor_ms
vision_encode_ms
embedding_transfer_ms
llm_prefill_ms
tpot_ms
如果 media_fetch_ms + media_decode_ms 已占到视觉请求首 token 延迟的大头,就应该先处理前端解码与媒体来源,而不是直接增加 LLM GPU。
2. 把 Encoder Cache 当成独立缓存层,不要和 KV Cache 混为一谈
对于商品图、固定截图、共享流程图、同一图片的多轮问答等业务,请求内容可能不断变化,但媒体本身经常重复。每次都重新运行视觉 Encoder,会浪费大量重复计算。
Dynamo 的 Embedding Cache / Encoder Cache 使用 CPU 侧 LRU 缓存保存视觉 Encoder 输出,命中后可以跳过视觉编码。vLLM 也提供多模态 Processor Cache,并允许通过稳定的 multi_modal_uuids 避免每次重新对原始媒体内容做哈希。
需要特别区分三类缓存:
| 缓存 | 缓存对象 | 主要节省 | 典型失效条件 |
|---|---|---|---|
| MM Processor Cache | 预处理后的多模态输入 | resize、processor 等 CPU 工作 | Processor 参数、输入内容变化 |
| Encoder / Embedding Cache | 视觉 Encoder 输出 embedding | Vision Encoder GPU 计算 | Encoder、Processor、输入内容变化 |
| KV Cache | LLM attention key/value | LLM prefill | Prompt/token 前缀变化 |
如果把三者合并成一个”缓存命中率”,线上排障几乎没有意义。建议分别观察命中率、容量、淘汰量和节省的阶段耗时。
缓存 key 也不能只使用图片 URL。CDN URL 可能不变而内容已更新,同一个 URL 在不同版本的图像 Processor 或视觉 Encoder 下也可能产生不同 embedding。生产环境更稳妥的 key 至少应包含:
media_content_hash + encoder_model_version + processor_version + processor_parameters
若业务使用稳定媒体 ID,应确保 ID 对应内容不可变,或者把内容版本号一起纳入 cache key。
3. 只有 Encoder 真正成为瓶颈时,再做 Encoder Disaggregation
多模态模型通常把视觉编码和语言模型推理放在同一个服务实例中。优点是部署简单,缺点是视觉请求突增时,Encoder 与 LLM 会在同一组 GPU 上争夺资源,而且两种阶段的资源需求不一定一致。
Encoder Disaggregation 的思路是把视觉 Encoder 单独放到 Encode Worker,得到 embedding 后再传给下游 LLM Worker。Dynamo 当前支持 E/PD、E/P/D 等拓扑,但从生产治理角度,最重要的是”Encoder 可以独立扩容和独立选硬件”,而不是为了拆分而拆分。
┌─ Encode Worker 1 ─┐
Media Decode ───┼─ Encode Worker 2 ─┼──> embeddings ──> LLM Worker Pool
└─ Encode Worker N ─┘
适合拆分的信号包括:
vision_encode_ms的 P95/P99 长期占据 TTFT 的显著比例;- Encoder 队列增长,而 LLM decode GPU 仍有余量;
- 图片请求与文本请求的流量比例波动很大;
- Vision Encoder 与语言模型适合不同规格 GPU;
- 需要独立控制视觉阶段容量,避免图片流量拖慢纯文本流量。
反过来,如果业务只是低并发图片问答,Encoder 利用率很低,拆分后新增的 RPC、embedding transfer、部署拓扑和故障域可能比收益更大。聚合部署应当是默认基线,分离是瓶颈驱动的优化。
研究工作《Efficiently Serving Large Multimodal Models Using EPD Disaggregation》也验证了编码阶段独立配置资源的潜力。但论文中的 TTFT 和吞吐改善依赖模型、图片数量、硬件和流量形态,生产环境不应直接照搬论文收益率,应以自己的阶段级 profiling 为准。
输入预算比”后端硬扛”更重要
多模态接口天然存在请求放大问题:一张 512×512 图片和 20 张高分辨率图片完全不是同一种成本;一个 8 帧视频和一个数百帧视频也不是同一种负载。
vLLM 的多模态配置允许限制每个 prompt 的媒体数量,并为视频帧数、宽高等参数设置约束。生产环境应把这些限制提升为 API contract,而不是隐藏的引擎参数。
例如可以建立如下分层预算:
multimodal_budget:
default:
max_images: 4
max_image_width: 2048
max_image_height: 2048
max_video_frames: 32
trusted_batch_job:
max_images: 16
max_video_frames: 96
真正需要控制的不只是”文件大小”,还包括:
- 图片数量;
- 解码后像素总量;
- 视频帧数;
- 视频时长和采样策略;
- 媒体下载超时;
- 单请求累计媒体字节数;
- Processor 产生的视觉 token 数量。
这些字段应同时进入请求日志和成本归因,否则多模态服务很难解释”为什么同样一次 API 调用,耗时和 GPU 成本差异数倍”。
媒体 URL 本身也是生产边界
支持 image_url、video_url 后,服务端通常会主动访问外部地址。这个能力如果没有 URL policy,会带来 SSRF 和内部网络探测风险。
Dynamo 当前文档明确给出了默认 URL 校验策略:默认允许 HTTPS 和 data URL,并阻止私网 / loopback 等内部地址;只有在明确的内网部署场景才建议放宽。无论采用哪套 Serving 框架,都应该建立类似规则:
- 默认拒绝内网和 metadata endpoint;
- 对重定向后的每一跳继续校验;
- 设置 DNS、连接、读取和总下载超时;
- 限制响应体大小和 MIME type;
- 不允许任意本地文件路径;
- 对可信内部媒体域名使用 allowlist。
这类校验应在媒体 fetch 层完成,不要等到模型 Worker 收到异常输入后再处理。
一套可落地的演进顺序
第一步:先建立阶段级基线
先不要改架构。给现有聚合部署增加阶段耗时、输入规模和资源指标,至少连续覆盖一轮真实高峰。重点指标包括:
media_fetch_ms/media_decode_ms/vision_encode_ms的 P50/P95/P99;- Encoder queue depth;
- Encoder GPU utilization / memory;
- 图片数量、像素总量、视频帧数;
- MM Processor Cache hit ratio;
- Encoder Cache hit ratio;
- 纯文本与多模态 TTFT 分布。
必须把纯文本和多模态请求拆开看。混合后的总体 P95 很容易掩盖视觉请求已经恶化的事实。
第二步:先解决 CPU / I/O,再动 GPU 拓扑
如果瓶颈集中在图片 fetch 和 decode,优先做并行媒体解码、连接池、超时、媒体代理或边缘缓存。此时扩 Encoder GPU 通常解决不了问题。
第三步:只有存在重复媒体时才上 Encoder Cache
先测重复媒体比例,再决定缓存容量。若 99% 请求都是唯一图片,10 GB 的 embedding cache 也不会产生理想收益,反而增加主机内存占用和失效治理成本。
Dynamo 文档说明其多模态 embedding cache 使用 CPU 侧 LRU;当前 vLLM 集成要求 vLLM 0.17.0 或更高版本。实际部署时应以当前版本兼容矩阵为准。
第四步:Encoder 饱和后再独立拆分
如果 profiling 显示 Encoder 是稳定瓶颈,再拆出 Encode Worker。拆分后重新验证:
TTFT = media + processor + queue(E) + encode + transfer + queue(LLM) + prefill
不要只看 encode 是否下降,还要确认新增的 queue(E) 和 transfer 没有吃掉收益。
适用场景
这套方法尤其适合:
- 电商商品图问答,同一商品图片被大量重复访问;
- OCR / 图像理解平台,单请求包含多张图片;
- 企业文档截图、设计稿或流程图的多轮问答;
- 视频理解服务,需要严格控制采样帧数;
- 同一集群同时承载纯文本与视觉请求,希望避免视觉突发拖慢文本流量;
- Vision Encoder 和语言模型对 GPU 型号、显存、算力需求差异明显的场景。
不太适合一开始就做复杂拆分的场景是低并发内部工具、媒体输入极少、没有明显视觉阶段瓶颈,或者业务仍处于快速验证期。此时保持单实例聚合部署通常更可控。
常见误区
误区一:TTFT 高就直接扩 LLM GPU。 多模态 TTFT 高可能是图片下载、解码或视觉 Encoder 阻塞。没有阶段指标时扩 GPU 只是昂贵的猜测。
误区二:只要有图片就开大缓存。 缓存收益取决于媒体重复率。唯一图片流量不会因为 LRU 更大而自动变快。
误区三:把 Encoder Cache 当成 Prefix / KV Cache。 它们缓存的对象、生命周期和正确性边界完全不同。Encoder 或 Processor 升级时,旧 embedding 通常不能继续无条件复用。
误区四:为了架构先进直接做 E/P/D 全拆分。 多一个阶段就多一个队列、传输链路、故障域和容量参数。先证明 Encoder 是瓶颈,再拆。
误区五:只限制上传文件大小。 压缩后的 JPEG 很小,解码后可能是大尺寸像素矩阵;视频文件大小也无法直接代表实际采样帧与 Encoder 成本。预算必须面向解码后的计算规模。
误区六:把媒体 URL 当成普通字符串。 一旦服务端主动 fetch URL,它就是网络访问能力。必须做协议、域名/IP、重定向、大小和超时控制。
上线检查清单
- 已区分纯文本与多模态请求的 SLO;
- 已拆分 media fetch、decode、processor、vision encode、transfer、prefill、decode 指标;
- 已设置图片数量、分辨率、视频帧数等输入预算;
- 已限制媒体下载超时、大小、协议与访问网段;
- 已验证 MM Processor Cache 与 Encoder Cache 的命中率和失效策略;
- Cache key 包含模型 / Processor 版本;
- 已确认是否真的需要 Encoder Disaggregation;
- 拆分后已测 embedding transfer 开销;
- 已对纯文本流量做隔离回归,确认视觉峰值不会明显拖慢文本请求;
- 升级视觉 Encoder 或 Processor 时有明确的缓存失效与灰度策略。
参考资料
- NVIDIA Dynamo — Multimodal Model Serving:https://docs.nvidia.com/dynamo/v1.4.1/multimodal/overview
- NVIDIA Dynamo — Parallel Media Decoding:https://docs.nvidia.com/dynamo/multimodal/parallel-media-decoding
- NVIDIA Dynamo — Embedding Cache:https://docs.nvidia.com/dynamo/user-guides/multimodal/embedding-cache
- NVIDIA Dynamo — Encoder Disaggregation:https://docs.nvidia.com/dynamo/dev/multimodal/encoder-disaggregation
- vLLM — Multimodal Inputs:https://docs.vllm.ai/en/latest/features/multimodal_inputs/
- Singh et al. — Efficiently Serving Large Multimodal Models Using EPD Disaggregation:https://arxiv.org/abs/2501.05460