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 显存总量 vs 已用量 进程级显存占用详情 连续监控(watch) 判断是否接近 OOM 阈值 查找「小进程占大坑」 观察显存波动规律 结论:nvidia-smi 只能告诉你「有没有问题」 不能告诉你「碎片在哪、有多碎」 需要更深入的工具 →

这张图想表达的核心意思是:nvidia-smi 是起点,不是终点。它帮你快速定位「显存是不是紧张了」,但碎片的具体位置和成因,需要后续章节的工具来深挖。

一个小技巧:把 nvidia-smi 的输出重定向到文件,每隔 10 秒记录一次。跑完一个训练任务后,用 Python 画个显存占用曲线。如果曲线像锯齿一样忽高忽低,碎片大概率跑不了。

好了,nvidia-smi 的基础用法就这些。说白了,它就是你的第一双眼睛。用好了,能省下大量排查时间。

想要更多资料,联系可可