3、基础工具:使用nvidia-smi查看显存状态,解读关键指标
排查显存碎片,第一步不是写代码,而是先看看显存到底怎么了。
我个人习惯,遇到任何显存问题,第一件事就是打开终端,敲一行命令:
nvidia-smi
这玩意儿就像医生的听诊器。不用它,你连病人什么状况都不知道。
3.1 显存到底被谁吃了?
先看一个典型的输出:
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 525.85.05 Driver Version: 525.85.05 CUDA Version: 12.0 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|===============================+======================+======================|
| 0 Tesla T4 Off | 00000000:00:1E.0 Off | 0 |
| N/A 42C P0 28W / 70W | 3245MiB / 15360MiB | 0% Default |
| | | N/A |
+-------------------------------+----------------------+----------------------+
关键看这一行:
| 3245MiB / 15360MiB |
左边是已用显存,右边是总显存。3245MiB 被用了,还剩 12GB 多。
嗯,这里要注意:这个数字是当前时刻的瞬时值。它不会告诉你碎片率,但能告诉你显存是不是快满了。
核心判断标准:如果已用显存接近总显存(比如超过90%),碎片问题大概率已经发生了。我在项目中遇到过,显存占用85%时还能跑,到了92%突然 OOM,其实就是碎片在作祟。
3.2 进程级别的显存占用
光看总量不够。你得知道每个进程吃了多少。
加个参数:
nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv
输出长这样:
pid, process_name, used_memory [MiB]
12345, python3, 2048
12346, python3, 1024
12347, python3, 128
看到那个 128 MiB 的进程了吗?
我曾经排查过一个案例,有个进程只占了 128 MiB,但它卡在显存中间,导致旁边的大块连续空间无法分配。这就是典型的碎片制造者。
我的排查习惯:先看有没有「小进程占着茅坑不拉屎」。如果有,kill 掉它,往往能释放出大块连续空间。
3.3 显存碎片的核心指标:Memory-Usage 的波动
只看一次 nvidia-smi 是不够的。碎片是动态的。
我建议你连续观察:
watch -n 1 nvidia-smi
每秒钟刷新一次。注意看 Memory-Usage 那列。
如果出现以下现象,基本可以判定碎片严重:
- 显存占用不高,但分配大张量时报错。比如只用了 6GB,但想分配一个 4GB 的 tensor 却 OOM。
- 显存占用忽高忽低。每次分配释放后,总量没变,但可用空间越来越小。
- 重启进程后显存占用变少。同样的代码,重启后显存占用少了 500MB,说明之前有碎片残留。
注意:nvidia-smi 显示的「已用显存」是操作系统视角的物理分配量。它不区分「正在使用的数据」和「已经释放但未归还给操作系统的缓存」。说白了,PyTorch 的缓存分配器可能已经释放了 tensor,但显存还在进程手里,nvidia-smi 依然算作「已用」。
3.4 更精细的排查:nvidia-smi 的扩展命令
基础版看完了,咱们上点硬货。
我个人常用这个命令来获取更细粒度的信息:
nvidia-smi dmon -s pucvmet
这个命令会持续输出:
| 列名 | 含义 | 碎片排查中的价值 |
|---|---|---|
| p | 电源状态 | 低功耗模式可能影响显存带宽 |
| u | GPU 利用率 | 利用率低但显存高,可能是碎片导致计算停滞 |
| c | 上下文切换 | 频繁切换可能暗示显存分配失败后的重试 |
| v | 显存利用率 | 直接看显存是否被占满 |
| m | 显存带宽利用率 | 带宽低但显存高,碎片导致无法有效利用带宽 |
| e | ECC 错误 | 硬件层面的错误,可能加剧碎片问题 |
| t | 温度 | 温度过高可能导致显存降频,影响分配效率 |
你想想看,如果 u(GPU利用率)只有 20%,但 v(显存利用率)到了 95%,这基本就是碎片在作妖了。GPU 在等显存分配,但显存被碎片堵死了。
3.5 避坑指南:nvidia-smi 的局限性
用了这么多年,我得说句实话:nvidia-smi 不是万能的。
- 它看不到碎片率。nvidia-smi 只告诉你「用了多少」,不告诉你「碎片有多碎」。
- 它看不到进程内部的分配细节。比如 PyTorch 的缓存分配器内部有多少空闲块,nvidia-smi 一概不知。
- 它只能看瞬时值。碎片是动态演变的,你需要结合时间序列数据来判断。
我的经验:nvidia-smi 是排查碎片问题的「第一道防线」。它帮你快速定位「有没有问题」,但「问题在哪、怎么解决」,需要结合更深入的工具,比如后面会讲到的 cuda-memcheck 和 nsys。
3.6 一张图看懂 nvidia-smi 在碎片排查中的位置
下面这张图,是我自己总结的排查流程。你可以把它当作一个思维导图:
这张图想表达的核心意思是:nvidia-smi 是起点,不是终点。它帮你快速定位「显存是不是紧张了」,但碎片的具体位置和成因,需要后续章节的工具来深挖。
一个小技巧:把 nvidia-smi 的输出重定向到文件,每隔 10 秒记录一次。跑完一个训练任务后,用 Python 画个显存占用曲线。如果曲线像锯齿一样忽高忽低,碎片大概率跑不了。
好了,nvidia-smi 的基础用法就这些。说白了,它就是你的第一双眼睛。用好了,能省下大量排查时间。
想要更多资料,联系可可