第一章 性能分析工具全景:从宏观到微观的武器库

做算子调优,说白了就是跟性能瓶颈死磕。但你要是连瓶颈在哪都不知道,那调优就是瞎忙活。我个人习惯,拿到一个模型或者算子,第一件事不是改代码,而是先上工具看数据。

这一章,我把常用的性能分析工具给你捋一遍。Nsight Systems、Nsight Compute、ncu命令行、PyTorch Profiler、TensorBoard,这五个家伙各有各的脾气,也各有各的绝活。

1.1 工具定位:谁该在什么时候用?

先给你一张图,把工具之间的关系理清楚。我当年刚接触这些工具时,也花了不少时间才搞明白它们的分工。

性能分析工具全景图 Nsight Systems 宏观时间线 · 整体瓶颈 Nsight Compute 算子内核 · 指令级分析 ncu 命令行 自动化 · CI集成 PyTorch Profiler 框架级 · 自动梯度 TensorBoard 可视化 · 对比分析 互补 互补 互补 宏观 → 微观 → 自动化,层层递进

嗯,这张图你看懂了吗?说白了,Nsight Systems 是看全局的,Nsight Compute 是钻细节的,ncu 是跑批量的,PyTorch Profiler 是跟框架绑定的,TensorBoard 是画图的。它们不是替代关系,是互补关系。

1.2 Nsight Systems:先看全局,再谈优化

我个人习惯,拿到一个性能问题,第一个打开的就是 Nsight Systems。为什么?因为它能给你一张完整的时间线图,告诉你 GPU 在干嘛、CPU 在干嘛、数据拷贝花了多久。

核心价值:一眼看出「瓶颈在哪一层」——是计算慢?还是数据传输慢?还是 Kernel 启动开销大?

我记得有一次,团队里一个小伙伴跑模型发现 GPU 利用率只有 30%,他怀疑是算子写得不好。我让他先跑一下 Nsight Systems,结果发现大部分时间都花在 CPU 端的 PyTorch 调度上,GPU 一直在等活干。这根本不是算子的问题,是数据加载和预处理拖了后腿。

使用技巧:用 nsys profile -o output_file python train.py 采集数据,然后用 nsys-ui 打开 GUI 看。重点关注「GPU 活动」和「CPU 活动」的重叠情况。

1.3 Nsight Compute:钻到算子内部去

当你用 Nsight Systems 定位到某个算子是瓶颈后,就该 Nsight Compute 上场了。它能告诉你这个 Kernel 内部发生了什么——指令吞吐、内存带宽、warp 占用率、bank conflict 等等。

说白了,Nsight Compute 就是算子的「显微镜」。我刚开始用的时候,看到那一堆指标也头大。后来慢慢总结出几个关键指标:

指标 含义 理想值
SM 吞吐 每个 SM 每周期能执行多少指令 接近理论峰值
内存带宽利用率 实际带宽 / 理论带宽 > 80%
Warp 占用率 活跃 warp 数 / 最大 warp 数 > 50%
L1/L2 命中率 缓存命中情况 越高越好

避坑指南:我曾经犯过一个错误——只看占用率不看内存带宽。结果占用率很高,但带宽利用率很低,说明线程都在空转等数据。后来我学会了,要结合「计算瓶颈」和「访存瓶颈」两个维度一起看。

1.4 ncu 命令行:自动化分析的利器

GUI 工具虽然直观,但没法集成到自动化流程里。这时候 ncu 命令行就派上用场了。你想想看,如果你每天要跑几十个算子的性能测试,一个个点 GUI 不得累死?

ncu 的基本用法很简单:

# 采集单个算子的性能数据
ncu --set full -o output_file python run_benchmark.py

# 只采集关键指标,减少开销
ncu --metrics sm__throughput.avg.pct_of_peak_sustained_elapsed,dram__throughput.avg.pct_of_peak_elapsed python run_benchmark.py

我个人习惯,在 CI 流程里加一个 ncu 的步骤,每次提交代码自动跑一遍,如果某个算子的性能下降了 5% 以上,就报警。这样能及早发现问题,不用等到上线才抓瞎。

1.5 PyTorch Profiler:框架级别的便利

如果你用的是 PyTorch,那 PyTorch Profiler 绝对是你的好帮手。它跟框架深度集成,能自动记录每个算子的执行时间、显存分配、梯度计算等。

用法也很简单:

import torch.profiler as profiler

with profiler.profile(
    activities=[profiler.ProfilerActivity.CPU, profiler.ProfilerActivity.CUDA],
    record_shapes=True,
    with_stack=True
) as prof:
    output = model(input_tensor)
    loss = loss_fn(output, target)
    loss.backward()

# 打印排序后的结果
print(prof.key_averages().table(sort_by="cuda_time_total", row_limit=10))

嗯,这里要注意一点:PyTorch Profiler 的开销比 Nsight 工具要大一些,所以生产环境慎用。我一般只在调试阶段开它。

1.6 TensorBoard:让数据说话

最后说说 TensorBoard。很多人觉得它只是用来画 loss 曲线的,其实它的性能分析功能也很强大。PyTorch Profiler 可以直接导出 TensorBoard 格式的数据:

prof.export_chrome_trace("trace.json")

然后你用 TensorBoard 打开,就能看到类似 Nsight Systems 的时间线图。虽然精细度不如 Nsight,但胜在方便——不用切换工具,一个浏览器全搞定。

我的习惯:先用 PyTorch Profiler + TensorBoard 快速扫一遍,发现可疑的算子再用 Nsight Compute 深挖。这样效率最高。

1.7 工具选择决策树

说了这么多,你可能会问:到底该用哪个?我给你一个简单的决策逻辑:

  • 想快速看整体性能 → Nsight Systems 或 PyTorch Profiler + TensorBoard
  • 想深挖某个算子的瓶颈 → Nsight Compute
  • 想自动化跑批量测试 → ncu 命令行
  • 想对比不同版本的性能 → TensorBoard 的对比视图

说白了,没有最好的工具,只有最合适的工具。我见过有人只用 ncu 命令行也能调出很好的性能,也有人离不开 GUI 的直观展示。关键是找到你自己顺手的那一套组合拳。

这一章的内容就到这里。记住,工具只是手段,理解性能瓶颈的本质才是目的。下一章我们开始实战,拿一个具体的算子来练练手。


想要更多资料,联系可可