3、NVIDIA Nsight Systems入门:安装与配置、Timeline视图、关键指标解读
好,咱们开始聊Nsight Systems。说实话,这工具是我日常工作中用得最多的一个。你想想看,写CUDA代码,最怕什么?不是写不出来,是写出来跑不快。跑不快又不知道卡在哪。Nsight Systems就是帮你找到那个「卡点」的利器。
我个人习惯,拿到一个新项目,第一件事不是看代码,而是先跑一遍Nsight Systems。为什么?因为它能给你一个全局视角。就像你进到一个工厂,先站在二楼往下看,哪条流水线堵了,哪台机器在空转,一目了然。
3.1 安装与配置
安装其实没什么难度。去NVIDIA官网下载对应你操作系统的版本就行。Windows、Linux、macOS都支持。我建议你直接装最新的,因为老版本有时候对新显卡的支持不太好。
这里有个坑,我踩过——环境变量。装完之后,记得把Nsight的bin目录加到PATH里。不然你在命令行敲nsys会提示找不到命令。嗯,我当时折腾了十分钟才反应过来。
配置方面,默认设置其实就够用。但如果你要抓取长时间运行的程序,建议调整一下--duration参数。默认是60秒,超过会自动停止。
# 基本用法
nsys profile -o my_profile ./my_cuda_app
# 指定抓取时长(单位:秒)
nsys profile --duration=120 -o my_profile ./my_cuda_app
# 只抓取GPU相关事件
nsys profile -t cuda,osrt -o my_profile ./my_cuda_app
我个人习惯用-t cuda,osrt,nvtx这个组合。cuda抓GPU事件,osrt抓系统调用,nvtx抓你自己打的标记。这样信息最全,又不会太冗余。
3.2 Timeline视图
打开生成的.qdrep文件,你首先看到的就是Timeline视图。说白了,就是一条时间轴,上面画满了各种颜色的条条块块。
别被吓到。咱们拆开来看。
Timeline视图分上下两部分:
- 上半部分:CPU活动。显示每个线程在干什么。比如主线程在提交kernel,其他线程可能在等同步。
- 下半部分:GPU活动。显示每个stream上的kernel执行、内存拷贝、同步操作。
你想想看,如果CPU和GPU之间有大段的空白,说明什么?说明有一方在等另一方。这就是典型的「负载不均衡」。
我记得有一次,帮一个团队优化一个深度学习推理程序。他们觉得GPU利用率已经很高了,结果我一打开Timeline视图,发现GPU在大部分时间都在等CPU把数据准备好。说白了,数据搬运成了瓶颈。
3.3 关键指标解读
光看颜色条还不够,你得会读数字。Nsight Systems里几个关键指标,我一个个说。
3.3.1 GPU Utilization
这个指标告诉你GPU有多忙。但注意,不是越高越好。如果GPU Utilization一直100%,但你的程序跑得并不快,那说明你的kernel可能太复杂了,或者有别的隐藏问题。
我见过一个案例,GPU Utilization 98%,但吞吐量只有理论峰值的30%。后来发现是kernel里用了太多atomicAdd,导致内存访问冲突严重。嗯,这就是典型的「看似忙碌,实则低效」。
3.3.2 Memory Throughput
内存吞吐量,分读和写。这个指标能帮你判断是不是「内存带宽受限」。
| 指标 | 正常范围 | 瓶颈信号 |
|---|---|---|
| Memory Throughput (Read) | 60%-90% 峰值 | <30% 说明kernel计算密集或访存模式差 |
| Memory Throughput (Write) | 40%-70% 峰值 | >90% 说明写操作过多,可能需优化 |
| Compute Utilization | 40%-80% | <20% 说明计算单元没充分利用 |
为什么会这样?说白了,如果你的kernel是计算密集型的,那内存吞吐量低是正常的。但如果你觉得它应该是访存密集型的,结果内存吞吐量却很低,那就要检查你的数据访问模式了——是不是没做合并访问?
3.3.3 API Trace
这个视图显示的是CUDA API的调用情况。比如cudaMemcpy、cudaLaunchKernel这些。
我建议你重点关注两个东西:
- cudaMemcpy的耗时。如果它占了总时间的很大比例,说明数据搬运是瓶颈。考虑用异步拷贝或者统一内存。
- cudaLaunchKernel的延迟。如果每次launch kernel都有几十微秒的延迟,那说明你的kernel太小了。可以考虑合并多个小kernel。
3.3.4 NVTX标记
这个是我个人最推荐的。你可以在代码里插入NVTX标记,把程序的不同阶段标出来。比如「数据加载」、「预处理」、「推理」、「后处理」。
#include <nvtx3/nvToolsExt.h>
// 标记一个范围
nvtxRangePushA("Data Loading");
// ... 数据加载代码 ...
nvtxRangePop();
// 标记一个瞬间事件
nvtxMarkA("Kernel Launched");
有了这些标记,你在Timeline视图里就能一眼看出,哪个阶段耗时最长。我曾经帮一个客户优化,他们觉得是kernel慢,结果我一看NVTX标记,发现「数据预处理」阶段占了60%的时间。嗯,方向完全错了。
3.4 实战建议
最后,给你几个实战中的小建议:
- 先跑一次全量profile。不要一开始就想着只抓某个部分。全量profile能给你全局视角。
- 关注「空隙」。Timeline视图里,任何一段空白都值得你点进去看看。是CPU在等?还是GPU在等?
- 对比优化前后。每次改完代码,都跑一次profile。对比两次的Timeline视图,看看你改的地方有没有效果。
- 别只看平均值。有时候平均值好看,但个别kernel拖了后腿。用Nsight Systems的统计功能,看看每个kernel的耗时分布。
我曾经有一个项目,优化了一个月,性能提升不到10%。后来用Nsight Systems一看,发现有个很小的kernel,每次只跑几微秒,但被调用了上百万次。累计耗时占了总时间的40%。我把这个小kernel合并到前一个kernel里,性能直接翻倍。你想想看,这就是工具的价值。
好,这一章就到这里。下一章咱们聊Nsight Compute,那个是专门分析单个kernel的。到时候你会看到更细粒度的指标。