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类似的时间轴,但更细粒度
我常用的分析套路:
- 先看Overview,确认GPU利用率是否达标
- 如果利用率低,切到Trace,看CPU和GPU之间有没有空隙
- 如果利用率高但速度慢,切到Operator,找最耗时的算子
- 针对耗时算子,看能不能用融合kernel替代,或者调整batch size
个人经验:有一次我发现一个叫aten::copy_的算子占了15%的时间。查了半天,发现是数据从CPU拷贝到GPU时,用了.cpu()和.cuda()来回倒腾。改成pin_memory=True后,直接省了10%的时间。
知识体系总览
这四个工具,各有各的用处。我画了张图,帮你理清它们的关系:
说白了,这四个工具是一个递进的关系。先用nvidia-smi快速判断有没有问题,再用Nsight Systems定位到具体阶段,然后用PyTorch Profiler找到具体算子,最后用TensorBoard把结果可视化出来。
嗯,工具就这些。但工具是死的,人是活的。关键是要养成「先分析,再优化」的习惯。我见过太多人一上来就改代码,改完发现没效果,又改回来。浪费时间。
记住:没有数据支撑的优化,都是瞎猜。
一句话总结:
- nvidia-smi:看有没有病
- Nsight Systems:看哪儿病了
- PyTorch Profiler:看具体哪儿疼
- TensorBoard:把诊断结果画出来