欢迎光临
我们一直在努力

Continuous Batching 为什么比静态批处理强?拆解端到端吞吐量翻倍的秘密

在大型语言模型(LLM)部署和推理服务中,吞吐量(Throughput)是衡量服务效率的关键指标。传统的静态批处理(Static Batching, SB)方法在处理高并发请求时暴露出了严重的效率问题。而近年来,以vLLM为代表的框架所采用的连续批处理(Continuous Batching, CB)技术,能够显著提升端到端吞吐量,甚至实现翻倍的效果。

什么是静态批处理(Static Batching)及其瓶颈

静态批处理是早期LLM服务中常用的方法。它将一定数量(例如N个)的请求聚合在一起,形成一个固定大小的批次,然后一次性送入GPU进行计算。只有当批次中所有请求都生成完毕,新的请求才能开始处理。

这种方法的瓶颈主要体现在两个方面:

1. 填充浪费(Padding Waste)

LLM推理需要处理不同长度的输入和输出序列。在静态批处理中,为了让GPU能并行处理,所有序列必须被填充(Padding)到批次中最长序列的长度。如果批次中大部分请求的长度远小于最大长度,GPU就会浪费大量算力来计算这些填充的占位符。

2. 队头阻塞(Head-of-Line Blocking, HoL Blocking)

LLM推理是迭代的:每一步只生成一个Token。如果一个批次中有9个短请求和1个极长请求,那么这9个短请求即使已经完成了所有Token的生成,也必须等待那个最长的请求完全生成完毕后,整个批次才能释放GPU资源给新的请求。这种等待就是HoL Blocking,导致GPU在许多请求实际完成后处于低效或空闲状态。

连续批处理(Continuous Batching)的工作原理

Continuous Batching 旨在解决 HoL Blocking 和 Padding Waste 问题,其核心思想是:以Token为粒度进行调度,而不是以完整请求为粒度。

1. 动态调度与Token级迭代

CB 不等待整个批次完成。它在每一步(每次生成一个Token)后,都会检查当前批次中哪些序列已经完成(例如,生成了EOS token或达到了最大长度)。一旦有序列完成,该序列立即被踢出批次,并且队列中等待的新请求会立即被动态插入到这个空出的位置,从而形成一个新的、更紧凑的批次。

2. PagedAttention:内存管理的核心

实现高效的连续批处理,必须解决内存管理问题,特别是如何高效管理KV Cache(键值缓存)。传统的静态批处理中,KV Cache是为最大可能长度预先分配的,导致内存浪费。

vLLM框架引入的 PagedAttention 机制(类似于操作系统中的分页内存管理)是CB高效运行的关键。它将KV Cache分割成固定大小的“块”(Blocks),这些块不需要连续存储。这带来了两个巨大优势:

  • 消除内部碎片: 序列只分配其需要的块,而不是整个预分配的连续内存,极大地提高了内存利用率。
  • 零拷贝动态调度: 当一个请求完成或加入时,只需要管理块的指针,而不需要进行大规模的数据拷贝或重新排列,使得批次切换和动态插入请求的成本极低。

拆解吞吐量翻倍的秘密

Continuous Batching 能够显著提升吞吐量的秘密在于它极大地提升了 GPU 的占有率(Occupancy)有效计算时间

特性 静态批处理 (SB) 连续批处理 (CB)
调度粒度 完整请求 Token/迭代
内存管理 连续预分配 (KV Cache) 分页管理 (PagedAttention)
浪费的主要原因 Padding Waste, HoL Blocking 极少
GPU 状态 频繁空闲(等待最长序列) 持续高负载(动态填充)

通过连续批处理,GPU几乎总是在处理实际有效的Token计算,避免了因等待最长序列或计算填充Token而导致的空闲时间。这使得在相同的时间内,GPU能够处理更多的并发请求,从而使端到端吞吐量得到显著提升。

实践:使用 vLLM 体验连续批处理

vLLM 是目前最流行的实现了 Continuous Batching 框架之一。在实践中,使用 vLLM 部署模型,几乎是体验到 CB 性能提升的最简单方法。

1. 安装 vLLM

pip install vllm

2. 启动 API 服务器(默认启用 CB)

以下命令启动一个基于 Mistral-7B 模型的推理服务器。vLLM 默认使用 PagedAttention 和 Continuous Batching。

python -m vllm.entrypoints.api_server --model mistralai/Mistral-7B-Instruct-v0.2 --port 8000

3. 异步并发请求测试

为了展示 CB 带来的高并发能力,我们可以使用异步客户端发送多个请求,即使它们长度不同,vLLM 也能高效地在每次迭代中切换和处理它们,最大化 GPU 资源利用。

import requests
import json
import time

# 定义一个发送请求的函数
def generate_request(prompt, max_tokens=100):
    headers = {"Content-Type": "application/json"}
    data = {
        "prompt": prompt,
        "max_tokens": max_tokens,
        "temperature": 0.0
    }
    response = requests.post("http://localhost:8000/generate", headers=headers, data=json.dumps(data))
    return response.json()

# 并发发送不同长度的请求
start_time = time.time()

# 请求 A: 短请求
req_a = generate_request("介绍一下什么是大语言模型", max_tokens=30)
# 请求 B: 长请求
req_b = generate_request("详细描述Continuous Batching的工作机制和优势", max_tokens=150)
# 请求 C: 中等请求
req_c = generate_request("什么是HoL Blocking", max_tokens=50)

end_time = time.time()

print(f"总处理时间(并发模拟): {end_time - start_time:.2f} 秒")
# 在vLLM的CB机制下,短请求会先完成并退出批次,释放资源,实现高吞吐。

通过这种方法,CB 确保了无论请求队列中的序列长度如何分布,GPU都能保持高利用率,这是实现吞吐量翻倍的关键。

【本站文章皆为原创,未经允许不得转载】:汤不热吧 » Continuous Batching 为什么比静态批处理强?拆解端到端吞吐量翻倍的秘密
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址