分离式编码器#

为什么需要分离式编码器?#

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

  1. 独立的细粒度扩展

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

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

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

  2. 更低的首 Token 时间(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 负载均衡代理 -

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

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

我们使用 vllm_ascend/distributed/kv_transfer/kv_p2p/mooncake_layerwise_connector.py 中的 MooncakeLayerwiseConnector 创建示例设置,并参考 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 缓存逐层推送到解码器,实现计算与传输的重叠。传输完成后,解码器无缝地继续后续的 Token 生成。docs/source/developer_guide/Design_Documents/disaggregated_prefill.md 展示了预填充分离的简要思路。

限制#

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

  • 关于 PD 分离部分,请参考 PD 分解的限制说明