当年带团队排查训练变慢,我第一句话永远是:“先看一眼 nvidia-smi。”不管情况听起来多复杂,很大概率能从这条命令的输出里找到线索。监控 NVIDIA GPU 的运行状态和使用情况,听起来像运维的基础活,但真正把它做成一套能提前发现问题的机制,远不是敲一条命令、截个图那么简单。
这篇文章是我自己长期折腾 GPU 服务器后的经验总结,适合三类人:正在管理多卡服务器的开发者,需要判断任务有没有真正跑在 GPU 上的算法工程师,以及想把手头几块卡的监控变得规范的运维同学。你会从 nvidia-smi 的字段解读开始,逐步走到持续记录、多卡统一采集、告警闭环,中间还会穿插一些“监控数据本身会误导人”的真实案例。
1. 第一块垫脚石:把 nvidia-smi 的输出逐行读懂
1.1 一条命令能带出多少信息
nvidia-smi 是 NVIDIA 驱动自带的命令行工具,驱动装好就能用,不需要额外安装。默认执行后会看到所有 GPU 的列表,里面包含 GPU 编号、名称、总线位置、风扇转速、温度、当前功耗、功耗上限、显存使用量、SM 侧的计算利用率,以及正在运行的进程列表。
对单机排查来说,这条命令的信息量已经相当大。几个常用的参数:
# 每 2 秒刷新一次,适合观察负载波动 nvidia-smi -l 2 # 只看指定编号的卡,例如只看 0 号和 1 号卡 nvidia-smi -i 0,1 # 查看结构化完整信息 nvidia-smi -q # 只查某一类信息 nvidia-smi -q -d TEMPERATURE nvidia-smi -q -d POWER nvidia-smi -q -d UTILIZATION有一点需要留意:-l或--loop参数在部分新版驱动里被标记为不推荐,更稳妥的做法是用watch -n 2 nvidia-smi来做定时刷新。肉眼观察负载波动时,watch 的形式也更顺手,因为终端会持续更新,不需要反复手动敲命令。
1.2 字段语义:GPU-Util 到底代表什么
很多刚开始用 GPU 的人会把 GPU-Util 直接理解成“显卡整体利用率”,看到 60% 就觉得卡没吃满。这是很典型的误判。GPU-Util 的准确含义是 SM(流式多处理器)上活动时钟周期的比例,它主要反映计算核心的繁忙程度,跟显存占用、PCIe 吞吐、视频编解码单元都没有直接关系。
字段含义值得认真过一遍:
| 字段 | 实际含义 | 常见误解 |
|---|---|---|
| GPU-Util | SM 激活时钟周期占比 | 以为等于整卡综合利用率 |
| Memory-Usage | 显存分配量,不是带宽使用量 | 显存占满不代表计算满 |
| Temp | GPU 核心温度 | 温度高不马上坏,但会触发降频 |
| Pwr:Usage/Cap | 当前实际功耗 / 允许上限 | Cap 是硬性约束,长期贴着会被限频 |
| Perf | 当前性能状态 P0-P12 | P0 最高,但只表示状态档位 |
还有一个关键点:显存占用率不等于显存带宽使用率。一个推理服务可能把几十 GB 显存全部占掉了,但它实际每秒读写的字节数很低;反过来,一个数据吞吐很大的训练任务只占用少量显存,却在疯狂搬运数据。所以排查性能问题时,这两个指标要分开理解,混在一起很容易得出错误结论。
1.3 瞬时快照的盲区
nvidia-smi 默认只是一次快照。如果你的任务每 5 秒出现一个计算波峰,随机看一眼可能正好落在波谷,于是得出“GPU 完全没在工作”的结论;也可能正好撞上波峰,看到一个虚假的 100% 利用率。
我之前帮某个团队定位过训练变慢的问题。他们全天候盯着终端,发现 nvidia-smi 里 GPU-Util 只有 5% 左右,于是怀疑代码根本没有真正用到 GPU。我让他们用循环命令记录一段时间后,发现利用率其实每 10 秒都会冲到 98% 再回落,GPU 一直在正常执行,只是每个训练 step 之前有一段非常长的数据预处理阶段,计算密集部分被拉长了。如果只依赖一次快照,排查方向就会完全跑偏。这就是监控要走“持续记录”而不是“反复看一次”的根本原因。
2. 让日志替人值守:从手动查询到持续采集
2.1 watch 能解决刷新频率,但解决不了历史
在终端里用watch -n 2 nvidia-smi,是很高效的临时观察手段。它能让你坐在屏幕前盯住负载波动,不用重复敲命令。显卡很多的时候,可以配合-i参数只看某几张关键的卡,避免一屏输出塞满。
但 watch 有一个天然短板:它只刷新当前画面,不保存任何历史数据。过几天你想回看“前天下午那张卡的温度到底多高”,watch 给不了任何线索。要让监控具备复盘能力,就必须把数据落盘。这一步做起来不难,难的是很多人根本没往这个方向想。
2.2 让 nvidia-smi 输出机器可读的数据
nvidia-smi 提供了一个对自动化和日志非常友好的查询模式,可以直接输出 CSV 格式:
nvidia-smi --query-gpu=timestamp,name,utilization.gpu,memory.used,temperature.gpu,power.draw --format=csv输出类似:
2025/01/10 12:00:00,某GPU型号,45 %,6.5 GB,62,280 W想去掉表头和单位,便于后面用脚本直接处理,可以加noheader和nounits:
nvidia-smi --query-gpu=timestamp,utilization.gpu,memory.used,temperature.gpu,power.draw --format=csv,noheader,nounits -l 2-l 2表示每 2 秒输出一条。采样间隔我一般不用低于 1 秒,因为温度、功耗这类信号变化本身不算快,过高的采样频率只会让日志文件迅速膨胀,对排查帮助有限。
2.3 一个最小但可靠的采集脚本
下面的脚本足够应付单机监控,也是很多更复杂系统的雏形:
#!/bin/bash OUTPUT_DIR=/var/log/gpu_monitor mkdir -p "$OUTPUT_DIR" LOG_FILE="$OUTPUT_DIR/gpu_$(date +%Y%m%d).csv" if [ ! -f "$LOG_FILE" ]; then echo "timestamp,gpu_name,utilization_gpu,memory_used_mb,temperature_c,power_w" > "$LOG_FILE" fi while true; do nvidia-smi --query-gpu=timestamp,name,utilization.gpu,memory.used,temperature.gpu,power.draw \ --format=csv,noheader,nounits >> "$LOG_FILE" sleep 5 done这个脚本的作用很朴素:首次启动时写入表头,之后每 5 秒追加一行数据。nounits让纯数字输出不带百分号、W、MB 等后缀,方便后续用脚本、电子表格或分析工具直接读。临时跑起来可以用nohup ./gpu_monitor.sh &,正式环境建议做成 systemd 服务,保证断电重启后自动拉起。
日志文件会持续增长,建议每天生成一个文件,再用 logrotate 或定时任务保留最近半个月的数据。做监控的目的是在故障发生后翻出时间线,而不是无限囤积历史数据。
3. 温度、功耗、时钟频率:性能表象下的因果链
3.1 温度不是故障,降频才是
GPU 温度高本身不直接导致故障,真正让性能下降的是温度墙触发的降频机制。绝大多数显卡在核心温度达到某个阈值后(常见是 83 到 85 度附近),驱动会自动下调核心频率,压低功耗,让温度不再继续上升,代价是计算速度变慢。
你看监控日志时会发现一种典型曲线:温度卡在 86 度上下,利用率显示 100%,但任务实际吞吐远低于预期。散热不好的机器上,频率可能会在升上去和降下来之间反复横跳,利用率曲线像锯齿一样,整体性能损失能到 10% 到 30%。
所以监控温度时,我关心的不是“多少度会烧坏”,而是“多少度开始降频”。想快速判断当前是否处于降频状态,可以查时钟:
nvidia-smi -q -d CLOCK如果clocks.current.graphics明显低于clocks.max.graphics,而负载又很满,基本可以断定被温度墙或功耗墙压住了。
3.2 功耗墙:利用率高不代表真满载
除了温度墙,另一个容易被忽略的限制是功耗墙。每张卡都有功耗上限,不同型号差异很大,甚至同一型号在不同驱动版本、不同功耗模式下的上限都可能不同。如果任务同时吃大量显存带宽和算力,显卡会优先逼近功耗上限,此时即使计算单元还有空余,频率也不会继续往上拉。
从监控数据判断其实很直接:power.draw如果长期贴着power.limit,同时 SM 利用率也高,那说明显卡处于“又热又饱”的状态。如果power.draw很低但 SM 利用率很高,说明实际负载本身没有吃到功耗上限,扩展方向不在显卡本身。
多机多卡的场景还要注意整机供电调度。某些情况下相邻显卡会共享供电冗余,一张卡突然满载可能导致另一张卡短暂降频。这类问题不靠长期日志很难发现,因为这属于“看起来一切正常,但性能比平时差”的隐性损耗。
3.3 显存高占用与低计算利用率:瓶颈多半不在卡内
我见过大量这样的案例:memory.used接近显存总量,但utilization.gpu非常低,甚至只有个位数。很多人第一反应是 GPU 坏了或者驱动有问题。实际上,这往往意味着 GPU 正在“等数据”。
训练或推理任务的流程大致是:CPU 侧读取数据、预处理、拷贝到显存,然后 GPU 才能开始计算。如果 CPU 侧数据加载太慢、预处理线程数不够、磁盘 IO 卡住,GPU 就会空转等待。显存此时仍然被模型和中间结果占据,所以显得很满,但计算核心是闲的。
这种情况下,GPU 监控指标并没有骗人,它只告诉你瓶颈不在 GPU。要定位真因,还得把 CPU 利用率、磁盘读写、网络吞吐放到同一个时间线里看。很常见的触发场景是训练脚本里num_workers设置不合理,或者数据集样本读取逻辑退化成了单线程串行。把 GPU 指标和主机指标放在同一块看板上,能大幅减少这类误判。
4. 多卡与集群下的监控升级:DCGM 和统一指标
4.1 单点命令撑不住多卡环境
手头有两三张卡的时候,逐卡执行 nvidia-smi 完全够用。可一旦变成几十张卡甚至跨节点,这个方案的痛点就非常明显:
- 需要逐节点登录,信息零散;
- 没有历史聚合能力,无法画趋势;
- 每次执行都是独立进程,采样之间缺乏统一精度;
- 进程级信息在多用户共享环境下很难做细粒度管理。
这时候需要引入一个能统一暴露 GPU 指标、持续后台采样的组件。DCGM(数据中心 GPU 管理器)就是这条路线上比较成熟的方案。它不是来替代 nvidia-smi 的,nvidia-smi 依然适合单卡快速诊断,而 DCGM 更适合规模化、体系化的监控采集。
4.2 DCGM 的基本使用
DCGM 装好后,最常用的是dcgmi这套命令。先开启采样统计,再按需查询:
# 启用默认采样统计 dcgmi stats --enable # 查看当前支持的统计字段 dcgmi stats --list # 查看指定 GPU 的近期统计 dcgmi stats -v # 实时监控一组指标 dcgmi dmon -e 203,204,205,251,301,302这里的字段编号对应温度、功耗、SM 利用率、显存利用率、PCIe 读写速率等。具体编号在不同版本里可能略有差异,实际使用前建议用dcgmi dmon -e的补全列表确认。
DCGM 和 nvidia-smi 最大的区别是:它由独立后台进程持续采样,你查询到的是历史窗口内的平均值、最大值和最小值,而不是某一次执行的瞬时快照。这天然解决了“看监控时任务刚好处于波谷”的问题。
4.3 指标选型:不要什么数据都接
把 DCGM 接入可视化体系时,最常见的错误是“什么都采集”,最后看板上几十条折线,故障发生时根本不知道看哪条。我的建议是初期只盯这几类指标:
| 指标类别 | 参考字段 | 判断重点 |
|---|---|---|
| 温度 | temperature.gpu | 连续超过 85 度需要告警 |
| 功耗 | power.draw, power.limit | 长期贴上限表示被功耗限制 |
| 计算负载 | utilization.gpu | 结合任务阶段判断忙闲 |
| 显存 | memory.used | 接近总量时警惕分配失败 |
| 传输 | pcie.tx, pcie.rx | 满负荷时看是否被数据侧拖累 |
在数据导出层面,DCGM 生态里有现成的 exporter 可以把指标转成带标签的时间序列,配合常见的开源监控生态和可视化面板使用。不少团队会直接把它接进已有的监控系统里。对只有少量 GPU 的场景,前面的 CSV 采集脚本已经够用;上了规模再切 DCGM,收益才会明显。
5. 监控数据骗人的时刻:三个常见误判案例
5.1 进程残留制造的显存“泄漏”
有次排查发现,某张卡的显存占用一直居高不下,但代码明明已经退出了。用 nvidia-smi 看进程,发现残留着一个早已不工作的计算进程。原因是任务通过调度平台启动了一个子进程,调度器异常退出后,子进程变成僵尸,显存锁一直没释放。指标曲线看起来像典型的“应用显存泄漏”,真正原因其实是进程没清理干净。
排查命令很简单:
nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv fuser -v /dev/nvidia0 ps aux | grep python | grep -v grep确认是残留进程后,用kill -9清理。这里要特别提醒:动手之前一定确认 PID 对应的启动命令,别把还在正常服务别人任务的应用误杀。尤其多用户共享节点上,杀错进程比显存泄漏本身更麻烦。
5.2 PCIe 瓶颈:GPU 在等数据而不是在算
有些任务日志里 SM 利用率很高,但整体耗时依然不符合预期。再深入看数据,高利用率其实是大量轻量级内核频繁启动造成的假象,真正开销花在了 CPU 到显存的数据搬运上。默认 nvidia-smi 看不到 PCIe 吞吐,但通过 DCGM 的 PCIe 读写指标能清晰地看到传输量。
我之前处理过一个小型推理服务的案例:模型很小,每次请求却要从 CPU 侧读入一张大图,PCIe 带宽长期被占满,但 GPU 计算利用率只有 30%。团队一开始以为是算力不够,持续加卡,问题反而更严重。直到我把 PCIe 吞吐量画到看板上,才定位到瓶颈在图片解码和主机到设备拷贝。解决方案是在 CPU 侧提前完成解码和缩放,让 GPU 只负责真正的计算部分。
5.3 ECC 错误:硬件风险的照妖镜
只看负载的监控方案很容易漏掉 ECC 错误。ECC 是显存数据的纠错机制,当显存出现物理不稳定时,错误计数会持续增长。对长期运行的服务和训练任务来说,这是很关键的硬件风险信号。
查看命令:
nvidia-smi -q -d ECC输出里通常有volatile和aggregate两组计数。前者表示本次驱动加载以来的错误数,后者是累计值。我一般只看 volatile 是否在短期内快速上升。如果某个时间段错误数爆发式增长,优先考虑备份数据和排查负载,再决定是否需要联系硬件售后。这个指标平时可能几个月都是零,但一旦出现值,往往就是问题来临前的预兆。
6. 让监控闭环:告警阈值与自动处理的尺度
6.1 先定阈值,再定告警
监控的价值体现在异常发生时能第一时间暴露问题,而不是让人整天盯屏幕。阈值设置要根据具体硬件规格来定,不能从网上随手抄一个数字。我习惯分两档处理:
- 警告档:温度持续超过 85 度、显存剩余不足 5%、功耗持续贴近上限。这类情况只记录并推送通知,不一定需要立刻处理。
- 严重档:温度超过 95 度、出现新的非易失 ECC 错误、GPU 掉卡或驱动报错。这些需要立刻把负责人拉进处理流程。
这里强调“持续”二字。瞬时超过阈值非常常见,比如任务启动时显存快速分配,可能出现一瞬间的高占用。如果每次波动都告警,很快所有人都会对告警麻木。给告警加上连续 N 次采样才触发的条件,基本能过滤掉绝大多数毛刺。
6.2 自动处理:能不做尽量不做
很多人在监控搭好后,第一反应是“温度高了自动降频、显存不够自动杀进程”。自动杀进程是我最不建议做的操作。多用户共享 GPU 的环境下,自动回收策略很容易误伤在线服务,也容易引发协作层面的问题。
更稳妥的闭环顺序是:采集 → 告警 → 人工介入。只有对明确可判定的异常,比如确认是残留进程、驱动掉卡后需要拉起服务,才考虑自动化。即便是自动拉起,也必须设置冷却时间和重试上限,避免服务陷入“崩溃-拉起-再崩溃”的循环。
我自己的体会是,GPU 监控跑通后,最先带来明显收益的往往是“掉卡”和“温度降频”两件事。它们不是每天都发生,但每次发生都能省下好几个小时的排查时间。从 nvidia-smi 的单机瞬时常量,到 CSV 持续采集,再到 DCGM 的多卡统一监控,按规模选型就好。先把最小可用的方案跑起来,再逐步优化,远比一开始就搭建大而全的系统更实在。