2. 性能分析工具链概览:Linux perf、NVIDIA Nsight Systems、Nsight Compute、PyTorch Profiler、TensorBoard

做算子性能优化,说白了就是跟时间赛跑。你写了个算子,跑起来比预期慢了三倍,这时候怎么办?瞎猜?拍脑袋?我刚开始干这行的时候就这么干过,结果折腾了两天,发现瓶颈在一个完全没想到的地方。

后来我学乖了。性能优化,第一步不是动手改代码,而是先搞清楚——时间到底花在哪了。这就需要一套趁手的工具链。今天我就把平时最常用的几把「手术刀」给你捋一遍。

核心观点:没有万能工具,只有合适的场景。每个工具解决一个层面的问题,组合使用才能看清全貌。

2.1 Linux perf:系统级的「听诊器」

perf 是 Linux 内核自带的性能剖析工具。它不依赖任何第三方库,装上 Linux 就能用。我个人习惯把它当作第一道防线——先看看算子在整个系统层面有没有异常。

举个例子,你怀疑算子访存有瓶颈,可以这样采样:

# 采样 CPU 周期和缓存未命中
perf stat -e cycles,instructions,cache-misses ./my_operator

# 或者用火焰图看热点函数
perf record -g ./my_operator
perf script | stackcollapse-perf.pl | flamegraph.pl > flame.svg

perf 能告诉你什么?CPU 利用率、缓存命中率、分支预测错误率、上下文切换次数……这些都是硬件级别的指标。我在项目中遇到过一个问题:算子本身计算量不大,但跑起来 CPU 占用率只有 30%。用 perf 一查,发现大量时间花在了 page fault 上——原来是内存分配策略有问题。

小技巧:perf 的 -g 参数会记录调用栈,配合火焰图看,热点一目了然。但注意采样频率别太高,否则会影响程序运行。

2.2 NVIDIA Nsight Systems:GPU 层面的「时间轴」

如果你的算子跑在 GPU 上,perf 就有点力不从心了。它看不到 GPU 内部发生了什么。这时候就该 Nsight Systems 上场了。

Nsight Systems 说白了就是一个时间轴分析器。它能告诉你:CPU 在干什么、GPU 在干什么、数据传输花了多久、Kernel 启动有没有延迟。我建议你把它当作 GPU 性能分析的起点。

# 命令行采集
nsys profile -o my_profile -t cuda,nvtx ./my_operator

# 然后打开 GUI 看时间轴
nsys-ui my_profile.qdrep

你想想看,有时候你觉得是 Kernel 跑得慢,结果一看时间轴,发现大部分时间花在了 cudaMemcpy 上。嗯,这种情况我遇到过不下十次了。

注意:Nsight Systems 的采样开销很小,但如果你开启了全量 trace,数据量会非常大。建议先做粗粒度分析,再针对热点做细粒度采样。

2.3 Nsight Compute:Kernel 内部的「显微镜」

Nsight Systems 告诉你「哪个 Kernel 慢」,Nsight Compute 则告诉你「这个 Kernel 为什么慢」。它是针对单个 CUDA Kernel 的深度分析工具。

它能给出什么?占用率、内存带宽利用率、计算吞吐量、指令混合比、bank conflict……这些都是 GPU 微架构级别的指标。我曾经用它分析过一个矩阵乘算子,发现性能只有理论峰值的 20%。Nsight Compute 告诉我:L1 缓存命中率只有 30%,大量时间花在了等待数据上。

# 分析单个 Kernel
ncu --kernel-name my_kernel -o my_kernel_report ./my_operator

# 或者直接看关键指标
ncu --set full --kernel-name my_kernel ./my_operator

核心指标:关注 Memory Throughput 和 Compute Throughput 的比值。如果 Memory 远低于理论值,说明访存有瓶颈;如果 Compute 远低于理论值,说明计算单元没喂饱。

2.4 PyTorch Profiler:框架层面的「探针」

如果你用 PyTorch 开发算子,PyTorch Profiler 是最直接的入口。它不需要额外安装,PyTorch 自带。它能自动捕获算子的执行时间、显存分配、CUDA 事件等。

import torch.profiler as profiler

with profiler.profile(
    activities=[profiler.ProfilerActivity.CPU, profiler.ProfilerActivity.CUDA],
    record_shapes=True,
    with_stack=True
) as prof:
    output = my_operator(input_tensor)

print(prof.key_averages().table(sort_by="cuda_time_total", row_limit=10))

我个人习惯先用 PyTorch Profiler 快速扫一遍,看看哪个算子最耗时。然后针对那个算子,再用 Nsight Compute 做深度分析。这样效率最高。

避坑指南:我曾经因为没加 record_shapes=True,导致看不到张量形状信息,分析半天找不到问题。记得加上这个参数,它能帮你定位到具体的输入尺寸。

2.5 TensorBoard:可视化的「仪表盘」

TensorBoard 不只是一个训练可视化工具。它也能展示性能数据。PyTorch Profiler 可以直接导出 TensorBoard 格式,让你在浏览器里交互式地查看时间轴和算子统计。

# 导出为 TensorBoard 格式
prof.export_chrome_trace("trace.json")

# 或者直接在 TensorBoard 里看
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter("log_dir")
writer.add_profiler(prof)
writer.close()

TensorBoard 的好处是直观。你可以拖拽时间轴,放大缩小,看每个算子的调用关系。我经常用它来跟同事沟通——一张截图比说半天话管用多了。

2.6 工具链的选择策略

说了这么多,你可能会问:到底该用哪个?我的建议是分层使用:

层次 工具 解决什么问题
系统级 Linux perf CPU 利用率、缓存、内存、IO 瓶颈
GPU 级 Nsight Systems CPU-GPU 交互、数据传输、Kernel 启动
Kernel 级 Nsight Compute GPU 微架构瓶颈、占用率、访存模式
框架级 PyTorch Profiler 算子级耗时、显存分配、调用栈
可视化 TensorBoard 交互式分析、团队沟通

你想想看,如果一上来就用 Nsight Compute 去分析一个 Kernel,结果发现瓶颈根本不在 GPU 上,那不就白费功夫了?所以我的习惯是:从外到内,逐层深入。先用 perf 看系统,再用 Nsight Systems 看 GPU 整体,最后用 Nsight Compute 看具体 Kernel。

总结一句话:perf 管系统,Nsight Systems 管全局,Nsight Compute 管细节,PyTorch Profiler 管框架,TensorBoard 管展示。五把刀各司其职,组合起来就是一套完整的性能分析流水线。

算子性能分析工具链 从系统到 Kernel,逐层深入 算子性能瓶颈 Linux perf 系统级 · CPU/缓存/内存 Nsight Systems GPU 级 · 时间轴/传输 Nsight Compute Kernel 级 · 微架构 PyTorch Profiler 框架级 · 算子耗时 TensorBoard 可视化 · 交互式分析 推荐分析流程 ① Linux perf → 排除系统级瓶颈 ② Nsight Systems → 定位慢 Kernel ③ Nsight Compute → 深度分析 Kernel ④ PyTorch Profiler + TensorBoard → 框架级验证
可可精品资料库