一、显存基础与瓶颈分析
显存这东西,说白了就是GPU的「工作台」。你模型多大、batch size开多高,全看它脸色。我刚开始做大模型推理优化时,吃过不少显存的亏——明明代码逻辑没问题,一跑就OOM,查半天发现是显存分配策略不对。
这一章,咱们把显存的底裤扒干净。从架构到带宽,从溢出原因到分析工具,一条龙讲透。
1.1 显存架构:GPU的「内存条」长什么样?
现代GPU的显存,主流是GDDR6或HBM2e/HBM3。你想想看,显存和CPU内存最大的区别是什么?
- 带宽优先:显存追求的是超高带宽,而不是低延迟。HBM2e的带宽能到2TB/s以上,但延迟比DDR5高不少。
- 层级结构:GPU内部有L1/L2缓存,显存是最后一级。数据从显存读到寄存器,要走好几层。
- 多通道并行:显存控制器通常有多个通道,数据可以并行读写。
核心要点:显存带宽 = 显存频率 × 位宽 × 通道数。别只看显存大小,带宽才是推理吞吐的关键。
我在项目中遇到过,有人买了48GB显存的A6000,结果跑LLM推理时吞吐还不如24GB的A10G。为什么?因为A6000的显存带宽只有768GB/s,而A10G有600GB/s,但A10G的架构更优,小batch下反而更快。
1.2 带宽与延迟:为什么显存是瓶颈?
推理时,GPU计算单元(SM)跑得飞快,但数据从显存搬过来需要时间。这就是经典的「内存墙」问题。
| 指标 | CPU内存 (DDR5) | GPU显存 (HBM2e) | GPU显存 (GDDR6) |
|---|---|---|---|
| 带宽 | ~50 GB/s | ~2 TB/s | ~600 GB/s |
| 延迟 | ~100 ns | ~300-500 ns | ~200-300 ns |
| 容量 | 128-512 GB | 16-80 GB | 8-48 GB |
看到了吗?显存带宽比CPU内存高一个数量级,但延迟也更高。为什么会这样?因为显存要服务几百个SM同时访问,设计上优先保证吞吐,延迟只能妥协。
我个人习惯,在优化推理时先看「计算密度」。如果算力利用率低,多半是显存带宽卡脖子。举个例子,LLM推理时,每个token都要读取整个模型权重,这完全是带宽敏感型任务。
避坑指南:我曾经在优化一个70B模型时,发现显存带宽利用率只有30%。查了半天,原来是数据排布不对——没有用连续内存访问模式。改成连续读取后,带宽利用率直接飙到85%。
1.3 显存溢出原因:为什么你的模型跑不起来?
显存溢出(OOM)是推理时最常见的错误。我见过太多人一上来就问「为什么我的模型显存不够?」。其实原因就那么几个:
- 模型权重太大:一个70B的FP16模型,光权重就要140GB。单卡根本放不下。
- KV Cache爆炸:LLM推理时,KV Cache随序列长度线性增长。1024长度的序列,KV Cache可能占几十GB。
- 中间激活值:推理时虽然不存梯度,但中间激活值(比如LayerNorm的输出)还是会占显存。
- 碎片化:频繁申请释放显存,会导致碎片化。明明总显存够,但就是分配不出连续块。
注意:别以为模型小就安全。我遇到过有人用7B模型,batch size开到128,结果KV Cache直接撑爆24GB显存。算一下:7B模型权重约14GB(FP16),KV Cache每层2×seq_len×hidden_size×batch_size,128 batch下轻松超过10GB。
你想想看,显存溢出其实是个「组合问题」。权重、KV Cache、激活值,三者加起来超过显存容量,就OOM。优化思路无非三个方向:
- 减少权重占用(量化、剪枝)
- 减少KV Cache(PagedAttention、MQA/GQA)
- 减少激活值(Checkpointing、重计算)
1.4 性能分析工具:Nsight Systems 实战
光靠猜不行,得用工具。Nsight Systems是我最常用的GPU性能分析工具,没有之一。
它的核心能力就两个:
- 时间线视图:看CPU和GPU的调度情况,哪个kernel在跑,花了多少时间。
- 带宽/算力利用率:看显存带宽和计算单元的利用率,找出瓶颈。
我建议你养成一个习惯:每次跑推理前,先开Nsight Systems抓一轮profile。哪怕只跑一个batch,也能看出很多问题。
举个例子,下面是一个简单的profile命令:
# 抓取GPU kernel和内存操作
nsys profile -o my_profile --trace=cuda,nvtx,osrt \
python run_inference.py --model llama-7b --batch_size 1
# 生成可视化报告
nsys stats my_profile.qdrep -o my_stats.csv
跑完之后,用Nsight Systems GUI打开.qdrep文件。你会看到类似这样的时间线:
- CPU端:数据加载、预处理、模型调用
- GPU端:kernel执行、内存拷贝、同步操作
我个人习惯重点关注两个指标:
- GPU利用率:如果低于80%,说明CPU端在拖后腿(数据加载慢、预处理瓶颈)。
- 显存带宽利用率:如果低于50%,说明kernel访存模式有问题(非连续访问、bank conflict)。
实战技巧:我曾经优化一个对话模型,发现GPU利用率只有40%。用Nsight Systems一看,原来是CPU在等数据加载——每次推理前都要从磁盘读tokenizer和预处理数据。改成预加载+缓存后,GPU利用率直接到90%。
嗯,这里要注意:Nsight Systems的overhead很小,但profile时还是会慢一点。生产环境建议只抓关键kernel,别全量profile。
知识体系总览
下面这张图,把本章的核心逻辑串起来了。从显存架构到瓶颈分析,再到工具实战,一条线走通。
这张图你看懂了吗?从左到右,从显存架构出发,引出带宽延迟问题,再落到溢出原因,最后用工具闭环。说白了,整个优化流程就是「测量-分析-优化」的循环。
我个人习惯,每次拿到一个新模型,先跑Nsight Systems看带宽利用率。如果低于60%,直接优化访存模式;如果高于80%,再考虑量化或剪枝。这样效率最高。
小提示:Nsight Systems的GUI里有个「GPU Utilization」视图,能直接看到SM占用率和显存带宽利用率。我建议你把这个视图固定到常用面板,每次profile都先看它。
好了,显存基础就聊到这儿。记住一句话:显存是推理的命门,带宽是瓶颈的核心。下一章咱们聊量化——怎么用更少的bit存权重,还不掉精度。