@akshay_pachaar: https://x.com/akshay_pachaar/status/2094490705024676272

X AI KOLs Following 新闻

摘要

这篇文章解释了用于在GPU上服务大型语言模型的静态、动态和连续批处理策略,详细说明了它们的权衡以及如何优化吞吐量和减少空闲时间。

https://t.co/y0RliaWgqT
查看原文
查看缓存全文

缓存时间: 2026/09/01 23:50

大语言模型中的静态批处理 vs. 动态批处理 vs. 连续批处理:清晰解析

你需要了解的一切,明白为何你的GPU在负载下闲置,以及哪种批处理策略能解决这个问题。从第一性原理出发解析三种策略,每种策略留下的缺陷,分块预填充技术,以及如何根据你的工作负载进行选择。

现代GPU每秒可执行数万亿次浮点运算。在单卡上运行大语言模型时,你常常会观察到它仅利用了算力的一小部分。

原因在于,生成单个token意味着需要从内存中读取模型的所有权重。读取操作占据了绝大部分时间,而计算单元大部分时间都在等待。

批处理技术弥合了这一差距。只需读取一次权重,让多个序列共享同一次计算过程,内存成本就被所有请求分摊了。

所有推理引擎都采用了批处理。它们之间的区别在于何时决定批次组成:

  • 静态批处理:在批处理开始前一次性决定
  • 动态批处理:通过计时器定时决定
  • 连续批处理:在每次前向传播时重新决定

最后一种策略带来了现代推理系统几乎全部的吞吐量提升。前两种策略值得先了解,因为它们各自存在缺陷,而后续策略正是为了解决这些缺陷而设计的。

今天我们将逐一剖析并理解这些策略。

开始吧!🚀

批处理存在的原因

A100 GPU每秒可执行约312万亿次BF16精度运算,内存带宽约为每秒2TB。解码过程完全依赖于内存带宽。

读取权重构成了全部成本,而计算单元则在等待中闲置。这种闲置算力本是免费容量,因此处理60个请求的批次与单个请求共享相同的权重读取过程,吞吐量随之提升,而内存读取时间保持不变。

请注意,吞吐量随批大小陡峭上升后趋于平缓:

  • 低于拐点时,你处于内存带宽瓶颈,批处理几乎无成本
  • 高于拐点时,你处于计算瓶颈,每个新增序列都会增加时间成本

拐点位置随模型、GPU和序列长度变化,需在自身硬件上实测。

现在你可能会想:既然批处理如此有效,为何不尽可能增大批次?

对于大语言模型而言,这比想象中困难。让我们理解原因。

大语言模型批处理的挑战

批处理技术比大语言模型更早出现。对于分类器或嵌入模型,这本质上是个打包问题:

将输入填充至相同长度,堆叠成单个张量,执行一次前向传播即可。

这种方法有效基于三个前提:单次传播生成完整答案、行与行之间无依赖、每行的计算成本在开始前已知。

但对于自回归大语言模型,这三个前提全部失效:

  • 单次传播只生成一个token,而非完整答案:400 token的回复需要400次前向传播
  • 行依赖于自身历史:每次传播都会写入该请求的KV缓存,下次传播需要读取
  • 计算成本未知:模型通过生成终止token来结束,可能出现在12 token后,也可能在4000 token后

填充技术无法解决这个问题。它只能对齐输入宽度,而无法缩短请求占用GPU的时间。

因此,固定起点的批次只能以最慢请求的速度运行。三种策略正是针对这一问题的渐进式解决方案。

静态批处理

最简单的方案不做任何处理:收集固定数量的请求,共同运行,等所有完成后一起返回。

最终状态如下图所示:

R3在t=9时刻停止生成,但由于R2仍在运行,它必须在GPU上等待至t=15。这造成了双重浪费:

  • 已完成响应的用户额外等待6个时间单位
  • 被占用的槽位本可服务于排队请求

浪费程度随输出长度方差增大而加剧。Anyscale在40GB A100上对OPT-13B的测试显示:输出长度分布变宽时,静态批处理的吞吐量降至约81 token/秒,而连续批处理仍保持一个数量级的性能优势。

有时仍是正确选择:当输出长度固定时(如分类、嵌入和评分任务),静态批处理不会产生等待问题,且实现更简单。

vLLM中的静态批处理配置示例:

from vllm import LLM, SamplingParams

llm = LLM(model="meta-llama/Llama-3.1-8B-Instruct")
outputs = llm.generate(prompts, SamplingParams(max_tokens=64))

通过限制max_tokens并保持提示长度相近,所有序列几乎同时完成。

👉 Hugging Face pipeline API的batch_size参数也属于静态批处理,适用于评估脚本,但不适合生产环境。

动态批处理

静态批处理还有第二个成本:请求可能在进入批次前等待很长时间。

如果批大小为8但只有5个请求到达,这5个请求必须空闲等待剩余3个。

动态批处理增加了计时器:当达到批大小限制或超时时间到期时触发批次(以先到者为准)。

最终状态如下图所示:

批次1在t=4时刻(固定间隔)触发,此时仅有R1和R2,因为第三个请求尚未到达。两者比静态批处理提前4个时间单位启动。

但它缩短了错误的等待时间:R1在t=7完成,仍需等待至t=12(因R2未完成)。

动态批处理为每个批次做一次决定,之后控制权交给引擎直至所有成员完成。对于固定输出模型,这已是完整解决方案(Triton推理服务器采用此模式)。

dynamic_batching {
  preferred_batch_size: [ 4, 8 ]
  max_queue_delay_microseconds: 100
}

👉 preferred_batch_size定义调度器尝试形成的批大小,max_queue_delay_microseconds限制请求最大等待时间。

连续批处理

前述两种策略都将批次视为整体工作单元,一旦开始就持续到所有请求完成。这个假设仍在造成资源浪费,而连续批处理彻底抛弃了它。

调度器执行一次迭代后收回控制权并重新决策。当某个序列生成终止token时立即离开批次,下一个迭代中等待的请求即可占用该槽位。

批次组成在每次迭代时变化,因此称为迭代级调度。没有任何槽位需要等待最慢序列,因此即使输出长度差异巨大,GPU也能保持饱和状态。

最终状态如下图所示:

瓶颈在于内存而非计算:每个活跃序列都持有随token增长的KV缓存,正是缓存(而非计算)决定了能并行处理多少序列。

在驻留13B模型的40GB A100上,一次只能处理少量长序列。当缓存池耗尽时,调度器会驱逐运行中的请求并稍后重新计算。

这看似GPU算力不足,实则是重复计算相同的预填充过程。

👉 各引擎用不同名称实现连续批处理:vLLM、SGLang和TGI称之为“continuous batching“,TensorRT-LLM称为“in-flight batching“,LMDeploy称为“persistent batching“。

分块预填充

每次迭代重建批次解决了慢成员问题,但引入新问题——用户感知为卡顿。

新加入的请求需要在单次迭代中完成预填充。32K token的提示会产生一次巨大的计算密集型传播,所有活跃解码进程都必须等待,导致其他请求的输出中途暂停。

下图对比分块预填充前后的差异:

整体预填充:

分块预填充:

(请求C的提示被分解为多个2K token的小块)

跨迭代分割提示:分块预填充将提示分解为固定大小的token范围,通过多次传播逐步扩展请求的KV缓存。

注意力计算逻辑不变,因为后续块会关注先前块处理的内容。第一个token在最后一块处理完成后才到达。

预填充受计算限制,解码受内存限制,因此同时包含两者的批次能充分利用芯片的两个部分。

选择分块大小时需注意:

  • 较小分块为调度器提供更多解码机会,减少活跃请求的ITL(首token间延迟)尖峰
  • 较大分块能更高效处理新提示,通常改善TTFT(首token延迟),但活跃解码进程可能在token间等待更久
  • 过小分块会降低GPU利用率并增加注意力开销,因为后续块需要重新读取前序块创建的KV缓存条目

vLLM V1默认启用分块预填充,通过–max-num-batched-tokens限制每迭代最大token数;SGLang使用–chunked-prefill-size,设为-1则禁用。

vLLM示例:

vllm serve meta-llama/Llama-3.1-8B-Instruct \
  --max-num-batched-tokens 8192

SGLang示例:

sglang serve --model-path meta-llama/Llama-3.1-8B-Instruct \
  --chunked-prefill-size 8192

👉 没有通用的最佳分块大小。它取决于模型、GPU、提示长度分布以及你的SLO(服务等级协议)衡量的延迟指标。

结论

三种策略仅在决定批次的时机上有所不同,各自适应不同的工作负载形态。

对于输出长度可变的生产流量,连续批处理是唯一可持续的方案,所有主流引擎都默认启用此功能。

本文基于一个GPU事实:前向传播的瓶颈在于从内存读取权重,而非数学计算。

我曾撰文详细解析GPU工作原理:

Akshay @akshay_pachaar·2023年8月13日 文章《GPU如何真正工作》 大语言模型工程师需要的直觉理解,无需硬件手册背景。 读完后,量化、推测解码、连续批处理等技术将不再只是技巧列表… 12146802581K

文章从第一性原理解释内存与计算竞争的原因、硬件中这种差距的根源,以及什么使工作负载受限于内存带宽。无需任何背景知识,它是理解本文所有内容的基础。

敬请期待更多内容!

感谢阅读。

祝好!:)

相似文章

在连续批处理中实现异步性

Hugging Face Blog

本文解释了如何为LLM推理实现异步连续批处理,将CPU批处理准备与GPU计算重叠,以最大化利用率并减少空闲时间。