吞吐与显存概述

吞吐量和显存,这两个词在AI系统里就像一对欢喜冤家。我刚开始做模型部署时,总觉得只要把吞吐量拉满就行,结果显存爆了,模型直接崩掉。后来才明白——吞吐是速度,显存是空间,两者必须一起考虑。

什么是吞吐量?

吞吐量,说白了就是单位时间内能处理多少数据。在AI推理场景里,常用的是每秒处理的样本数(Samples per Second)。

举个例子:你有一个图像分类模型,1秒钟能处理100张图片,那吞吐量就是100 samples/s。

吞吐量的核心公式

Throughput = Batch Size × Batch数 / 时间

或者更直观的:吞吐量 = 总样本数 / 总耗时

我遇到过不少团队,只盯着单张图片的推理延迟看。延迟低不代表吞吐高,你想想看——如果一次只处理1张图,延迟是10ms,但一次处理32张图,延迟可能变成50ms。算下来,后者吞吐量反而更高。

什么是显存?

显存(VRAM)就是GPU上那块高速内存。模型参数、中间激活值、优化器状态、梯度……全得塞进去。

显存消耗主要来自三块:

  • 模型参数:权重和偏置,这个基本固定
  • 激活值:前向传播时每层的输出,跟batch size成正比
  • 临时缓冲区:框架内部的各种缓存

我记得有一次调一个LLM推理服务,显存占用从12GB飙到22GB,排查了半天——原来是激活值缓存没释放。嗯,这里要注意,显存不是无限用的,爆了就OOM,服务直接挂。

为什么两者需要协同优化?

这个问题其实很现实。你单独优化吞吐,可能会把显存撑爆;你单独省显存,吞吐又上不去。

我画了一张图,帮你理清它们的关系:

吞吐与显存协同关系图 吞吐量(Throughput) 单位时间处理样本数 受Batch Size、计算效率影响 追求:越大越好 显存(VRAM) GPU上的高速内存 存储参数、激活值、梯度 限制:容量有限,爆了就崩 协同优化目标 在显存约束下,最大化吞吐量 或:在目标吞吐下,最小化显存占用 核心手段:显存复用、计算与传输重叠 相互制约 影响 影响

从图上你能看到,吞吐和显存是相互制约的。增大Batch Size能提升吞吐,但显存占用也线性增长。反过来,用梯度累积省显存,又会让吞吐下降。

我的经验之谈

做吞吐优化时,我习惯先摸清显存天花板。比如一张A100有80GB显存,模型参数占20GB,那留给激活值的空间就是60GB。在这个范围内,尽量把Batch Size推到最大。

但别死板——有时候显存没爆,但计算单元已经跑满了,再增大Batch Size也没用。这时候就该考虑计算与传输重叠了。

一个简单的例子

假设你有个Transformer模型,单卡推理:

# 伪代码示意
model = load_model()  # 参数占 10GB

for batch in dataloader:
    # 激活值占 2GB(batch_size=8)
    output = model(batch)  
    # 处理结果...

如果显存总共16GB,那batch_size=8时,总占用12GB,还有4GB余量。你试试把batch_size提到16——激活值变成4GB,总占用14GB,依然安全。但提到32,激活值8GB,总占用18GB,直接OOM。

注意

显存占用不是线性的。框架内部还有各种临时缓冲区、CUDA context的开销。我曾经遇到过,明明算出来只占14GB,实际跑起来却报了OOM——就是因为框架额外预留了2GB的缓存。

所以,永远留10%~20%的显存余量,别卡着上限跑。

协同优化的核心思路

说白了就两句话:

  1. 让显存里的数据被充分利用——别加载了参数只算一次就扔掉
  2. 让计算单元别闲着——数据加载和计算尽量重叠

具体怎么做?后面章节会详细讲。这里先记住一个原则:吞吐和显存不是二选一,而是跷跷板的两端,你要找到那个平衡点。

我个人习惯,每次调优前先画一张显存-吞吐的二维图,标出当前系统的位置,再决定往哪个方向调。你试试看,比盲目调参有效得多。


想要更多资料,联系可可