连续批处理原理与优化
GPU 专为高并行计算设计,但 LLM 推理由于自回归特性,往往难以充分利用硬件算力。批处理通过跨请求复用已加载的模型参数,是提升吞吐量的关键手段。本文将介绍从静态批处理、动态批处理到连续批处理(Continuous Batching) 的演进,并分析其底层机制。
1. 批处理策略的演进
1.1 静态批处理
服务器等待固定数量的请求到达后,将它们作为一个批次统一处理。缺陷在于:
- 首个请求必须等待最后一个请求到达才能启动。
- 所有请求必须等待最慢的请求完成,造成 GPU 空闲。
1.2 动态批处理
设置时间窗口,处理该窗口内到达的所有请求;若批次提前达到规模上限则立即启动。虽然能平衡吞吐量与延迟,但批次中最长请求仍决定整体完成时间。
1.3 连续批处理
连续批处理允许批次中每个序列独立完成,并立即用新请求替换已完成序列。它采用迭代级调度(Iteration-level Scheduling),批次组成在每个解码迭代周期动态变化,从而最大化 GPU 占用率。
主流推理框架如 vLLM、SGLang、TensorRT-LLM、LMDeploy 与 Hugging Face TGI 均已支持该机制。
2. 注意力机制与 KV Cache
2.1 预填充阶段(Prefill)
模型首先处理完整输入 Prompt,计算每个 Token 的 Key 与 Value 并缓存。该阶段计算密集,复杂度为
2.2 解码阶段(Decode)
进入自回归循环后,每步只需计算新 Token 的 Query、Key、Value,并复用历史 KV Cache。复杂度从
对于一个拥有
例如 Llama-2-7B(
2.3 分块预填充(Chunked Prefill)
当 Prompt 过长、单次前向传播无法容纳所有 Token 时,可将 Prefill 拆分为多个块,逐块计算并增量更新 KV Cache:
chunk 1: compute KV for tokens 1..m
chunk 2: prepend cached KV, compute KV for tokens m+1..2m
...这使得模型能够灵活适应不同显存限制,而不会丢失上下文信息。
3. 连续批处理的核心思想
3.1 朴素批处理的缺陷
朴素批处理要求所有序列长度相同,短序列通常需要 Padding 到最长序列。一旦某个序列提前生成 <eos>,其占用的空间会继续浪费到批次结束。
此外,当用新请求替换已完成请求时,新请求需要 Prefill,而批次中其他请求正在 Decode。新请求的全部长度几乎都转化为 Padding Token,造成巨大计算浪费。填充成本随 Batch Size
3.2 参差批处理(Ragged Batching)
连续批处理的关键是彻底消除批次维度及其 Padding。方法是不再堆叠张量,而是将不同请求的 Token 序列拼接(Concatenate)成一个超长序列,并通过精心设计的注意力掩码保证:
- 每个 Token 只能看到自身及之前的 Token(因果性)。
- 不同请求的 Token 之间互不干扰(请求隔离)。
这种长度不均匀的拼接方式称为参差批处理,其核心优势在于彻底消除 Padding Token。
3.3 完整算法
连续批处理是参差批处理与动态调度的结合:
- 确定内存预算
:每批次总 Token 数不超过该预算。 - 优先 Decode 序列:将所有处于 Decode 阶段(每个仅占 1 个 Token)的请求加入当前批次。
- 填充 Prefill 序列:利用分块预填充的灵活性,用 Prefill 请求填充剩余空间;若过长则拆分为合适大小的块。
- 动态替换:一旦某请求生成
<eos>,立即移除,并用新的等待请求(以其第一个 Prefill 块)替换。
通过在同一批次中高效混合 Prefill 与 Decode,并结合动态调度与无 Padding 的参差批处理,连续批处理显著提高了 LLM 服务系统的效率。
总结
连续批处理是现代高性能 LLM 服务系统的核心技术,它巧妙结合了三种机制:
- KV Cache:避免在自回归生成中重复计算历史 Token。
- 分块预填充:在显存受限时灵活、增量地处理可变长 Prompt。
- 参差批处理与动态调度:消除 Padding 与批次边界,确保 GPU 始终处于高负载状态。
正是这些技术的结合,使得 ChatGPT 等服务能够高效、低延迟地同时处理成千上万并发用户。