CUDA编程与算子优化
第8章:性能分析工具链
掌握 Nsight Systems、Nsight Compute 和 PyTorch Profiler 三大性能分析工具,建立系统化的性能诊断能力
Nsight Systems Nsight Compute PyTorch Profiler 性能分析
优化的前提是精准诊断——不会用 Profiler 的优化都是盲猜。本章掌握 AI Infra 领域的三大核心性能分析工具:Nsight Systems(全链路时间线)、Nsight Compute(Kernel 级下钻)、PyTorch Profiler(框架级 op 视图),并给出一套通用的”找到瓶颈→对症下药”工作流。
📑 目录
- 1. 性能分析的两层视角
- 2. Nsight Systems:全链路时间线
- 3. Nsight Compute:Kernel 级下钻
- 4. SOL(Speed of Light)面板解读
- 5. PyTorch Profiler:框架级视图
- 6. 标准排查流程
- 自我检验清单
- 参考资料
1. 性能分析的两层视角
| 工具 | 视角 | 回答的问题 |
|---|---|---|
| Nsight Systems | 系统级时间线 | ”GPU 在等谁?哪个阶段最慢?” |
| Nsight Compute | Kernel 级 | ”这个 kernel 是 memory bound 还是 compute bound?” |
| PyTorch Profiler | 框架 op 级 | ”我的模型里哪个 op 最耗时?” |
典型流程:先用 Nsys 看宏观时间线,定位耗时最长/最异常的 kernel → 用 Ncu 下钻这个 kernel,找具体的 limiter。
2. Nsight Systems:全链路时间线
2.1 抓 trace
# 命令行(通用)
nsys profile -o my_trace --stats=true \
--trace=cuda,nvtx,osrt,cudnn,cublas \
python train.py
# PyTorch 训练:用 nvtx 标注关键阶段
import torch.cuda.nvtx as nvtx
with nvtx.range("forward"):
out = model(x)
with nvtx.range("backward"):
loss.backward()
with nvtx.range("optimizer"):
optimizer.step()
2.2 打开 trace
nsys-ui my_trace.nsys-rep # 图形界面
会看到一条时间线,行包括:
- CPU 线程:Python 调用、CUDA API
- CUDA Stream:Kernel 执行时间块
- NVTX:你标注的范围
- NCCL:通信 op
- GPU Metrics:SM 利用率、HBM 带宽
2.3 找 GPU Idle Gap
GPU Stream: ████____████____████____████
↑ ↑ ↑
这些 gap 就是 GPU 空闲时间
GPU idle 的根因有几种:
| Gap 来源 | 症状 | 解决 |
|---|---|---|
| CPU 数据预处理慢 | gap 期间 CPU 线程在跑 PIL / decode / tokenize | num_workers > 0 多进程 dataloader |
| Host-Device 同步 | gap 期间是 cudaMemcpy 或 .item() | 减少同步、用 pinned memory |
| Kernel Launch Overhead | gap 极小但密集 | CUDA Graph / 算子融合 |
| 多卡通信等待 | NCCL Stream 占满 | 计算通信 overlap、梯度桶大小 |
| 调度器空转 | gap 大且无明显活动 | 检查 Python GIL、scheduler 实现 |
2.4 实战命令
# 只 trace 第 100-103 步,避免 trace 文件过大
nsys profile --capture-range=nvtx --capture-range-end=stop \
-c train_step --range-id=100-103 ...
# 自动统计,不用打开 GUI
nsys stats --report cuda_kernel_sum my_trace.nsys-rep
3. Nsight Compute:Kernel 级下钻
3.1 抓单个 kernel
# 抓所有 kernel,详细数据(很慢,只用于调试)
ncu -f -o report --set full python my_kernel.py
# 只抓特定 kernel,快得多
ncu -f -o report --kernel-name "my_softmax_kernel" \
--launch-skip 100 --launch-count 5 \
python my_kernel.py
# 命令行直接看报告
ncu --set basic ./my_app
3.2 打开报告
ncu-ui report.ncu-rep
3.3 关键面板
| 面板 | 用途 |
|---|---|
| GPU Speed of Light | 算力/带宽利用率,第一眼必看 |
| Memory Workload Analysis | HBM/L2/Shared/Register 各层带宽占用 |
| Compute Workload | SM 各类指令利用率(FP32/FP16/Tensor) |
| Scheduler Statistics | Warp 状态(active/stalled/eligible) |
| Warp State | 看 Warp 在等什么(memory/sync/…) |
| Source / SASS | 行级耗时(需要 -lineinfo 编译) |
| Occupancy | 实际 vs 理论占用率 |
3.4 对比模式
ncu-ui --baseline old.ncu-rep new.ncu-rep
直接看新版相对旧版的提升/退化,优化前后对比一目了然。
4. SOL(Speed of Light)面板解读
SOL = “如果硬件资源完全打满,kernel 能跑多快”——你和这个上限的差距,就是优化空间。
4.1 三个核心数字
| 指标 | 含义 |
|---|---|
| SM% | 算力利用率(几乎所有计算单元都在用) |
| Memory% | HBM 带宽利用率 |
| Tensor% | Tensor Core 利用率(独立计算) |
4.2 判断 Bound
SM% > 80%, Memory% < 50% → Compute Bound,优化方向是减算力 / 用 Tensor Core
SM% < 50%, Memory% > 80% → Memory Bound,优化方向是减访存 / 提高 AI
两个都 < 50% → Latency Bound,Occupancy 不够,优化方向是改 Block Size / 减寄存器
4.3 实战例子
| 场景 | SM% | Mem% | 诊断 |
|---|---|---|---|
| GEMM(BM=128) | 92% | 30% | Compute Bound,完美 |
| Softmax | 15% | 85% | Memory Bound,正常 |
| Attention(无 Flash) | 35% | 95% | Memory Bound,改 FlashAttention |
| LayerNorm | 10% | 70% | Memory Bound + Latency Bound,需要融合 |
| Bad GEMM(register spill) | 25% | 30% | Latency Bound,降寄存器 |
4.4 如果 Compute Bound 但没用 Tensor Core
看 Compute Workload 面板的 Tensor Pipe Utilization——如果接近 0,说明你只用了 CUDA Core,改用 WMMA / cuBLAS 立刻能提升 5-10 倍。
5. PyTorch Profiler:框架级视图
5.1 基本用法
from torch.profiler import profile, ProfilerActivity, schedule
with profile(
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
schedule=schedule(wait=1, warmup=1, active=3, repeat=2),
on_trace_ready=torch.profiler.tensorboard_trace_handler('./logs/profile'),
record_shapes=True,
profile_memory=True,
with_stack=True,
) as prof:
for step in range(10):
train_step()
prof.step()
5.2 终端摘要
print(prof.key_averages().table(sort_by="cuda_time_total", row_limit=20))
会看到一张表:每个 op 的 CPU/CUDA 时间、调用次数、平均耗时——能立刻锁定耗时大户。
5.3 TensorBoard 可视化
pip install torch-tb-profiler
tensorboard --logdir=./logs
会有几个标签页:
- Overview:整体时间分布(GPU kernel / CPU exec / DataLoader / Optimizer)
- Operator:按 op 类型分组的耗时
- Kernel:按 GPU kernel 分组
- Trace:类似 Chrome tracing 的时间线
- Memory:显存使用曲线
5.4 chrome://tracing 看 trace
prof.export_chrome_trace("trace.json")
# 打开 chrome://tracing,加载 trace.json
适合细粒度分析单个 iteration。
6. 标准排查流程
6.1 训练慢
1. 跑一次 nsys profile,看时间线
↓
2. GPU 利用率高吗?
├─ < 50% → 找 idle gap 来源(CPU 数据 / 通信 / sync)
└─ > 80% → 进入第 3 步
↓
3. 哪个 kernel 占时间最多?
↓
4. ncu 抓那个 kernel,看 SOL
├─ SM% 高 + Mem% 低 → Compute bound,优化算法 / 用 Tensor Core
├─ SM% 低 + Mem% 高 → Memory bound,减访存 / 算子融合
└─ 两者都低 → Latency bound,改 Block Size / 寄存器
6.2 推理慢
1. PyTorch Profiler 看 op 分布
↓
2. 找耗时大户(通常是 Attention / GEMM / LayerNorm)
↓
3. 该 op 已经是 SDPA / FlashAttention 了吗?
├─ 否 → 替换
└─ 是 → 用 ncu 下钻看是否还有空间
↓
4. KV Cache 显存够吗?
├─ 不够 → PagedAttention / 量化
└─ 够 → 提高并发
6.3 OOM
1. torch.cuda.memory_summary() 看哪一步显存激增
↓
2. torch.cuda.memory._record_memory_history → 上传 memory_viz
↓
3. 找峰值的来源(激活? 优化器状态? 临时 buffer?)
↓
4. 对症下药:
├─ 激活大 → Activation Checkpointing / 减 batch size
├─ 优化器大 → ZeRO / 8bit optimizer
└─ 临时 buffer → 检查是否有 .clone() 没释放
✅ 自我检验清单
- Nsys 实操:能用
nsys profile抓一次训练 trace,在 GUI 中找到最长的 kernel - GPU idle gap:能根据 trace 判断 gap 来源(CPU / 通信 / sync / launch overhead)
- NVTX 标注:能用
nvtx.range标注 forward / backward / optimizer 三个阶段 - Ncu 实操:能抓单个 kernel 的 ncu 报告,在 SOL 面板判断 bound 类型
- Compute vs Memory bound:看 SOL 数字能立刻给出优化方向
- Tensor Core 检查:能从 Compute Workload 面板看 Tensor Pipe Utilization
- PyTorch Profiler:能集成到训练脚本,在 TensorBoard 中可视化
- chrome://tracing:能导出 trace.json 并在 Chrome 中查看
- OOM 排查:能用 memory_viz 工具找出显存峰值来源
- 完整流程:面对一个”训练慢”的问题,能给出系统化的排查步骤
📚 参考资料
官方文档
- Nsight Systems User Guide:https://docs.nvidia.com/nsight-systems/UserGuide/
- Nsight Compute Documentation:https://docs.nvidia.com/nsight-compute/
- NVIDIA Profiler User’s Guide:https://docs.nvidia.com/cuda/profiler-users-guide/
- PyTorch Profiler Recipe:https://pytorch.org/tutorials/recipes/recipes/profiler_recipe.html
- PyTorch Memory Visualization:https://pytorch.org/blog/understanding-gpu-memory-1/
教程与博客
- NVIDIA GTC Profiling 系列 talk:每年都会更新最佳实践
- Andrei Damian:How to Use Nsight Systems:实战入门
- 方佳瑞:从 Nsight 报告看 LLM 推理优化
工具
- PyTorch TensorBoard Profiler 插件:
pip install torch-tb-profiler - Perfetto:https://ui.perfetto.dev/ —— 比 chrome://tracing 更强的 trace 可视化
系统级综述
- AI Systems Performance Engineering(Chris Fregly, O’Reilly 2025):learning.oreilly.com —— Ch5 系统讨论 CUDA 编程与 profiling、Ch7 PyTorch 调优,把单点 profile 工具串成”全栈瓶颈定位”流程