GPU 到底在忙什么定义与来源 ↗

GPU 利用率 100%,
计算单元就都忙了吗?

同一段负载,DCGM 能报出三个“利用率”。差别全在分母。

SM × 时间:格子实心,就说明有 warp 驻留SM 0SM 1SM 2SM 30观察窗口 TT
实心:至少有一个 warp 驻留 空心:没有
100%
25%
3.1%
GPU_UTIL

有 kernel 在跑的时间 ÷ T
T ÷ T = 100%

SM_ACTIVE

所有 SM 的活动时间加起来 ÷ (SM 数 × T)
1T ÷ 4T = 25%

SM_OCCUPANCY

驻留 warp 数的时空平均 ÷ 每个 SM 的上限
1 ÷ 8 × 25% = 3.1%

一个 SM 一直忙,就够让 GPU_UTIL 冲到 100% 了。图里只画了四个 SM,每个最多驻留八个 warp,时钟也按固定值算。GPU 定义 ↗SM 定义 ↗
为什么图里的时间窗口要对齐?

图里把三个指标放进同一个窗口,只是为了方便比较。真实的 GPU_UTIL 来自驱动自己的采样窗口,profiling 字段各有一段采集区间,和 Prometheus 抓取的那一刻未必对得上。这里的公式也做了简化:时钟固定,每个 SM 一样忙。所以别拿线上的 GPU_UTIL 反推 SM_ACTIVE。另外,profiling 的比率原值通常是 0–1,图里乘了 100;GPU_UTIL 本身就是 0–100。

warp 驻留了,也可能只是在等数据

放大看一个持续活动的 SM
驻留暂不发射一个 block:64 threads每组 32 threads → 1 warpSM · warp 驻留槽位8 个槽位里驻留了 2 个 warpTensor 管线矩阵指令没有在跑FP32 管线FP32 指令没有在跑斜线:warp 已经驻留,但还在等内存返回。

SM_ACTIVE100%有 warp 驻留

SM_OCCUPANCY25%驻留 2 / 上限 8

运算管线暂时空闲但 warp 仍算进前两项

SM(流式多处理器)里装着 block,block 再拆成 warp,warp 往运算管线发指令。驻留和发射是两个阶段的事。CUDA 模型 ↗
Tensor、FP16、FP32 与“实际算力”是什么关系?

PIPE_TENSOR_ACTIVE、PIPE_FP16_ACTIVE、PIPE_FP32_ACTIVE 和 PIPE_FP64_ACTIVE 反映的是对应管线有多活跃,归一化用的是硬件定义的周期口径,所以 100% 在不同架构、不同管线上未必是同一件事。Tensor Core 能跑低精度矩阵运算,但 FP16 管线指标不含 HMMA 矩阵指令。这几个数不能直接相加,更不能拿它乘上标称 TFLOPS,当成实际吞吐。真要算有效 FLOP/s,还得知道运算次数、精度、指令类型和耗时。

真实 SM 能驻留多少 warp,要看 GPU 架构。实际 occupancy 还会被 block 大小、寄存器和共享内存卡住。图里的八个槽位只是拿来显示比例;occupancy 高,不代表跑得更快。

显存用了多少,和显存在忙什么,是两回事

装下了多少数据

DCGM_FI_DEV_FB_USED / FB_TOTAL

60GiB 已用总计 80 GiB
0 GiB80 GiB

有多少周期在收发数据

DCGM_FI_PROF_DRAM_ACTIVE

观察周期 →实心:传输
20% 活动

显存用了 75%,但显存接口只有 20% 的周期在传数据。

数据只要还留在显存里,哪怕没有读写,FB_USED 照样很高。DRAM_ACTIVE 数的是接口有多少周期在活动,别把它直接当成实测带宽。字段定义 ↗
MEM_COPY_UTIL、DRAM_ACTIVE、带宽利用率怎么区分?

MEM_COPY_UTIL 是驱动采样窗口里,显存被读或被写的时间占比。它既不是显存用量百分比,也不专指主机和 GPU 之间的复制。DRAM_ACTIVE 则是 profiling 给出的显存接口周期活动比例。两者来源和归一化方式都不一样,读数对不上很正常。

实测带宽就是传输字节数除以时间。带宽利用率,就是拿实测带宽除以同方向、同单位、同口径的参考带宽。别用 DRAM_ACTIVE 去乘显存标称带宽,那算不出真实带宽。另外,这里按 1 GiB = 1024 MiB 换算,exporter 输出的 FB_USED 单位是 MiB。算容量占比时先把单位统一,再想清楚分母用 FB_TOTAL,还是扣掉驱动保留空间后的可用容量。

TX 和 RX,得站在被监控的 GPU 这边看

经 PCIe 接收NVLink主机内存CPU 侧被监控的 GPUPCIe RX方向以这台 GPU 为准另一张 GPU当前无传输DCGM_FI_PROF_PCIE_RX_BYTES

2 GB ÷ 1 s = 2 GB/s = 2,000,000,000 B/s

0 GB/s图示刻度上限 8 GB/s
PROF_PCIE_*_BYTES 和 PROF_NVLINK_*_BYTES 是区间平均速率,单位 B/s。名字里带 BYTES,不代表它就是累积计数器。带宽定义 ↗
算链路利用率前,还要统一什么?

PCIe 的 profiling 速率把协议头和数据一起算进去。NVLink 的 profiling 指南描述的是不含协议头的速率,API 字段页的措辞又略有出入,用之前最好对着自己的部署版本和后端核一遍。图里没有把这两个数当成同口径的有效载荷吞吐来比。

TX 和 RX 各有各的方向上限;多链路加起来的总速率,要和同样链路数的总带宽比。两张 GPU 通信也不一定走 NVLink,也可能绕道 PCIe。较新的 *_BYTES_TOTAL 字段只有在 exporter 里按 counter 导出时才该用 rate(),这些原始累积字段本图不展开。

忙了 1 秒,为什么读数只有 10%?

假设 GPU 只在第 1 秒执行 kernel,随后 9 秒空闲。

每格 1 秒

1 秒窗口

忙 1 秒 + 空闲 0 秒

1 s

1 ÷ 1 = 100%

5 秒窗口

忙 1 秒 + 空闲 4 秒

5 s

1 ÷ 5 = 20%

10 秒窗口

忙 1 秒 + 空闲 9 秒

10 s

1 ÷ 10 = 10%

忙的始终只有 1 秒。窗口越长,算进去的空闲时间越多,平均值就越低。

10% 说的是这 10 秒里的平均值;第 1 秒仍然全程在执行 kernel。采集区间 ↗

这里的窗口长度用于演示均匀时间平均。DCGM 的采集窗口和 Prometheus 的抓取间隔是两回事:后者决定多久读取一次结果,不能还原窗口内部的瞬时变化。

100 * DCGM_FI_PROF_SM_ACTIVE

原值 0.25 就是 25%。这是个 gauge,别对它用 rate()。

功率是能量变化的快慢,能量是累积出来的面积

200 W面积 = 2,000 J0 s10 s

ΔE / Δt = 平均功率

2,000,000 mJ ÷ 10 s ÷ 1,000 = 200 W

TOTAL_ENERGY_CONSUMPTION 的单位是 mJ。Prometheus 的 rate() 会处理计数器重置;先逐条序列求 rate,再按需要汇总。

POWER_USAGE 是当下的功率读数;TOTAL_ENERGY_CONSUMPTION 是驱动上次重载以来攒下的总能量。Exporter 类型与单位 ↗rate() ↗
图里的空白、零值,还有 MIG 实例

DCGM 先采集、缓存字段,exporter 再输出,Prometheus 再按自己的间隔来抓。这三层的时间间隔并不一样。计数器复用可能留下采样空隙或零值,profiling 暂停时会返回 BLANK。字段不支持、没权限、没采到样本,再加上真的等于零,这四种情况得分开处理,别一律补零。异常大的特殊值同样不能当有效读数。

MIG(多实例 GPU)会把一张物理 GPU 切成多个实例。哪些字段支持、归一化范围多大,都要看具体的实体和设备。聚合之前先核对 UUID、GPU/实例标签和时间戳;别把整卡序列和实例序列加在一起,也别把整卡读数复制给每个共用这张 GPU 的 Pod,当成它们的独占用量。

dcgmi profile -l -i 0

在目标 GPU 上跑一下这条命令,看看哪些指标组能同时采集,再对一遍 exporter 的 counters 配置。文档里写着某个字段,不代表你手上的 GPU 和配置就一定会导出它。

几个读数摆在一起,再判断原因

持续运行的小 kernel

GPU100%SM25%Tensor5%

先看并行规模、batch 大小和 kernel 形状。GPU_UTIL 高,只能说明 kernel 执行的时间长。

显存传输活动较多

SM90%DRAM70%Tensor10%

接着测实际带宽、缓存命中率和算术强度。DRAM 活动高只是条线索,还不能就此认定带宽瓶颈。

时钟下降,吞吐也下降

SM 时钟业务吞吐时间 →

把功率、温度和 CLOCKS_EVENT_REASONS 一起看。温度高、功率贴着上限,都得有对应的限制原因才算数。

这三个场景都是假设出来的,只用来对比,读数不是诊断阈值。最后还得看任务耗时、tokens/s 或 samples/s 来验证。解释边界 ↗

指标速查

字段名沿用 dcgm-exporter 常见写法,新 API 别名见详情。以实际 /metrics 的 HELP、TYPE 和部署版本为准。

定义与来源