分段预填充(实验性)
本页将向您介绍 vLLM 中的分段预填充特性。
注意
此特性为实验性,可能随时更改。
为什么选择分段预填充?¶
两个主要原因
- 分别调优首个 token 生成时间 (TTFT) 和 token 间延迟 (ITL)。分段预填充将 LLM 推理的预填充和解码阶段放在不同的 vLLM 实例中。这让您可以灵活地分配不同的并行策略(例如
tp
和pp
),以在不影响 ITL 的情况下调优 TTFT,或在不影响 TTFT 的情况下调优 ITL。 - 控制尾部 ITL。如果不使用分段预填充,vLLM 可能会在一个请求的解码过程中插入一些预填充作业。这会导致更高的尾部延迟。分段预填充有助于解决此问题并控制尾部 ITL。使用适当块大小的分块预填充也可以达到同样的目的,但在实践中很难确定正确的块大小值。因此,分段预填充是控制尾部 ITL 更可靠的方法。
注意
分段预填充不会提高吞吐量。
使用示例¶
请参阅 examples/online_serving/disaggregated_prefill.sh以获取分段预填充的使用示例。
基准测试¶
请参阅 benchmarks/disagg_benchmarks以获取分段预填充的基准测试信息。
开发¶
我们通过运行两个 vLLM 实例来实现分段预填充。一个用于预填充(我们称之为预填充实例),另一个用于解码(我们称之为解码实例),然后使用一个连接器将预填充 KV 缓存和结果从预填充实例传输到解码实例。
所有分段预填充的实现代码都在 vllm/distributed/kv_transfer
下。
分段预填充的关键抽象
- 连接器(Connector):连接器允许 KV 消费者(kv consumer) 从 KV 生产者(kv producer) 获取一批请求的 KV 缓存。
- 查找缓冲区(LookupBuffer):查找缓冲区提供两个 API:
insert
KV 缓存和drop_select
KV 缓存。insert
和drop_select
的语义类似于 SQL,其中insert
将一个 KV 缓存插入到缓冲区中,而drop_select
返回匹配给定条件的 KV 缓存并将其从缓冲区中删除。 - 管道(Pipe):一个用于张量传输的单向 FIFO 管道。它支持
send_tensor
和recv_tensor
。
注意
insert
是非阻塞操作,而 drop_select
是阻塞操作。
下图展示了上述三个抽象的组织方式
分段预填充的工作流程如下
buffer
对应于 LookupBuffer 中的 insert
API,而 drop_select
对应于 LookupBuffer 中的 drop_select
API。
第三方贡献¶
分段预填充与基础设施高度相关,因此 vLLM 依赖于第三方连接器来实现生产级别的分段预填充(vLLM 团队将积极审查并合并第三方连接器的新 PR)。
我们推荐三种实现方式
- 完全自定义连接器:实现您自己的
Connector
,并调用第三方库来发送和接收 KV 缓存,以及更多其他操作(例如编辑 vLLM 的模型输入以执行自定义预填充等)。这种方法提供了最大的控制权,但也存在与未来 vLLM 版本不兼容的风险。 - 类数据库连接器:实现您自己的
LookupBuffer
,并像 SQL 一样支持insert
和drop_select
API。 - 分布式点对点连接器:实现您自己的
Pipe
,并像torch.distributed
一样支持send_tensor
和recv_tensor
API。