吞吐量定义:公式与影响因素
吞吐量,英文叫 Throughput。说白了,就是系统在单位时间内能处理多少请求。
我习惯把它理解成「系统的出活速度」。你想想看,压测的目的不就是看系统到底能扛多少并发、每秒能处理多少任务吗?这个「每秒处理的任务数」,就是吞吐量。
吞吐量的计算公式
公式其实很简单,但不同场景下写法略有不同。我直接给你最通用的版本:
Throughput = 总请求数 / 总耗时
举个例子:
- 你发了 10000 个请求
- 从第一个请求发出到最后一个请求返回,总共花了 50 秒
- 那么吞吐量 = 10000 / 50 = 200 请求/秒
但在 GPU 显存瓶颈的场景下,我更常用的是另一个公式:
Throughput = BatchSize × 每秒处理的迭代次数
为什么用这个?因为显存瓶颈直接限制了 BatchSize 的大小。你 BatchSize 上不去,吞吐量自然就卡死了。我在调优大模型推理时,经常盯着这个公式看——BatchSize 提不上去,其他优化都是白搭。
核心要点:吞吐量 = 并发能力 × 处理速度。显存瓶颈主要卡的是「并发能力」这一项。
影响吞吐量的因素
影响吞吐量的因素很多,我挑几个跟显存强相关的重点讲。
1. BatchSize 大小
这个最直接。BatchSize 越大,单次推理能处理的数据越多,吞吐量理论上就越高。
但问题来了——显存是有限的。我遇到过好几次,BatchSize 设得太大,直接 OOM(显存溢出),程序崩了。你想想看,吞吐量直接归零。
所以 BatchSize 的选择是个权衡:
- 太小:GPU 利用率低,吞吐量上不去
- 太大:显存爆了,直接挂掉
- 合适:刚好填满显存,又不溢出
我的经验:一般从 BatchSize=1 开始,逐步翻倍往上试,直到显存占用达到 90% 左右。留 10% 的余量给临时变量和碎片。
2. 模型大小与显存占用
模型本身占用的显存是固定的。参数越多,显存占用越大,留给 Batch 的空间就越少。
举个例子:
| 模型参数量 | 模型显存占用 | 可用显存(假设 24GB) | 最大 BatchSize |
|---|---|---|---|
| 7B | ~14GB | ~10GB | 8~16 |
| 13B | ~26GB | 不够用 | 需要量化或分布式 |
| 70B | ~140GB | 完全不够 | 必须多卡或量化 |
你看,模型越大,留给 Batch 的空间就越小,吞吐量自然受限。
3. 输入输出序列长度
这个很多人容易忽略。在 NLP 任务里,输入文本越长,显存占用就越大。特别是 Transformer 架构,显存占用跟序列长度是平方关系。
我踩过这个坑:有一次压测,输入长度从 512 增加到 1024,显存占用直接翻了 3 倍多。BatchSize 不得不从 32 降到 8,吞吐量直接掉了 75%。
注意:序列长度对显存的影响不是线性的,是 O(n²) 级别的。长文本场景下,显存瓶颈会非常明显。
4. 显存带宽
显存带宽决定了数据从显存搬运到计算单元的速度。带宽不够,计算单元就得等着数据,吞吐量自然上不去。
不同 GPU 的显存带宽差异很大:
- A100: ~2TB/s
- V100: ~900GB/s
- RTX 3090: ~936GB/s
- RTX 4090: ~1.0TB/s
我做过对比测试:同样的模型和 BatchSize,A100 比 V100 的吞吐量高了 30% 以上。原因就是显存带宽翻了一倍多。
5. 显存碎片化
这个比较隐蔽。频繁申请和释放显存,会导致显存碎片化。碎片多了,明明总显存还有剩余,但就是分配不出一个连续的大块内存。
我曾经遇到一个情况:显存占用显示只有 70%,但 BatchSize 稍微大一点就 OOM。查了半天,发现是显存碎片太多。后来用了显存池(memory pool)才解决。
知识体系图
下面这张图总结了吞吐量的核心逻辑:
总结一下
吞吐量的核心公式就两个:
- 总请求数 / 总耗时 —— 宏观视角
- BatchSize × 迭代次数/秒 —— 微观视角,适合显存瓶颈分析
影响吞吐量的因素里,跟显存最相关的就是 BatchSize、模型大小、序列长度和显存带宽。这四个因素互相制约,你调一个,其他几个可能跟着变。
嗯,我个人建议你在做压测时,先把显存占用监控起来。看到显存快满了,先别急着优化代码,看看是不是 BatchSize 设太大了,或者序列长度太长。很多时候,问题就出在这些基础参数上。
避坑指南:我曾经为了追求高吞吐量,把 BatchSize 调到显存占用的 98%。结果跑了一个小时,显存碎片越来越多,最后 OOM 了。现在我都控制在 85%~90%,留点余量给系统抖动。
想要更多资料,联系可可