2. 性能分析工具

做训练调优,最怕什么?

怕你改了一堆代码,跑起来一看,GPU利用率才30%。

更怕的是,你根本不知道瓶颈在哪。

我刚开始做分布式训练那会儿,就吃过这个亏。模型跑得慢,我以为是数据加载的问题,折腾了两天数据管道,结果发现是GPU在等CPU喂数据。嗯,工具没用对,白费功夫。

所以这一章,咱们就把几个最常用的性能分析工具捋一遍。说白了,就是给你的训练过程装个「监控探头」。

1. nvidia-smi:最基础的GPU监控

这个命令,做AI的应该没人不知道。但我发现很多人只会看个显存占用,其实它还能挖出不少信息。

核心用法:

# 实时监控,每秒刷新一次
nvidia-smi -l 1

# 只看关键指标,简洁模式
nvidia-smi --query-gpu=index,temperature.gpu,utilization.gpu,memory.used --format=csv -l 1

# 查看所有GPU的进程信息
nvidia-smi pmon -c 1

我个人习惯,训练一开始就开个终端挂着 nvidia-smi -l 1。为什么?因为你能第一时间看到GPU利用率有没有掉下去。

关键指标解读:

指标 正常范围 异常信号
GPU-Util 90%-100% 低于80%说明有瓶颈
Memory-Usage 根据模型大小 接近100%可能OOM
Volatile GPU-Util 波动正常 频繁大幅波动说明等待

注意:GPU-Util显示100%不代表计算单元全在干活。它可能只是在等显存读写。我曾经被这个指标骗过,以为利用率很高,结果用Nsight一看,计算单元空闲率40%。

2. Nsight Systems:全局视角的性能分析

如果说nvidia-smi是「体温计」,那Nsight Systems就是「CT扫描仪」。

它能告诉你:CPU在干什么?GPU在干什么?数据在哪儿堵住了?

基本使用:

# 命令行采集,持续10秒
nsys profile -o output_file -t cuda,nvtx,osrt python train.py

# 只采集GPU kernel信息
nsys profile -t cuda -o kernel_only python train.py

# 采集时限制范围
nsys profile -o trace_output -t cuda,nvtx \
  --capture-range=cudaProfilerApi \
  python train.py

采集完会生成一个 .nsys-rep 文件。用Nsight Systems GUI打开,你会看到一条时间轴。

我一般重点看三个东西:

  • GPU Streams:看kernel有没有重叠执行。理想情况是多个stream同时跑。
  • CPU Threads:看数据加载线程是不是在空转。如果CPU线程大部分时间在等I/O,那就是数据管道的锅。
  • NVTX标记:我习惯在代码里加NVTX range,这样一眼就能看出每个阶段花了多少时间。

小技巧:在PyTorch代码里加NVTX标记很简单:

import torch.cuda.nvtx as nvtx

nvtx.range_push("forward")
output = model(input)
nvtx.range_pop()

nvtx.range_push("backward")
loss.backward()
nvtx.range_pop()

这样在Nsight里就能清晰看到前向和反向的时间占比。

3. PyTorch Profiler:代码级别的精细分析

Nsight看全局,PyTorch Profiler看细节。它能告诉你每个算子花了多少时间,显存分配情况,甚至kernel的启动延迟。

标准用法:

import torch.profiler as profiler

with profiler.profile(
    activities=[
        profiler.ProfilerActivity.CPU,
        profiler.ProfilerActivity.CUDA,
    ],
    schedule=profiler.schedule(wait=1, warmup=1, active=3, repeat=1),
    on_trace_ready=profiler.tensorboard_trace_handler('./log'),
    record_shapes=True,
    profile_memory=True,
    with_stack=True
) as prof:
    for step in range(10):
        train_step()
        prof.step()

关键参数说明:

参数 作用 我的建议
wait 跳过前N步 设为1,跳过预热
warmup 预热步数 设为1-2,让CUDA kernel预热
active 实际采集步数 3-5步足够,太多数据反而难分析
record_shapes 记录张量形状 建议开启,方便定位形状不匹配

避坑指南:我曾经在profile的时候把active设成了100,结果生成了几个G的trace文件,打开TensorBoard直接卡死。记住,profile的目的是找瓶颈,不是做全量统计。3-5步足够。

4. TensorBoard:可视化分析

PyTorch Profiler采集的数据,最终要用TensorBoard来看。

启动命令:

tensorboard --logdir=./log --port=6006

打开浏览器,你会看到几个关键视图:

  • Overview:整体时间线,CPU和GPU的利用率对比
  • Operator:每个算子的耗时排名。我一般先看Top 10,如果某个算子占比异常高,那就是优化目标
  • Memory:显存分配和释放的时间线。能看到有没有显存碎片
  • Trace:和Nsight类似的时间轴,但更细粒度

我常用的分析套路:

  1. 先看Overview,确认GPU利用率是否达标
  2. 如果利用率低,切到Trace,看CPU和GPU之间有没有空隙
  3. 如果利用率高但速度慢,切到Operator,找最耗时的算子
  4. 针对耗时算子,看能不能用融合kernel替代,或者调整batch size

个人经验:有一次我发现一个叫aten::copy_的算子占了15%的时间。查了半天,发现是数据从CPU拷贝到GPU时,用了.cpu()和.cuda()来回倒腾。改成pin_memory=True后,直接省了10%的时间。

知识体系总览

这四个工具,各有各的用处。我画了张图,帮你理清它们的关系:

性能分析工具知识体系 nvidia-smi 实时GPU状态监控 Nsight Systems 全局时间线分析 PyTorch Profiler 算子级精细分析 TensorBoard 可视化分析面板 第一层:快速排查 第二层:系统级分析 第三层:代码级分析 第四层:可视化呈现 推荐分析流程 ① nvidia-smi 看GPU利用率 ② Nsight Systems 定位瓶颈阶段 ③ PyTorch Profiler 定位具体算子 ④ TensorBoard 可视化验证

说白了,这四个工具是一个递进的关系。先用nvidia-smi快速判断有没有问题,再用Nsight Systems定位到具体阶段,然后用PyTorch Profiler找到具体算子,最后用TensorBoard把结果可视化出来。

嗯,工具就这些。但工具是死的,人是活的。关键是要养成「先分析,再优化」的习惯。我见过太多人一上来就改代码,改完发现没效果,又改回来。浪费时间。

记住:没有数据支撑的优化,都是瞎猜。

一句话总结:

  • nvidia-smi:看有没有病
  • Nsight Systems:看哪儿病了
  • PyTorch Profiler:看具体哪儿疼
  • TensorBoard:把诊断结果画出来