先问个问题:你上一次盯着nvidia-smi输出发呆是什么时候?是不是发现显存占用很高、GPU-Util 却来回跳,训练速度上不去,却根本不知道瓶颈在哪儿?这就是 GPU 性能实时监控存在的意义——它不只是看那些百分比数字,而是帮你在训练、推理、压测和运维过程中,把"GPU 到底在干什么"这件事彻底看清楚。
这篇东西适合谁?刚配好深度学习环境的初学者、跑大模型训练的研究人员、管着几台甚至几十台 GPU 服务器的运维人员,还有做 GPU 驱动开发或性能调优的工程师。我会从最常用的命令工具讲到多机集群监控方案,给出完整可复现的命令和参数,也会穿插一些只有踩过坑才懂的经验,比如为什么有时候 GPU 利用率看着不错但训练就是慢,再比如trt-warn: unable to determine GPU memory usage这类警告到底意味着什么。
1. 显存爆了还是算力没吃满:GPU实时监控到底在看什么
很多人把 GPU 监控理解成"盯着显存和利用率看",这没错,但太粗糙了。GPU 是一个包含计算单元、显存控制器、缓存、视频编解码器等多个子系统的复杂处理器。你在终端敲nvidia-smi看到的几个数字,只是它抛给你的冰山一角。
1.1 监控的核心维度:不止是显存和利用率
实时监控 GPU 性能,本质上是监控这几类指标:
- SM 利用率(GPU-Util):显示流处理器(SM)的忙碌程度,但要注意它统计的是"某采样间隔内 SM 上至少有一个 warp 在执行指令的时间占比",不是算力被用了百分之多少。这意味着 50% 的利用率可能代表"计算时断时续",也可能代表"任务本身只用了部分 SM"。
- 显存占用与带宽:显存占用反映容量压力,显存带宽反映数据搬运压力。训练大模型时经常出现显存容量快满了,但带宽利用率并不高的状况,这时候单纯加显存不一定能提速。
- 温度与散热:GPU 核心温度超过 85℃ 甚至 90℃ 会产生热节流(thermal throttling),自动降低核心频率来保护硬件。这是最常见的"性能神秘下降"原因。
- 功耗与频率:通过
nvidia-smi -q -d POWER可以看到当前功耗、功耗上限,以及当前核心/显存频率。如果功耗撞墙(power cap)或频率上不去,说明供电或散热受限。 - PCIe 传输速率:数据从 CPU 内存搬到 GPU 显存要走 PCIe 总线,如果传输速率持续跑满,说明数据供给跟不上 GPU 计算速度,训练/推理会出现周期性空闲。
光知道这些还不够,关键是建立"数据流"的思维。一次深度学习训练的最小循环是:CPU 读取数据 → 预处理 → 拷贝到 GPU 显存 → GPU 计算 → 结果拷回。这个环节里任何一段瓶颈,都会反映在 GPU 监控数据上。比如 GPU-Util 反复跳动,通常不是 GPU 的问题,而是 CPU 数据供给的瓶颈。
1.2 什么样的监控频率才算"实时"
"实时"是个弹性概念。人眼观察习惯用watch -n 1 nvidia-smi,每秒刷新一次;性能剖析需要毫秒级采样,如nvprof、Nsight Systems;而集群资源管理只需秒级或分钟级采集,如 Prometheus 默认 15 秒抓取一次。
我的建议是分场景:临时排查问题用 0.5~1 秒刷新率;持续观察训练过程用 2~5 秒;做长期监控和告警用 15~60 秒。如果刷新频率太高,采集程序本身也会消耗一定 CPU 和 PCIe 带宽,反过来影响性能数据的准确性。
2. 工具也要分场景选:从单卡调试到集群运维的监控工具分层
GPU 监控工具五花八门,但不是越高级越好。选型的关键是匹配你当下的场景。我把常用工具按使用场景分成了四层,每一层解决不同的问题。
| 场景 | 推荐工具 | 特点 | 上手难度 |
|---|---|---|---|
| 单机快速查状态 | nvidia-smi | 系统自带,信息全,适合临时瞄一眼 | 低 |
| 单机交互式监控 | nvtop | 类htop界面,多卡一目了然 | 低 |
| 脚本/自动化采集 | gpustat、nvidia-smi --query-gpu | 输出结构化,方便解析 | 中 |
| 压测与稳定性验证 | gpu-burn | 满负荷压测,验证算力/散热 | 中 |
| 多机集群持久监控 | DCGM + Prometheus + Grafana | 指标全、可告警、可追溯 | 高 |
有些朋友一上来就搭 Grafana,结果发现日常训练中利用率波动根本不需要这么重的方案。反过来,管着几十台 GPU 服务器的人还在挨个机器敲nvidia-smi,那也是拿大炮打蚊子却打错了方向。我的一贯原则是:先用手边最快的工具定位问题,再决定要不要上重型监控系统。
下面几节我会按"日常使用频率从高到低"的顺序,逐个讲这些工具的实际用法和需要注意的细节。
3. nvidia-smi 的完整打开方式:一个老命令被你用成了皮毛
大多数人用nvidia-smi只是敲一下回车看个大概,其实这个命令包含了很多实用子命令。掌握好了,单机场景下它能覆盖 80% 的监控需求。
3.1 从静态输出开始:读懂每一行字段
裸敲nvidia-smi的输出分为几部分:
+-----------------------------------------------------------------------------+ | NVIDIA-SMI 525.85.12 Driver Version: 525.85.12 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 NVIDIA GeForce RTX 4090 | On | 00000000:01:00.0 On | N/A | | 0% 43C P8 18W / 450W | 1024MiB / 24564MiB | 0% Default | | | | N/A | +-------------------------------+----------------------+----------------------+几个容易被忽视的点:
- Persistence-M 状态:如果显示
Off,建议用nvidia-smi -pm 1开启持久模式,可以降低程序反复启动时 GPU 的初始化延迟。 - Perf 状态:P0 是最高性能状态,P8 是空闲状态。如果 GPU 有负载但 Perf 始终不是 P0,说明驱动或供电状态有问题。
- Pwr:Usage/Cap:当前功耗/最大功耗。RTX 4090 的 450W 是上限,训练时如果长期在 300W 以下波动,说明计算负载不均匀。
- GPU-Util:注意它包含图形与计算任务,但不会区分是图形渲染还是 CUDA 计算。做深度学习时建议用
nvidia-smi -c查看计算模式,或用nvidia-smi dmon看详细分类。
3.2 定时刷新:watch 与 dmon 的实战选择
要让信息动起来,最直接的办法是:
watch -n 1 nvidia-smi但watch的缺点是每次执行都是完整重绘,CPU 占用稍高,在内存紧张的机器上不太友好。更专业的做法是使用nvidia-smi dmon(设备监控模式):
nvidia-smi dmon -s pucvmet -d 1参数说明:
-s pucvmet:指定显示哪些指标,p=功耗、u=利用率、c=显存使用率、v=显存已用、m=显存总量、e=ECC、t=温度。-d 1:每 1 秒输出一次。
dmon的输出是流式文本,非常适合重定向到文件做性能日志:
nvidia-smi dmon -s pucvmet -d 5 > gpu_monitor.log &之后用tail -f gpu_monitor.log边训练边观察。比起watch在终端界面里闪烁,这种方式更适合放在后台长期运行。
3.3 参数化查询:脚本自动化的基础
如果要写脚本定时采集 GPU 指标,nvidia-smi --query-gpu比解析nvidia-smi的表格输出可靠得多:
nvidia-smi --query-gpu=index,name,utilization.gpu,memory.used,memory.total,temperature.gpu,power.draw --format=csv,noheader,nounits输出示例:
0, NVIDIA GeForce RTX 4090, 67, 1024, 24564, 43, 18用,分隔再配合 awk、python 或 pandas,就能轻松构建自己的监控脚本。我还习惯加上-l 1让它周期性输出,配合 tee 存入 CSV 文件:
nvidia-smi --query-gpu=index,utilization.gpu,memory.used,power.draw,temperature.gpu --format=csv,noheader -l 1 | tee gpu_history.csv提示:在容器内运行这条命令时,需要以
--gpus all方式启动容器,否则可能看不到 GPU 或报Unable to determine GPU memory usage之类的警告。docker run --gpus all是访问 GPU 设备节点的前提,不是可选项。
3.4 查进程:定位谁在占 GPU
多用户共用服务器时,经常需要找出"谁占了我的卡"。用:
nvidia-smi --query-compute-apps=pid,used_memory,process_name --format=csv,noheader或者直接nvidia-smi看下半部分的进程列表。结合ps -fp <pid>就能确认是哪个用户在跑什么程序。遇到僵尸进程占显存的情况,kill -9 <pid>是常用手段,但操作前建议先和进程所有者沟通一下,避免误杀别人的训练任务。
4. nvtop 和 gpustat:适合个人训练的轻量交互式监控
单机训练时,我不太建议频繁切到终端敲watch nvidia-smi,交互式的nvtop和一行一刷的gpustat体验会好很多。这两个工具都很轻,安装也不复杂。
4.1 nvtop:GPU 界的 htop
nvtop的界面长得很像 Linux 里的htop,但展示的是 GPU 数据。安装方式:
# Ubuntu/Debian sudo apt install nvtop # 其他发行版或想装新版 git clone https://github.com/Syllo/nvtop.git mkdir -p nvtop/build && cd nvtop/build cmake .. make sudo make install启动后直接nvtop。主界面按 GPU 分块展示:占用率、显存、功耗、温度、频率、风扇转速,底部还有进程列表和系统负载。支持鼠标点击排序,也可以按p切换进程视图。
我在实际使用中觉得它最实用的两个场景:
- 多卡对比:同时观察 8 张卡时,一眼能找出哪张卡温度异常高或利用率异常低。
- 快捷键过滤:按
g可以选择只看部分 GPU,按c切换色彩模式,长时间盯屏幕时护眼效果好。
需要留意的是,nvtop在很老的驱动版本或某些虚拟化环境下可能采集不到频率/功耗数据,显示 N/A。这时优先确认驱动版本是不是太旧,别急着怪工具。
4.2 gpustat:适合写进脚本和 SSH 登录提示
gpustat是 Python 写的小工具,核心 API 会调用nvidia-smi,但输出做了人性化处理:
pip install gpustat gpustat -cp输出示例:
k80vm01.local Thu May 18 10:23:45 2024 [0] NVIDIA RTX 4090 | 67% | 1024/24564 MB | python train.py(1024M)参数说明:
-c:显示命令名称。-p:显示进程 PID。-i:间隔几秒刷新,比如gpustat -i 2。--json:输出 JSON 格式,适合脚本解析。
我习惯把它写进.bashrc的登录提示里,让用户一登录服务器就能看到当前 GPU 占用和自己在跑的任务:
# ~/.bashrc 末尾加一行 gpustat --no-colorgpustat还支持把结果发到 Slack、微信等 Webhook,适合个人用小规模告警,比如"某张卡利用率跌到 0 超过十分钟"这种情况。它最大的优势就是轻——没有守护进程、没有数据库、没有额外依赖,装完就能跑。
5. gpu-burn 压力测试:验证算力与散热的极限状态
监控工具只能看到"当前状态",但如果你想确认一台 GPU 服务器在全负载下到底稳不稳,就得主动制造压力。gpu-burn是这一场景里最常用的工具。
5.1 gpu-burn 能解决什么问题
简单说,gpu-burn让 GPU 进入持续满负荷计算状态,用来验证:
- 算力是否正常:在相同 GPU 型号下,跑同一份压测代码,结果分数可以横向对比。分数明显偏低,说明 GPU 核心可能有故障或降频。
- 散热是否达标:持续满载会让温度爬升,如果很快就冲到 90℃ 甚至触发保护性关机,说明散热系统需要处理。
- 供电是否稳定:功耗持续维持在 TDP 上限,如果出现功率波动或掉驱动,大概率是电源或供电模块问题。
- 多卡之间是否有隐性故障:多卡同时压测时,某张卡崩掉或报 ECC 错误,这张卡基本可以标记为"待检修"。
5.2 编译与运行:实操步骤
代码在 GitHub 的wilicc/gpu-burn仓库,编译依赖 NVCC(CUDA Toolkit 自带):
git clone https://github.com/wilicc/gpu-burn.git cd gpu-burn make运行:默认压测所有 GPU,持续 10 秒:
./gpu_burn 60命令最后一位数字是持续秒数,我一般建议压测 30~60 分钟来看散热表现。也可以指定只压测某几张卡:
CUDA_VISIBLE_DEVICES=0,2 ./gpu_burn 300压测过程中的典型输出:
GPU 0 (Tesla T4): 145.6 GFLOPS GPU 1 (Tesla T4): 144.3 GFLOPS Test completed.同时另开一个终端跑nvtop或nvidia-smi dmon观察温度和功耗曲线。如果温度曲线持续上升后平稳在 70~80℃,功耗稳定在上限,说明整卡散热和供电都没问题。
注意:
gpu-burn会把 GPU 占用打到接近 100%,如果机器上有别人正在跑训练任务,压测前一定要先沟通,最好申请一个空闲的维护窗口再做。
5.3 压测结果的解读
压测不只是看有没有报错,还要关注两点:
一是分数稳定性。多次运行gpu_burn 60,结果波动在 ±2% 以内算正常。如果第一次 145 GFLOPS,第二次 120 GFLOPS,大概率是温度导致降频了,需要检查硅脂、风扇、灰尘,甚至机箱风道。
二是卡间一致性。相同型号的卡跑出来分数接近,如果某张卡差距明显,可以单独用nvidia-smi -q -d CLOCK看它的当前核心频率是否被锁定在较低档位。曾经有朋友遇到过一张 3090 因为坏了的风扇导致核心频率锁在 300MHz 左右,压测分数比其他卡低了 4 倍,靠的就是卡间对比才发现的问题。
6. DCGM + Prometheus + Grafana:多机多卡场景的持久化监控方案
当你手上管的 GPU 从个位数变成两位数以上,或者要监控 GPU 集群一段时间的性能变化趋势,单机的nvidia-smi和nvtop就不够了。这时候需要一套指标采集 → 存储 → 可视化的闭环方案,NVIDIA 官方的 DCGM(Data Center GPU Manager)是我推荐的首选数据源。
6.1 DCGM 是什么,和 nvidia-smi 有什么区别
nvidia-smi适合单机单次查询,DCGM 是为数据中心大规模管理设计的。它本身不是可视化工具,而是一个后台服务加一套指标模型,你要通过dcgmi命令行查询,或者用它的 Prometheus 导出器把指标暴露给监控系统。
DCGM 能提供的指标比nvidia-smi丰富得多,比如:
- SM 占用、内存控制器占用、FP32/FP16 单元利用率细分
- 显卡功耗、核心温度、显存温度、NVLink 带宽
- PCIe 收发速率、ECC 错误计数、Xid 错误
- 计算的"充分性"指标,比如是否存在"GPU 空闲但显存占用高"的状态
安装方式(Ubuntu):
sudo apt-get install -y datacenter-gpu-manager sudo systemctl enable --now nvidia-dcgm6.2 dcgmi 常用命令:快速验证
DCGM 自带dcgmi命令,可以做基本的健康检查和指标查询:
# 查看所有 GPU 的健康状态 dcgmi diag -r 1 # 查看 GPU 实时指标,类似 nvidia-smi dmon dcgmi dmon -e 100,101,102,103,104,105,110,111,112,113,114,115这里-e后面跟的是指标 ID,100 表示 GPU 利用率,101 表示 SM 利用率,102 表示显存占用,110 表示核心温度,111 表示功耗等。完整指标列表可以用dcgmi dmon --listmetrics -v查看。
我实际用下来,DCGM 相比裸敲nvidia-smi的最大价值是能够准确判断性能是否被正确利用。举个例子,DCGM 有一个指标叫DCGM_FI_DEV_GPU_UTIL(利用率),还有一个叫DCGM_FI_PROF_SM_OCCUPANCY(SM 占用率)。前者可能显示 100%,但后者可能只有 20%,说明任务虽然一直占用 GPU,但每个 SM 内部的执行单元并没有塞满。这种细粒度信息对分析算子性能瓶颈特别关键。
6.3 接入 Prometheus 和 Grafana
NVIDIA 官方提供了dcgm-exporter,把 DCGM 指标暴露为 Prometheus 格式:
git clone https://github.com/NVIDIA/dcgm-exporter.git cd dcgm-exporter make ./dcgm-exporter --address=:9400 &然后在 Prometheus 配置里加一个 job:
scrape_configs: - job_name: 'dcgm' static_configs: - targets: ['gpu-node-01:9400', 'gpu-node-02:9400']Grafana 里导入 NVIDIA 官方提供的 DCGM Dashboard(ID 号 12239),就能看到每张卡的利用率、温度、功耗、显存、NVLink 等一整套图表。这套方案我用了很久,最大的体会是"出问题不用猜了"——某个时间段训练变慢,直接在 Grafana 上叠加查看当时段的 GPU 频率、功耗、温度曲线,基本能找到原因。
6.4 集群场景的告警配置思路
长期监控必须配告警,否则监控数据只是事后诸葛。我常用的几条告警规则:
- 某张卡温度持续超过 85℃ 超过 10 分钟 → 告警散热问题
- 某张卡出现 ECC 错误或 Xid 错误 → 告警硬件故障
- GPU 利用率持续高于 95%,但任务不是压测 → 提示可能过度使用,需要排队或扩容
- 显存占用持续超过 90% 且发生 OOM 事件 → 告警任务调度不合理
Prometheus 的alertmanager可以对接钉钉、飞书、邮件,具体配置各家有差异,核心思路是"告警要能推动人去处理,而不是躺在日志里没人看"。
7. 一次真实的"GPU利用率低"排查过程:从监控数据倒推瓶颈
前面讲了不少工具,这里用一个非常典型的案例串起来:某天同事反馈,训练速度比之前慢了很多,但看 nvidia-smi,GPU-Util 有 70% 左右,显存占用也正常,CPU 和内存占用都不高。这种"哪里都不高但就是卡"的现象,在深度学习环境里太常见了。下面是我完整的排查过程。
7.1 第一轮:用 nvidia-smi dmon 抓细粒度数据
我先让同事把训练任务跑起来,然后我用:
nvidia-smi dmon -s pucvmet -d 0.5观察 2 分钟。输出显示 GPU-Util 在 60%~80% 之间反复跳动,每 2~3 秒出现一次跌到 10% 以下的谷底。同时观察显存使用,发现mem列没有明显变化,说明不是显存不足,而是计算过程周期性空转。
这种"锯齿形"利用率曲线,通常不是 GPU 本身的问题。GPU 一旦开始执行 kernel,利用率就会打满,除非 kernel 很小或中间有等待。
7.2 第二轮:确认数据加载是不是瓶颈
我用perf和top检查 CPU 上的数据加载线程:
top -H -p $(pgrep -f train.py | head -1)发现 CPU 总占用率才 40%,但有几个 Python worker 线程满负荷在跑,且si(软中断)数值偏高。进一步分析,训练脚本用了 DataLoader,但num_workers=0,所有数据预处理都在主进程里同步执行。GPU 算完当前 batch 后,CPU 还没准备好下一批数据,GPU 只能空转等待。
这个问题的本质是CPU 数据供给跟不上 GPU 消耗速度。把num_workers调到 8,并把pin_memory=True打开,重新跑一遍,GPU-Util 稳定在 95% 以上。
7.3 第三轮:检查 PCIe 传输是否有瓶颈
提升 DataLoader 之后还有轻微波动。继续观察,发现 GPU-Util 每隔 5 秒会掉一次。我用nvidia-smi -q -d PCIE查看 PCIe 速率:
nvidia-smi -q -d PCIE | grep -A 8 "PCIe"看到TX和RX的吞吐接近 1.5GB/s,而这个服务器的主板 PCIe 插槽只跑在 PCIe 3.0 x8 上,理论带宽约 8GB/s,实际有效带宽打对折。如果数据尺寸大且频繁传输,PCIe 很容易成为瓶颈。
解决思路有三种:
- 换到 PCIe 4.0 x16 插槽,或者换支持更多通道的 CPU 平台。
- 使用
torch.cuda的固定内存(pinned memory)减少拷贝次数。 - 尽量一次性把数据放 GPU 显存,减少小批次高频传输。
同事最终把数据预处理后的张量提前放到 GPU 显存缓存起来,解决了周期性掉速问题。
7.4 排查链路总结
这次排查的完整路径是:整体观察 → 细粒度采样 → 定位周期性波动 → 检查 CPU 数据供给 → 检查 PCIe 传输 → 针对性调优。每一步都依赖监控工具给出准确数据,而不是靠猜。我把常用排查思路整理成了一张表:
| 现象 | 优先怀疑方向 | 用哪个工具确认 |
|---|---|---|
| 利用率高但速度慢 | 算子效率低、线程占用低、SM 占用率不高 | nvidia-smi dmon、Nsight Compute |
| 利用率锯齿波动 | 数据加载/预处理瓶颈 | top -H、nvidia-smi dmon看时间相关性 |
| 温度高且频率低 | 散热/供电问题 | nvidia-smi -q -d CLOCK、gpu-burn压测 |
| 显存 OOM | 模型/输入太大、内存碎片 | nvidia-smi --query-gpu=memory.used |
| 多卡训练时某卡利用率低 | 负载不均、通信瓶颈 | DCGM 的 NVLink/PCIe 指标 |
8. 监控数据里的几个坑,踩过才敢说
最后这部分是我自己的血泪经验。工具给的数据不是绝对真理,解读不当会让你在错误方向上浪费大量时间。
8.1 GPU-Util 不一定是"你任务"的利用率
多进程共享 GPU 时,nvidia-smi显示的 GPU-Util 是整卡上所有进程的总利用率。你看到 90% 的占用,可能有一半是别人的任务在用。排查自己任务的真实占用率,需要用nvidia-smi --query-compute-apps=pid,used_gpu_memory,utilization按 PID 过滤,或者用 Nsight 做进程级 profile。
8.2 显存占用高不等于计算饱和
这个坑出现频率极高。一张卡显存占用 20GB / 24GB,但 GPU-Util 只有 5%,这很可能是在做推理服务,显存用来装载模型权重,但计算稀疏。也可能是代码里把大量缓存全塞进了显存,或者训练过程中等待外部响应。
遇到这种状态,我通常先看进程是训练还是推理,再去翻代码里有没有显式分配缓存或torch.cuda.empty_cache用的时机是否合理。盲目加显存容量往往解决不了问题。
8.3 频率和功耗才是"真实状态"
GPU 核心频率比利用率更能反映真实工作强度。同型号两张卡,一张在 1.8GHz 稳定运行,另一张在 1.2GHz 到 1.8GHz 之间反复跳变——即使利用率都显示 80%,后者的实际算力输出也明显偏低。查调频原因要看:
nvidia-smi -q -d CLOCK | grep -A 6 "Clocks Event Reasons"输出里的HW Slowdown、Thermal、Power Cap等字段会直接说明降频原因。
8.4 容器内监控注意设备映射和权限问题
在 Docker 容器里,如果没加--gpus all,或者加了但容器里的驱动版本和宿主机不兼容,经常出现nvidia-smi报错或输出为空。有一次我在容器里用gpustat死活看不到显存数据,最后发现是容器缺少nvidia-smi二进制,需要把宿主机的命令挂载进去,或者装完整 NVIDIA 驱动工具包。
还有一个坑是 TensorRT 推理时日志提示trt-warn: unable to determine GPU memory usage,这通常发生在用 TensorRT 读取显存信息失败时。原因可能是容器权限不足或驱动与 CUDA 版本不匹配。排查思路先确认宿主机的nvidia-smi正常,再检查容器内的 CUDA 和 TensorRT 版本是否与驱动兼容,别盯着日志本身纠结太久。
8.5 监控工具本身也会拖累性能
长时间高频采样会引入额外开销。nvidia-smi -l 0.1这种 10Hz 的采样会频繁唤醒 GPU 管理接口,在一些驱动版本下反而干扰任务的显存分配性能。做性能测试时,监控采样频率尽量低,或者只在关键时段开启,免得数据污染了你的实验结论。
根据我个人的使用经验,日常训练建议用nvtop做人工观察,用nvidia-smi dmon做后台日志,真正排查算子级性能问题再上 Nsight 系列;服务器数量多的时候,DCGM + Grafana 是值得投入的方向。没有什么工具是万能的,关键是搞清楚你当前最需要解决什么问题,再选对工具。希望这篇内容能让你在 GPU 性能监控这件事上少走点弯路。