☰
Linux下GPU监控实战:从nvidia-smi到性能排查的完整指南
2026/10/22 15:02:31 网站建设 项目流程

跑模型的时候GPU利用率一直上不去,或者显存莫名其妙被占满,这种情况每个做深度学习或者科学计算的人应该都遇到过。想在Linux下把显卡状态摸清楚,最常用的路子其实就是那么几个命令,但很多人只会敲一个nvidia-smi,看到个表格就觉得完事了,其实远远不够。这篇文章我从最基础的查询命令讲起,到实时监控、工具选型,再到脚本化采集和问题排查,把我在实际环境中用过、踩过坑的经验都整理出来,希望对你有帮助。

1. 基础命令:nvidia-smi的完整使用思路

1.1 初识nvidia-smi:最核心的显卡状态查询

nvidia-smi是所有NVIDIA显卡监控的基石工具,只要装了NVIDIA官方驱动,这个命令就天然可用,不需要额外安装任何东西。

直接敲nvidia-smi,你会看到一块信息面板,里面包含了驱动版本、CUDA版本、GPU名称、显存总量、当前显存占用、GPU利用率、温度、功耗等核心信息。这个命令本质上是NVIDIA驱动对外暴露的系统管理接口,它的数据来源是驱动实时采集的硬件状态,所以它的准确度是可信的。

我见过不少人只看最上面几行的显存使用情况,却忽略了下面的进程列表。实际上nvidia-smi下方展示的进程信息,才是定位"谁占了我的卡"的关键。这个点后面我会展开讲。

用nvidia-smi有个误区:很多人以为必须要用sudo才能执行。其实普通用户只要对NVIDIA设备节点有读权限,这个命令就能正常使用,而绝大多数Linux发行版默认都开放了这些权限。如果你在执行命令时遇到"Unable to determine the device handle"这类报错,那通常不是权限问题,而是驱动与硬件之间出现了异常,比如驱动崩溃后没能自动恢复。

1.2 常用参数详解:从静态查询到定制化输出

nvidia-smi本身支持非常丰富的参数,只敲一个裸命令属于最低效的用法。我把日常用得最多的几个参数整理一下:

  • nvidia-smi -L:列出当前机器上所有的GPU设备列表,每张卡会显示编号、型号和UUID。这个参数适合在写脚本时用来确认当前环境有几张卡。
  • nvidia-smi --query-gpu=... --format=csv:这是最灵活的用法,你可以指定要查询的字段,输出成csv格式,方便程序解析。
  • nvidia-smi -l 2:每隔2秒刷新一次,这是最简单的实时监控方式。但注意,这个参数在较新版本的驱动里已经改名为--loop,功能是一样的。
  • nvidia-smi -i 0:指定查询编号为0的那张卡,适合多卡环境下单独查看某一张卡。
  • nvidia-smi -p:显示GPU的ECC错误信息和PCIe链路信息,这个参数对排查硬件稳定性问题很有用,但大部分做算法的人不知道。

举个例子,如果我想用脚本采集每张卡的利用率和显存占用,通常我会这样写:

nvidia-smi --query-gpu=index,utilization.gpu,memory.used,memory.total --format=csv,noheader,nounits

输出结果会像下面这样:

0, 85, 10240, 24576 1, 12, 4096, 24576

noheader表示不输出表头,nounits表示去掉百分号和MB这类单位后缀,方便后续用awk或者Python做数值处理。这种输出格式在写监控脚本时是刚需,比直接解析nvidia-smi的默认面板要容易得多。

2. 实时监控:从静态查询到动态刷新

2.1 watch命令:最简单的实时刷新方案

nvidia-smi本身虽然有循环刷新功能,但实际生产环境中我更推荐用watch命令配合nvidia-smi使用。

watch -n 2 nvidia-smi

这个命令的作用是每2秒执行一次nvidia-smi并全屏刷新显示结果。为什么要用watch而不是直接用nvidia-smi -l 2?因为watch有一个非常好用的特性:它可以高亮显示两次输出之间发生变化的内容。当显存占用从10G跳到11G时,watch会自动把变化的部分用白底高亮标出来,这对快速捕获资源波动非常有效。

watch的另一个好处是退出干净。按Ctrl+C即可退出,不会遗留下后台进程。相比之下,如果你用了nvidia-smi -l然后直接关闭终端,偶尔会出现进程未清理的情况(虽然概率很低)。

注意,watch默认刷新间隔是2秒,如果你的GPU利用率变化非常快,可以缩短到1秒,但再短就没有意义了,因为nvidia-smi本身的数据采集和渲染也需要几十毫秒,刷新频率过高反而造成无谓的系统开销。

2.2 gpustat:更美观的监控工具

nvidia-smi什么都好,就是输出面板太"朴素"了。如果你习惯用终端工作,而且想在监控GPU的同时兼顾美观和可读性,gpustat是绝佳的补充工具。

gpustat是一个基于Python的第三方工具,安装方式很简单:

pip install gpustat

装好之后直接运行:

gpustat -i 2

它会用彩色表格展示每张卡的利用率、显存占用、温度、功耗,还会列出每个占用GPU的进程以及对应的用户。最舒服的一点是,它会自动把显存占用按当前用户/进程做一个排序展示,一眼就能看清是谁在占用资源。

gpustat的底层数据来源同样是nvidia-smi,所以它的准确性完全不用担心。它只是把nvidia-smi的原生输出处理成了更适合人眼阅读的形式。在多人共用的服务器上,我一般会常驻一个gpustat窗口,比默认面板清晰太多。

有小伙伴问过gpustat是否会引入额外性能开销,毕竟它引入了Python层。实测下来,它的查询频率远低于nvidia-smi本身的硬件查询上限,CPU开销几乎可以忽略不计。

2.3 其他实用工具:nvtop 与 GPU 性能分析器

如果你需要的是更接近"任务管理器"那样的体验——带有柱状图、历史曲线,并且想同时监控CPU和GPU的整体系统负载,那么nvtop很合适。安装方式在Ubuntu/Debian系可以这样:

sudo apt install nvtop

nvtop启动后的界面很直观:上半部分是GPU的整体指标折线图,下半部分是每张卡的详细数据,包括SM利用率、显存带宽、PCIe吞吐量等。这些指标比nvidia-smi默认展示的信息深入得多,对分析"GPU利用率为什么低"这类性能问题帮助很大。

不过要给个建议:nvtop更适合做深度的性能剖析,日常快速查看还是用nvidia-smi或gpustat效率更高。因为nvtop的界面信息密度很高,新手容易迷失在大量数字里。先跑起来再说,等真需要深入定位瓶颈时,再切换到nvtop查看细节。

3. 读懂输出内容:看一眼数据就知道哪里出了问题

3.1 核心指标解读:利用率、显存、温度、功耗

很多人在第一眼看到nvidia-smi的面板时,最大的困惑是"这么多数字,到底哪些才是关键的?"我按重要程度排个序:

  • GPU利用率(utilization.gpu):表示GPU计算单元(SM)的忙碌程度。注意,这里的利用率计算的是"在采样周期内是否有计算任务在执行"的比例,而不是"计算单元被占用了多少容量"。所以你会看到利用率高达95%以上,但模型实际速度并没有提升,因为这里的95%可能只是表示"一直有任务在跑",并不代表计算资源被充分用满。
  • 显存占用(memory.used):深度学习场景下最直观的指标之一,直接决定你这张卡还能不能塞下下一个任务。
  • 温度(temperature.gpu):长期在85度以上运行的GPU,性能会明显下降,甚至触发降频保护。
  • 功耗(power.draw):结合显卡的Max Power参数,可以判断当前是否处于高负载状态。功耗接近上限说明GPU在满负荷运行,如果利用率很高但功耗很低,说明可能出现了降频或供电限制。

这几项数据之间是关联的,不能割裂来看。举个例子:一张卡写着utilization.gpu 100%、memory.used 2G、power.draw 60W,这说明计算单元虽然满载,但实际任务量并不大,可能是因为模型太小或者瓶颈在数据读取上。GPU利用率高不代表显卡在高效工作,这个认知非常重要。

3.2 判断GPU是否真正被有效利用

我见过不少团队拿着一张跑满100%利用率的卡,以为已经压榨出了全部性能,结果换一种数据读取方式之后,训练耗时直接下降了一半。这就是典型的"虚假繁忙"。

要判断GPU是否在高效工作,除了看利用率,还要结合这几项:

  • 计算规模是否匹配显存带宽。显存带宽是固定的硬件上限,当模型参数和激活值很大时,带宽会成为瓶颈,此时SM利用率反而不高。
  • 是否存在频繁的数据拷贝。如果训练代码中频繁在CPU与GPU之间传输数据,GPU的利用率会像过山车一样忽高忽低。
  • 是否有其他进程抢占资源。用nvidia-smi查看进程列表,看看除了当前任务之外,有没有别的进程也在占用显存或SM。

用nvtop可以查看更直观的SM/内存带宽占用曲线。如果SM利用率持续在90%以上,而且显存带宽也接近理论峰值,那这张卡几乎就是满负荷在工作,没有多少优化的空间了。

4. 进阶玩法:脚本化监控与自动化告警

4.1 用脚本采集GPU数据:写一个轻量监控器

监控GPU不只是为了"看",更重要的是把数据记录下来,用于后续分析。很多同学部署训练任务时,跑完一个模型就完事了,真正遇到"训练速度为什么变慢"这类问题时,手里完全没有历史数据可以查。这就非常被动了。

我习惯的做法是写一个简单的bash脚本,用一个循环定期向日志文件写GPU状态:

#!/bin/bash while true; do echo "$(date '+%Y-%m-%d %H:%M:%S')" >> gpu_monitor.log nvidia-smi --query-gpu=index,utilization.gpu,memory.used,temperature.gpu,power.draw \ --format=csv,noheader,nounits >> gpu_monitor.log sleep 5 done

用nohup在后台执行这个脚本,训练任务跑多久它就记录多久。事后想复盘时,直接看日志文件即可。如果配合Python的matplotlib,把数据和帧率画成曲线,训练过程中的资源变化一目了然。

import subprocess import time import csv LOG_FILE = "gpu_metrics.csv" INTERVAL = 5 # 采集间隔(秒) with open(LOG_FILE, mode="w", newline="") as f: writer = csv.writer(f) writer.writerow(["timestamp", "gpu_index", "utilization", "memory_used", "temperature", "power"]) while True: output = subprocess.check_output([ "nvidia-smi", "--query-gpu=index,utilization.gpu,memory.used,temperature.gpu,power.draw", "--format=csv,noheader,nounits" ]).decode("utf-8").strip().split("\n") timestamp = time.strftime("%Y-%m-%d %H:%M:%S") for line in output: parts = [x.strip() for x in line.split(", ")] writer.writerow([timestamp] + parts) f.flush() time.sleep(INTERVAL)

把数据落到CSV文件后,可以用pandas做数据切片,比如只看某一段训练时间内的平均利用率、温度峰值等,定位性能拐点。

4.2 多GPU均衡调度与占用检测

在多卡服务器上,除了查看单卡状态,更重要的是感知"哪张卡有空闲"。当多个人共用一台服务器时,经常出现有人把任务都堆在了0号卡上,而其他卡空着。用nvidia-smi配合一条简短的shell命令,可以快速挑出空闲卡:

for i in $(nvidia-smi --query-gpu=index --format=csv,noheader); do mem=$(nvidia-smi -i $i --query-gpu=memory.used --format=csv,noheader,nounits | tr -d ' ') echo "GPU $i used mem: ${mem} MiB" done

顺着这个思路,在Python里可以写成更优雅的自动选卡工具:

def get_free_gpus(max_memory_threshold_mb=1024): """返回显存占用低于阈值的GPU索引列表""" import subprocess output = subprocess.check_output([ "nvidia-smi", "--query-gpu=index,memory.used", "--format=csv,noheader,nounits" ]).decode("utf-8").strip().split("\n") free_gpus = [] for line in output: idx, mem_used = [x.strip() for x in line.split(", ")] if int(mem_used) < max_memory_threshold_mb: free_gpus.append(int(idx)) return free_gpus

这样在启动训练脚本时,可以自动选择最空闲的卡,而不是每次手工指定CUDA_VISIBLE_DEVICES。在多人共享的服务器上,这个小工具能省下非常多的沟通成本。

4.3 自动告警:当GPU异常时第一时间通知自己

长训练任务跑在后台,人不可能24小时盯着监控面板。我的习惯是设置一个简单的告警脚本——比如当GPU温度超过阈值,或者利用率长时间低于某个值(说明可能死锁或挂起了),就触发通知。

#!/bin/bash THRESHOLD_TEMP=85 while true; do temp=$(nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits | sort -n | tail -1) if [ "$temp" -gt "$THRESHOLD_TEMP" ]; then echo "GPU temperature exceeded ${THRESHOLD_TEMP}°C !" | mail -s "GPU Temp Alert" your_email@example.com fi sleep 60 done

如果没有配置邮件服务,也可以用curl结合第三方通知渠道发送告警到手机端。这个思路适用于所有需要无人值守的训练场景,虽然逻辑简单,但能避免因温度过高导致训练中断后无人知晓的尴尬情况。

5. 常见问题与排查经验:这些坑我都替你踩过

5.1 为什么nvidia-smi显示显存占用很高,但看不到进程?

这个问题出现的频率极高。用nvidia-smi看到显存被占了几十G,但下方进程列表却是空的,或者只显示几个<defunct>进程。这个现象通常是以下原因之一:

  • 进程已经崩溃,但由于显存回收机制尚未完全执行,资源还没有被完全释放。这种情况一般几十秒内会自动恢复。
  • 程序使用了CUDA的某种上下文缓存机制,即使主进程退出,子进程或僵尸进程仍持有显存和上下文。排查时可以执行ps aux | grep python看看是否有残留进程。
  • 使用了MIG(多实例GPU)模式,但当前用户没有权限查看其他实例的进程详情。

遇到这种情况,先检查所有用户的进程:

ps aux | grep -i python

找到残留进程后,如果能确认是废弃任务,直接kill -9终止即可。如果无法确认,也可以等一段时间再观察,因为某些框架在进程退出后需要一些时间异步释放显存。

注意:直接kill -9会有一定风险。如果那个进程正在写重要的模型checkpoint,强杀可能导致文件损坏。稳妥的做法是先发送SIGTERM(即kill不加参数),让程序有机会做清理工作,等几秒再考虑强杀。

5.2 为什么GPU利用率总是上不去?

GPU利用率低往往不是显卡本身的问题,而是数据供给跟不上。常见的根源有三个:

  • CPU预处理成为瓶颈。当数据加载、增强操作过于复杂,CPU处理速度跟不上GPU消费速度时,GPU只能空转等待。解决思路是增加num_workers、使用pin_memory,或者用TFRecord/LMDB这类高性能数据格式。
  • 频繁的cpu与cuda数据搬运。比如在训练循环里写了tensor.cpu()然后再次tensor.cuda(),这类操作非常昂贵,会中断GPU的计算流水线。
  • 模型结构本身并行度低。某些模型存在大量串行计算逻辑,即使GPU算力充足,也无法充分利用多核SM。

定位这类问题,一个简单的方法是逐步减小数据加载的负载,比如把数据集换成纯随机数,如果GPU利用率立刻大幅提升,那瓶颈基本就锁定在数据加载环节。

5.3 watch模式下刷新卡顿怎么办?

在多人共用、负载很高的服务器上,watch -n 1 nvidia-smi可能会出现刷新不及时或者画面撕裂感。这是因为watch需要在每次刷新时重新执行命令并渲染整个终端界面,而在高负载环境下nvidia-smi的响应也会变慢。

这种情况下建议把刷新间隔放宽到2秒以上,或者改用gpustat -i 5这类轻量级轮询。如果真的需要秒级甚至毫秒级的精确监控,建议使用NVIDIA官方提供的NVML绑定库(Python版为pynvml),它可以直接调用驱动层的接口,几乎不受终端渲染影响。

import pynvml pynvml.nvmlInit() handle = pynvml.nvmlDeviceGetHandleByIndex(0) util = pynvml.nvmlDeviceGetUtilizationRates(handle) print(f"GPU util: {util.gpu}%, Memory util: {util.memory}%")

pynvml就相当于Python版的nvidia-smi底层接口,相比于每次调用子进程执行nvidia-smi,它的开销低很多,适合用来做高频监控或集成进入训练框架。

5.4 结尾小技巧:让nvidia-smi真正成为你的专属工具

最后分享一个我的个人使用习惯:在服务器的~/.bashrc里加一个别名,把常用的监控参数固化下来,这样每次登录服务器只需要敲一个简短别名就能获得定制化的GPU信息。

alias gpu='nvidia-smi --query-gpu=index,name,utilization.gpu,memory.used,memory.total,temperature.gpu,power.draw --format=csv' alias gpuwatch='watch -n 2 nvidia-smi' alias gpustat='gpustat -i 2'

做算法也好,做系统运维也好,GPU监控这事本身不难,难的是把它用成一种习惯。不要等模型跑挂了才想到去看卡的状态,而是从任务启动的第一秒开始,就让监控数据滚动起来。一套顺手的小命令,能帮你省下大量排查问题的时间。希望这些经验能对你的实际工作有帮助。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询