跳到内容

分布式编码器

一个分布式编码器将多模态 LLM 的视觉编码器阶段运行在一个独立于预填充/解码器阶段的进程中。将这两个阶段部署在独立的 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. 开发

分布式编码是通过运行两个部分实现的

  • 编码器实例 - 执行视觉编码的 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 个令牌),然后将 KV 缓存传输到解码实例以完成剩余执行。KV 传输部分纯粹发生在 PD 实例执行之后。

docs/features/disagg_prefill.md 展示了分布式预填充 (v0) 的简要思路

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