3、Nsight Systems入门:安装与配置、Timeline视图初探、捕获第一个Profile
好,咱们正式开始动手了。
前面两章聊了不少概念,什么GPU硬件架构、任务调度流水线,听着挺热闹。但说句实在话,没有工具辅助,你根本看不到这些调度到底是怎么跑的。就像修车,光看说明书不行,你得把引擎盖打开,接上诊断仪,看实时数据流。
Nsight Systems,就是CUDA程序的那台「诊断仪」。这一章,我带你把它装好,跑通,抓到第一个Profile数据。
3.1 安装与配置:别在这步卡住
安装本身不复杂,但有几个坑,我当年都踩过。
3.1.1 下载与版本选择
去NVIDIA官网下载Nsight Systems。我个人习惯直接装独立版,不依赖Visual Studio或Eclipse。这样最干净,也最灵活。
- Windows:下载 .exe 安装包,一路Next就行。注意安装路径不要有中文。
- Linux:下载 .run 文件。我建议用
sudo sh -c "echo 0 > /proc/sys/kernel/perf_event_paranoid"临时放开性能计数器权限,否则有些硬件事件抓不到。 - 版本匹配:Nsight Systems版本最好和你的CUDA Toolkit版本对应。比如CUDA 12.x,就用Nsight Systems 2024.x。混搭也能用,但偶尔会出些奇怪的小毛病。
3.1.2 环境变量配置
装完之后,最好把Nsight的bin目录加到PATH里。这样你可以在终端里直接敲 nsys 命令。
# Linux 示例,加到 ~/.bashrc 里
export PATH=/opt/nvidia/nsight-systems/2024.3/bin:$PATH
Windows用户更简单,安装程序会自动处理环境变量。如果没生效,手动加一下系统PATH就行。
3.1.3 验证安装
打开终端,敲下面这行命令:
nsys --version
如果看到类似 NVIDIA Nsight Systems version 2024.3.1 的输出,恭喜你,装好了。
LD_LIBRARY_PATH。如果你也遇到类似问题,检查一下CUDA的lib路径有没有被正确加载。
3.2 Timeline视图初探:你的第一张「调度地图」
装好之后,咱们先别急着抓数据。先打开Nsight Systems,看看它的主界面长什么样。
说白了,整个工具的核心就一个视图——Timeline视图。它把CPU和GPU上发生的所有事情,按时间轴画出来。
3.2.1 界面布局
打开Nsight Systems,你会看到这样的布局:
- 左侧:进程/线程列表。显示所有被追踪的进程和线程。
- 中间:Timeline画布。这是主战场,所有事件都在这里展示。
- 右侧:详情面板。点击某个事件,这里会显示它的详细信息。
- 顶部:工具栏。控制抓取、回放、缩放等操作。
嗯,这里要注意:Timeline视图默认是按时间顺序从左到右排列的。CPU活动在上方,GPU活动在下方。你一眼就能看出CPU在等GPU,还是GPU在等CPU。
3.2.2 核心概念:Row与Event
Timeline里每一行(Row)代表一个执行单元。比如:
- CPU Thread Row:显示某个CPU线程上的函数调用、API调用。
- GPU Stream Row:显示某个CUDA Stream上的Kernel执行、Memcpy操作。
- GPU HW Queue Row:显示硬件队列里的实际调度情况。
每个彩色的小方块就是一个Event。比如一个Kernel启动、一次cudaMemcpy、一次同步操作。颜色越深,通常表示耗时越长。
3.3 捕获第一个Profile:动手吧
理论说再多,不如跑一次。咱们写一个最简单的CUDA程序,然后用Nsight抓它的Profile数据。
3.3.1 准备一个测试程序
随便写个向量加法的程序就行。比如:
// vector_add.cu
#include <cuda_runtime.h>
#include <stdio.h>
__global__ void vecAdd(float *a, float *b, float *c, int n) {
int idx = threadIdx.x + blockIdx.x * blockDim.x;
if (idx < n) {
c[idx] = a[idx] + b[idx];
}
}
int main() {
int n = 1 << 20;
size_t bytes = n * sizeof(float);
float *h_a, *h_b, *h_c;
float *d_a, *d_b, *d_c;
h_a = (float*)malloc(bytes);
h_b = (float*)malloc(bytes);
h_c = (float*)malloc(bytes);
for (int i = 0; i < n; i++) {
h_a[i] = 1.0f;
h_b[i] = 2.0f;
}
cudaMalloc(&d_a, bytes);
cudaMalloc(&d_b, bytes);
cudaMalloc(&d_c, bytes);
cudaMemcpy(d_a, h_a, bytes, cudaMemcpyHostToDevice);
cudaMemcpy(d_b, h_b, bytes, cudaMemcpyHostToDevice);
vecAdd<<<256, 256>>>(d_a, d_b, d_c, n);
cudaMemcpy(h_c, d_c, bytes, cudaMemcpyDeviceToHost);
printf("Result: %f\n", h_c[0]);
cudaFree(d_a);
cudaFree(d_b);
cudaFree(d_c);
free(h_a);
free(h_b);
free(h_c);
return 0;
}
编译它:
nvcc -o vector_add vector_add.cu
3.3.2 用nsys命令行抓取
我个人习惯先用命令行抓,因为可以精确控制抓取范围。打开终端,执行:
nsys profile -o my_first_profile ./vector_add
这条命令的意思是:
nsys profile:启动性能分析器。-o my_first_profile:输出文件名为my_first_profile,默认生成.nsys-rep文件。./vector_add:要分析的目标程序。
运行结束后,你会看到类似这样的输出:
Processing events...
[1] Vector Add: 100% (2/2)
Generated: /home/user/my_first_profile.nsys-rep
--gpu-metrics-device=0 参数。这样能减少CPU侧的开销,让Profile数据更干净。我调试GPU瓶颈时经常这么干。
3.3.3 在GUI中打开并查看
双击生成的 .nsys-rep 文件,或者直接在Nsight Systems里打开它。你会看到类似下面的Timeline:
看到这张图了吗?这就是你抓到的第一个Profile。你能清楚地看到:
- CPU先调了
cudaMalloc和cudaMemcpy(蓝色和红色方块)。 - 然后GPU开始执行Kernel(橙色方块)。
- 最后CPU等待GPU把结果拷回来(红色方块)。
你想想看,如果没有这个Timeline,你只能靠猜。现在一目了然:CPU在等GPU,还是GPU在等数据?
3.4 本章小结
这一章,咱们把Nsight Systems装好了,跑通了,也看到了第一个Timeline视图。说白了,就是让你知道:
- 怎么装、怎么配、怎么验证。
- Timeline视图长什么样,Row和Event是什么意思。
- 怎么用命令行抓一个Profile,然后在GUI里看。
嗯,到这里,你已经有了最基本的「诊断仪」。下一章,咱们会深入Timeline视图的细节,看看那些花花绿绿的方块到底代表什么,以及怎么从里面挖出性能瓶颈。
记住:工具只是手段,洞察才是目的。别急着优化,先看清楚你的程序到底在干什么。
想要更多资料,联系可可