一、批处理基础概念:从单挑到群殴的艺术
大家好,我是你们的老朋友。今天咱们聊聊批处理——这个看似简单,实则门道极多的概念。
说实话,我刚接触大模型推理时,也觉得批处理不就是把几个请求攒一起处理嘛,有啥好讲的?直到我在生产环境里踩了坑,才发现这里面的水有多深。
1.1 什么是批处理?
批处理,说白了就是「一次处理多个请求」。
想象一下你开了一家奶茶店:
- 单条处理:来一个客人做一杯,做完再接下一单
- 批处理:等攒够5个订单,一次性做5杯
在vLLM里,批处理就是把多个用户的推理请求打包,一起喂给GPU计算。这样做的好处很明显——GPU的并行计算能力被充分利用了。
核心原理:GPU擅长并行计算,单条请求往往喂不饱它。批处理相当于把多个「小任务」合并成「大任务」,让GPU满负荷运转。
1.2 动态批处理 vs 静态批处理
这里有个关键区别,我当年可是花了不少时间才搞明白。
静态批处理
静态批处理,就是提前定好批大小,雷打不动。
# 伪代码示例
batch_size = 8 # 固定值
while True:
requests = collect_n_requests(batch_size) # 必须凑够8个
process_batch(requests)
我在早期项目里用过这种方式。优点是实现简单,缺点是——如果请求不够,GPU就在空转;如果请求太多,后面的请求就得排队等着。
我曾经踩过的坑:有一次线上流量波动大,静态批处理导致延迟忽高忽低。高峰期请求积压,低峰期GPU利用率不到30%。
动态批处理
动态批处理就灵活多了。它会根据当前负载,自动调整批大小。
# 伪代码示例
max_batch_size = 8
max_wait_time = 50 # 毫秒
while True:
requests = collect_requests(max_wait_time) # 等50ms,能收多少收多少
if len(requests) > 0:
process_batch(requests[:max_batch_size])
动态批处理的核心思路是:要么攒够数量,要么等够时间。哪个条件先满足,就立即处理。
我的建议:生产环境首选动态批处理。虽然实现复杂一点,但能同时兼顾吞吐量和延迟。
1.3 批处理大小对吞吐量的影响
吞吐量,就是单位时间内能处理多少请求。批大小和吞吐量的关系,不是简单的线性增长。
我画了一张图,帮你理解这个关系:
从图上你能看到:
- 快速增长区:批大小从1增加到4,吞吐量几乎翻倍。因为GPU终于吃饱了。
- 饱和区:批大小超过8后,吞吐量增长变缓。GPU的计算资源接近极限。
- 拐点:每个模型都有一个最佳批大小。超过这个值,收益微乎其微。
实战经验:我测试过一个7B模型,批大小从1提到4,吞吐量提升了3倍;但从8提到16,只提升了20%。所以别盲目追求大batch。
1.4 批处理延迟权衡
吞吐量上去了,延迟怎么办?这是个经典的trade-off。
咱们用表格对比一下:
| 批大小 | 单请求延迟 | 吞吐量 | 适用场景 |
|---|---|---|---|
| 1 | 低(50ms) | 低(20 req/s) | 实时对话、低延迟要求 |
| 4 | 中(80ms) | 中(80 req/s) | 一般在线服务 |
| 8 | 高(150ms) | 高(120 req/s) | 离线批处理、非实时任务 |
| 16 | 很高(280ms) | 很高(140 req/s) | 后台分析、批量生成 |
为什么会这样?原因有两点:
- 排队延迟:批处理需要等请求凑够,这个等待时间直接加到每个请求上。
- 计算延迟:批越大,单次计算耗时越长。虽然平均到每个请求的时间少了,但最慢的那个请求得等所有请求都算完。
注意:别只看平均延迟。在批处理场景下,P99延迟(最慢的1%请求)往往比平均延迟高好几倍。我见过一个案例,平均延迟100ms,但P99延迟飙到了500ms。
1.5 如何找到最佳批大小?
这个问题没有标准答案。我分享一个我常用的方法:
# 最佳批大小搜索脚本(伪代码)
for batch_size in [1, 2, 4, 8, 16, 32]:
throughput = benchmark(batch_size)
latency_p99 = benchmark_latency(batch_size)
# 定义你的目标函数
score = throughput / latency_p99
print(f"批大小 {batch_size}: 吞吐量={throughput}, P99延迟={latency_p99}ms, 得分={score}")
我的经验:对于大多数LLM推理场景,批大小在4-8之间是个不错的起点。如果你的延迟要求高,就选小一点;如果追求吞吐量,就选大一点。但别超过16,除非你的GPU显存特别大。
嗯,批处理的基础概念就聊到这儿。记住一句话:批处理不是越大越好,找到那个平衡点才是关键。