跳到内容

解耦编码器 (Disaggregated Encoder)

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

  1. 独立、细粒度的扩展
  2. 更低的首字延迟 (TTFT)
  3. 编码器输出的跨进程复用与缓存

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


1 动机

1. 独立、细粒度的扩展

  • 视觉编码器较为轻量,而语言模型则大几个数量级。
  • 语言模型可以在不影响编码器集群的情况下进行并行化。
  • 编码器节点可以独立地进行添加或移除。

2. 更低的首字延迟 (TTFT)

  • 纯语言请求完全绕过视觉编码器。
  • 编码器输出仅在必要的注意力层注入,缩短了预填充的关键路径。

3. 跨进程复用与缓存

  • 进程内编码器将复用限制在单个工作节点内。
  • 远程共享缓存允许任何工作节点检索现有嵌入,消除了冗余计算。

2 使用示例

当前的参考路径是 ExampleConnector
以下现成的脚本展示了工作流程:

1 个编码器实例 + 1 个 PD 实例: examples/online_serving/disaggregated_encoder/disagg_1e1pd_example.sh

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


3 测试脚本

请参考目录 tests/v1/ec_connector

4 开发

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

  • 编码器实例 (Encoder instance) – 执行视觉编码的 vLLM 实例。
  • 预填充/解码 (PD) 实例 – 运行语言预填充和解码。
    • PD 既可以是一个使用 disagg_encoder_example.sh (E->PD) 的单一普通实例,也可以是使用 disagg_epd_example.sh (E->P->D) 的解耦实例。

连接器将编码器缓存 (EC) 嵌入从编码器实例传输到 PD 实例。
所有相关代码均在 vllm/distributed/ec_transfer 下。

关键抽象

  • ECConnector – 用于检索由编码器生成的 EC 缓存的接口。
    • 调度器角色 – 检查缓存是否存在并调度加载。
    • 工作节点角色 – 将嵌入加载到内存中。

以下是说明解耦编码器流程的图示:

Disaggregated Encoder Flow

对于 PD 解耦部分,预填充实例接收缓存的方式与上述解耦编码器流程完全相同。预填充实例执行 1 个步骤(预填充 -> 输出 1 个 token),然后将 KV 缓存传输给解码实例进行剩余的执行。KV 传输部分纯粹发生在 PD 实例执行之后。

docs/features/disagg_prefill.md 展示了关于解耦预填充 (v0) 的简要构思。

我们使用来自 vllm/distributed/kv_transfer/kv_connector/v1/nixl_connector.pyNixlConnector 创建了示例配置,并参考了 tests/v1/kv_connector/nixl_integration/toy_proxy_server.py 来促进 P 和 D 之间的 KV 传输;