解耦编码器#

为何需要解耦编码器?#

解耦编码器 将多模态大语言模型的视觉编码器阶段运行在与预填充/解码器阶段分离的进程中。将这两个阶段部署在独立的 vLLM 实例中,带来三个实际好处:

  1. 独立、细粒度的扩展

    • 视觉编码器是轻量级的,而语言模型则要大几个数量级。

    • 语言模型可以并行化,而不影响编码器集群。

    • 编码器节点可以独立地添加或移除。

  2. 降低首令牌生成时间 (TTFT)

    • 纯文本请求完全绕过视觉编码器。

    • 编码器输出仅在所需的注意力层注入,缩短了预填充的关键路径。

  3. 编码器输出的跨进程复用与缓存

    • 进程内编码器将复用限制在单个工作进程内。

    • 远程共享缓存允许任何工作进程检索现有的嵌入向量,从而消除冗余计算。

设计文档:https://docs.google.com/document/d/1aed8KtC6XkXtdoV87pWT0a8OJlZ-CpnuLLzmR8l9BAE


使用方法#

当前的参考实现路径是 ExampleConnector。以下开箱即用的脚本展示了工作流程:

1 个编码器实例 + 1 个 PD 实例:examples/online_serving/disaggregated_encoder/disagg_1e1pd/

1 个编码器实例 + 1 个预填充实例 + 1 个解码实例:examples/online_serving/disaggregated_encoder/disagg_1e1p1d/


开发说明#

替代文本

解耦编码通过运行两个部分来实现:

  • 编码器实例 – 一个执行视觉编码的 vLLM 实例。

  • 预填充/解码 (PD) 实例 – 运行语言预填充和解码。

    • PD 可以是一个包含 (E + PD) 的单一常规实例,也可以是解耦的 (E + P + D) 实例

一个连接器将编码器缓存 (EC) 嵌入向量从编码器实例传输到 PD 实例。所有相关代码位于 vllm/distributed/ec_transfer 目录下。

关键抽象#

  • ECConnector – 用于检索编码器生成的 EC 缓存的接口。

    • 调度器角色 – 检查缓存是否存在并调度加载。

    • 工作进程角色 – 将嵌入向量加载到内存中。

  • EPD 负载均衡代理 -

    • 多路径调度策略 - 动态地将多模态请求或文本请求分流到相应的推理路径

    • 实例级动态负载均衡 - 基于最小负载策略分发多模态请求,使用优先级队列来平衡各实例间的活跃令牌工作负载。

我们使用来自 vllm_ascend/distributed/kv_transfer/kv_p2p/mooncake_layerwise_connector.pyMooncakeLayerwiseConnector 创建示例设置,并参考 examples/disaggregated_prefill_v1/load_balance_proxy_layerwise_server_example.py 来促进 P 和 D 之间的 KV 传输。关于 Mooncake 的逐步部署和配置,请参考以下指南: https://docs.vllm.ai/projects/ascend/en/latest/tutorials/features/pd_disaggregation_mooncake_multi_node.html

对于 PD 解耦部分,当使用 MooncakeLayerwiseConnector 时:请求首先进入解码器实例,解码器通过元服务器反向触发一个远程预填充任务。然后预填充节点执行推理,并将 KV 缓存逐层推送到解码器,实现计算与传输的重叠。一旦传输完成,解码器无缝地继续后续的令牌生成。docs/source/developer_guide/Design_Documents/disaggregated_prefill.md 展示了关于解耦预填充的简要思路。

限制#

  • 如果要使用跨进程缓存,请禁用 --mm-processor-cache-gb 0

  • 对于 PD 解耦部分,请参考 PD 分解的限制