3、Batch Size的物理意义:吞吐量与延迟的跷跷板效应

聊到Batch Size,很多人第一反应就是「调大一点,跑得快」。嗯,这话没错,但只说对了一半。

我个人习惯把Batch Size看作一个资源分配器。它决定了你手里的计算资源(GPU显存、算力)怎么分给每个样本。你给每个样本分得越少,一次处理的样本就越多——这就是吞吐量。但代价呢?每个样本等得越久——这就是延迟。

说白了,这就是一个跷跷板。一头压下去,另一头就翘起来。

3.1 吞吐量:你到底有多快?

吞吐量,单位时间能处理多少样本。公式很简单:

吞吐量 = Batch Size × 每秒处理的Batch数

你可能会想:「那我Batch Size翻倍,吞吐量不就翻倍了?」

理想很丰满,现实很骨感。我在项目中遇到过这种情况:把Batch Size从32调到64,吞吐量只涨了60%。为什么?因为GPU的利用率不是线性的。

GPU就像一条流水线。小Batch时,流水线经常「空转」——计算单元等着数据加载。大Batch时,流水线填满了,但内存带宽又成了瓶颈。

我建议你记住这个经验值:

Batch Size范围 吞吐量增长趋势 瓶颈在哪
1 ~ 8 接近线性增长 GPU利用率低
8 ~ 64 亚线性增长(60%~80%) 内存带宽
64 ~ 256 增长缓慢(20%~40%) 计算单元饱和
256+ 几乎不再增长 显存容量

核心结论:吞吐量不是越大越好。找到那个「拐点」——再增大Batch Size,吞吐量几乎不涨了——才是最优解。

3.2 延迟:每个样本的等待时间

延迟,就是单个样本从进来到出去的时间。你想想看,Batch Size越大,一个样本就得等整个Batch处理完才能出去。这不就慢了嘛。

延迟分两种:

  • 计算延迟:模型真正算这个样本的时间。这部分基本固定,跟Batch Size关系不大。
  • 排队延迟:样本在队列里等前面样本处理完的时间。这部分跟Batch Size强相关。

举个例子。假设你处理一个样本需要10ms,Batch Size是32。那么:

  • 第一个样本:等0ms + 算10ms = 10ms
  • 第16个样本:等150ms + 算10ms = 160ms
  • 第32个样本:等310ms + 算10ms = 320ms

看到了吗?最后一个样本的延迟是第一个的32倍!

注意:如果你做的是实时推理(比如在线推荐、语音识别),延迟就是生命线。我曾经见过一个团队把Batch Size从64调到128,吞吐量涨了15%,但P99延迟直接翻倍——线上服务直接超时报警。得不偿失。

3.3 跷跷板效应:怎么找到平衡点?

吞吐量和延迟,就像鱼和熊掌。你不可能同时拿到最大吞吐量和最低延迟。

我个人的经验是:先明确你的业务场景,再决定往哪边倾斜。

场景一:离线批处理

比如训练模型、数据清洗。延迟无所谓,吞吐量越高越好。那就往死里怼Batch Size,怼到显存快爆为止。

# 伪代码示例:找到最大Batch Size
max_batch_size = 1
while True:
    try:
        model(batch_size=max_batch_size * 2)
        max_batch_size *= 2
    except RuntimeError:  # OOM
        break
print(f"最大Batch Size: {max_batch_size}")

场景二:在线实时服务

比如API接口、游戏AI。延迟有硬性要求(比如<100ms),那就得牺牲吞吐量。我建议从Batch Size=1开始,慢慢往上加,直到延迟接近阈值。

# 伪代码示例:找到满足延迟的Batch Size
latency_threshold = 100  # ms
batch_size = 1
while True:
    latency = measure_latency(batch_size)
    if latency > latency_threshold:
        break
    batch_size += 1
print(f"最大可用Batch Size: {batch_size - 1}")

场景三:混合场景

大部分场景其实都是混合的。既要吞吐量,又不能太慢。这时候我建议用动态Batch——根据当前负载自动调整。

一个小技巧:我曾经在广告推荐系统里用过「自适应Batch」。做法很简单:监控队列长度,队列短就用大Batch(提高吞吐),队列长就用小Batch(降低延迟)。效果出奇的好,吞吐量提升了30%,P99延迟还降了20%。

3.4 一张图看懂:吞吐量与延迟的跷跷板

下面这张SVG图,把整个逻辑串起来了。你一看就明白。

Batch Size:吞吐量与延迟的跷跷板效应 小Batch 高延迟 (每个样本等得久) 低吞吐量 (一次处理少) 大Batch 高吞吐量 (一次处理多) 低延迟 (每个样本等得短) 平衡点 跷跷板两端: 延迟 ↑ → 吞吐量 ↓ 吞吐量 ↑ → 延迟 ↑

这张图很直观。左边是小Batch,延迟高但吞吐量低;右边是大Batch,吞吐量高但延迟也高。中间那个黄色圆点,就是你要找的平衡点。

3.5 避坑指南

最后,分享几个我踩过的坑:

  • 别只看平均值:平均延迟好看,但P99延迟可能已经炸了。我建议重点关注P99甚至P999。
  • 显存不是唯一限制:有时候显存没爆,但内存带宽先爆了。用nvidia-smi看看GPU利用率,如果一直100%但吞吐量不涨,那就是带宽瓶颈。
  • 动态Batch不是万能药:它增加了系统复杂度。如果你的负载很稳定,固定Batch Size反而更靠谱。

一句话总结:Batch Size没有「最佳值」,只有「最适合你场景的值」。先搞清楚你要吞吐量还是低延迟,再动手调。别盲目跟风,也别死守一个值。

好了,这一章就到这里。记住跷跷板效应,下一章我们聊聊怎么用工具实际测量这些指标。

可可精品资料库