LoongServe论文解读:prefill/decode分离、弹性并行、零KV Cache迁移

LoongServe 论文解读:prefill/decode 分离、弹性并行、零 KV Cache 迁移

LoongServe: Efficiently Serving Long-context Large Language Models with Elastic Sequence Parallelism

论文提出了一种支持弹性分配的推理框架,通过引入弹性序列并行(Elastic Sequence Parallelism,简称 ESP)机制,动态地将 request 的 prefill 和 decode 阶段分配到 instance group 上。每个 group 可以根据负载的需求变化动态地 scale up 或者 scale down,并且没有 KV Cache 的迁移开销

请添加图片描述

问题背景

Transformer LLM 推理过程分为两个阶段:prefill 和 decode。

  • prefill 阶段:将用户输入的 prompts 生成 q、k、v,存入 KV Cache(为 decode 阶段缓存)。这一步计算并行好,是计算密集型 compute bound
  • decode 阶段:由最新产生的 tokens 生成 q、k、v,计算它与之前所有 tokens 的 attention,这一步需要从 KV Cache 中读取前面所有 token 的 key、value,因此是内存密集型 memory bound

请添加图片描述

在 long context 背景下,prefill 和 decode 阶段对计算和显存的需求非常不平衡,decode 阶段对 KV Cache 的容量需求是动态增长的、不能事先预测和分配(decode 阶段直到输出 end of sentence token 才停止)。如果把 prefill 和 decode 阶段混合部署,会造成 memory 等资源浪费,影响推理请求的吞吐量和延迟。因此,一种方法是将 prefill 和 decode 阶段分离,将 prefill 和 decode 部署在不同的 GPU 组。但是,prefill 和 decode 分离会带来新的问题:

  1. 如何细粒度地调度请求
  2. 请求从 prefill 到 decode 阶段可能需要迁移,迁移开销大
  3. GPU 组是静态划分的,无法跨组协同导致 memory 碎片(比如遇到 memory 需求很大的超长 request,哪怕所有 GPU 组的 memory 余量加起来可以满足,也无法服务)

相关工作

vLLM 提出了 Paged Attention 算法,将 attention 算法产生的连续的 key value 向量按照 block 进行组织和管理,以减少显存碎片。vLLM 还借鉴操作系统当中的虚拟内存和分页思想优化 Transformer 模型推理中产生的 KeyValue Cache,大大提高了显存当中 KV Cache 的利用效率。但 vLLM 是基于单机层面上设计,只能将 GPU 中的 block swap 到单机的内存当中。

SplitWiseDistServeTetriInfer 将 prefill 和 decode 分离到不同的 GPU 组中以避免干扰,但它们的静态并行性和分区策略并不灵活,无法处理动态工作负载。

Infinite-LLM 针对 long context 场景提出分布式 DistAttention,将 KV Cache 分割成 rblock,一个 node 可以借用别的 node 上的空闲显存。但它没有做 prefill/decode 分离,并且仍然需要周期性的 KV Cache 迁移来维持局部性,并且没有考虑不同请求之间或不同阶段之间的弹性资源需求。

设计思路

论文提出一种新的并行策略:弹性序列并行(Elastic Sequence Parallelism, ESP),并构建了一个分布式的大型语言模型(LLM)服务系统——LoongServe,以充分释放 ESP 的潜力。

请添加图片描述

LoongServe 由一组弹性实例和一个 global manager 组成。这些弹性实例可以动态地将自己组织成一组不相交的 ESP(Elastic Sequence Parallelism)组,每个 ESP 组并行处理请求批次。它们还支持高效的 scale up 和 scale down,并且不需要 KV Cache 的迁移。这样,弹性实例的 GPU 内存实际上形成了一个统一的分布式 KV Cache 缓存池,可以灵活地以 token 为粒度存储请求的键值张量,减少 GPU 内存碎片。Global manager 以 iteration 为粒度动态地调度请求、调整 ESP 组和管理分布式 KV Cache 缓存池,

请添加图片描述

Sequence Parallelism

为了 ESP 组内并行地处理请求,LoongServe 采用 Sequence Parallelism。在 attention 阶段,将 tokens 分到不同的实例,每个实例:

  1. 计算自己分到的 tokens 的 q、k,计算 local attention。
  2. 将自己的 k、v 传给下一个邻居实例。
  3. 从上一个邻居实例接收到 k、v,更新 attention,将接收到的 k、v 传递给下一个邻居,直到接收到所有的 k。

请添加图片描述

下面看 LoongServe 是如何进行 ESP 组的 scale up 和 scale down 的。

Scale down

ESP 组内的计算需求下降时,可以进行 scale down,降低 DoP(degree of parallelism)。此时,需要将 KV Cache 集中到少数的实例。由于 sequence parallelism 机制,k、v 会在组间传递,实例只需要选择性地保存一部分 k、v,而不需要额外进行 KV Cache 迁移。

如下图所示,要将实例 1~3 组成的 ESP 缩成实例 1~2 时,若将 kv1~4 保存在实例 1,kv5~6 保存在实例 2,实例 1 只需要在组间传递 kv 的时候选择性地保存 kv2,实例 2 只需要选择性地保存 kv6。

请添加图片描述

Scale up

ESP 组内的计算或者显存需求上升时,可以进行 scale up,提高 DoP。此时,需要使新加入的实例可以参与并行计算。LoongServe 将 sequence parallelism 扩展到 decode 阶段,采用 multi-master distributed decoding 机制。

每一个 master 实例负责 batch 当中的某一个 request。对于一个 decode 阶段的请求来说,master 和组内其他的实例上都存储一部分 kv,master 将新 token 的 q 传给其他实例,其他实例在本地计算 attention,然后将结果汇总到 master。

除了 attention 层以外的层(例如 FFN)都在 master 本地计算,其他实例不参与。这样就达到了分布式 KV Cache 缓存池的效果,只要 master 上有空余的 memory 可以分配给自己负责的 request,就可以继续迭代。

请添加图片描述

调度算法

算法分为 dispatching, elastic instance allocation, batching 和 elastic scaling plan generation 四个部分。

  • dispatching:负责从待处理的 requests 中选择要处理的请求集合 Rp
  • elastic instance allocation:决定将 Rp 分配给哪些实例集合 Ep
  • batching:根据 Rp 和 Ep 确定每个请求的并行度(DoP)
  • elastic scaling plan generation:以 iteration 为粒度动态地规划 ESP 组的 scale up 和 scale down。

具体算法见论文第五章。

测试

请添加图片描述

### PrefillDecode 的区别 #### 定义与功能 Prefill 是指在自然语言处理(NLP)模型中,当给定一段初始输入文本时,模型会将其转化为一系列 token 并生成对应的隐藏状态表示的过程。这一过程涉及将输入文本通过 Tokenization 转化为 token ID,并基于此计算出注意力机制所需的 K 矩阵和 V 矩阵[^2]。 Decode 则是指在生成任务中,模型逐个生成后续 token 的阶段。在此过程中,每次仅输入上一步生成的一个 token,利用该 token 计算当前步的输出并动态更新 K 矩阵和 V 矩阵[^4]。 #### 运行特点 在 prefill 阶段,由于需要一次性处理整个输入序列,因此其计算复杂度较高,主要体现在矩阵乘法操作的数量上。而在 decode 阶段,因为每次只处理单个 token,所以每一步的计算量相对较小,但由于可能需要多次迭代以完成完整的文本生成,总体耗时可能会较长。 #### 数值精度考量 无论是 prefill 还是 decode 阶段,在实际实现中都需要注意数值稳定性和计算效率之间的平衡。例如,在某些关键组件如 Emb、Output Head 或 Attention Operator 中仍需采用更高精度的数据格式来减少累积误差的影响[^1]。 ```python # 示例代码展示如何区分预填充(Prefill)和解码(Decode) class TransformerModel: def __init__(self): self.k_matrix = None self.v_matrix = None def prefill(self, input_tokens): # 将输入tokens转换为K,V矩阵 k_values, v_values = compute_attention_matrices(input_tokens) self.k_matrix = k_values self.v_matrix = v_values def decode(self, last_token): # 使用最后一个token更新K,V矩阵并预测下一个token updated_k, updated_v = update_kv_with_new_token(last_token, self.k_matrix, self.v_matrix) next_token_probabilities = calculate_next_token(updated_k, updated_v) return select_highest_probability_token(next_token_probabilities) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值