2. 搭建Jetson开发环境

Jetson 上的 CUDA 算子开发与桌面 GPU 有一个本质差异:算力、内存带宽、功耗墙和散热条件都被封装在一块嵌入式模组里,软件栈也由 NVIDIA 通过 JetPack 统一发布。因此环境搭建不是“装个驱动”这么简单,而是要把 L4T(Linux for Tegra)系统、CUDA、cuDNN、TensorRT、Nsight 工具链一次性对齐到同一版本,否则后续编译算子时会出现头文件与运行库不匹配、Nsight Compute 无法采集、甚至内核模块加载失败等问题。本节以 Jetson Orin 系列(如 Orin Nano 8GB / Orin NX 16GB)为例,走通从刷机到远程开发的全过程。

一、JetPack 安装:先定版本,再选刷机方式

JetPack 的版本决定了 CUDA 与 L4T 的对应关系。例如 JetPack 6.x 对应 L4T 36.x、CUDA 12.x、Ubuntu 22.04;JetPack 5.x 对应 L4T 35.x、CUDA 11.4/11.8、Ubuntu 20.04。选择原则是:先看你要用的算子依赖哪个 CUDA 版本,再反查 JetPack。如果使用较新的 PyTorch/TensorRT,优先 JetPack 6;如果依赖旧版 cuDNN 或厂商 SDK,则可能必须留在 JetPack 5。

刷机有两条路径。其一是 SDK Manager:在 Ubuntu 主机上安装 SDK Manager,用 USB-C 连接 Jetson 进入 Recovery 模式,选择目标模组与 JetPack 版本,工具会自动下载 BSP、烧写系统并安装 CUDA、cuDNN、TensorRT、OpenCV 等组件。其二是 SD 卡镜像:从 NVIDIA 官网下载对应模组的 .img,用 balenaEtcher 写入 SD 卡,插入后首次开机完成 OEM 配置。前者适合量产与需要精确控制组件的场景,后者适合快速验证。

刷机完成后,务必先确认基础环境:

cat /etc/nv_tegra_release        # 查看 L4T 版本
nvcc --version                   # 查看 CUDA 编译器版本
dpkg -l | grep cudnn             # 查看 cuDNN 版本
sudo jetson_clocks               # 锁定最高频率,避免调优时被降频干扰
sudo nvpmodel -q                 # 查看当前功耗模式(如 15W / MAXN)

其中 jetson_clocks 与 nvpmodel 对性能调优尤为关键:默认功耗模式会动态调频,导致同一算子的耗时波动可达 30% 以上。做基准测试前应固定为 MAXN 并锁定时钟,测完再恢复,保证数据可复现。

二、CUDA Samples 验证:确认工具链真的可用

系统装好不等于 CUDA 可用。最直接的验证方式是编译并运行 CUDA Samples。Jetson 上通常已预置 /usr/local/cuda/samples,若没有可从 GitHub 的 cuda-samples 仓库获取对应版本。

cd /usr/local/cuda/samples/1_Utilities/deviceQuery
sudo make
./deviceQuery

若输出末尾出现 Result = PASS,说明驱动、运行时与设备识别正常。接着编译带宽测试与一个真实算子样例:

cd ../../5_Domain_Specific/  # 或 0_Introduction
cd /usr/local/cuda/samples/0_Introduction/matrixMul
sudo make && ./matrixMul

观察 matrixMul 输出的 Performance= ... GFlop/s,可与理论峰值对比。以 Orin Nano 8GB 为例,其 GPU 约 1024 个 CUDA Core、频率约 1.0 GHz,FP32 理论峰值约 2 TFlop/s 量级;实际样例受内存带宽限制,通常只能达到峰值的 20%~40%,这属于正常现象,也正好说明后续算子优化的空间所在。若 deviceQuery 报 no CUDA-capable device,多半是刷机后未重启或驱动未加载,先 sudo reboot 再排查。

三、Nsight Systems 与 Nsight Compute 部署

两者分工不同:Nsight Systems(nsys) 做系统级时间线分析,看 CPU 线程、CUDA API、内核、内存拷贝、NVTX 标记之间的时序关系,回答“时间花在哪”;Nsight Compute(ncu) 做单内核深度剖析,看 SM 占用率、访存吞吐、Warp 停顿原因,回答“这个内核为什么慢”。

在 Jetson 上,nsys 通常随 JetPack 预装,ncu 需要单独确认:

nsys --version
ncu --version
# 若 ncu 缺失,从 NVIDIA 官网下载对应 L4T 版本的 .deb 安装
sudo dpkg -i nsight-compute-*.deb

Jetson 上使用 ncu 有一个重要限制:需要 root 权限并开启性能计数器。若直接运行报 ERR_NVGPUCTRPERM,需执行:

sudo sh -c 'echo 1 > /proc/sys/kernel/perf_event_paranoid'
sudo ncu --set full ./my_kernel

典型采集命令如下,先用 nsys 定位热点内核,再用 ncu 深挖:

# 系统级时间线,输出报告
nsys profile -o report_%p --stats=true ./my_app

# 单内核剖析,只看目标内核
ncu --kernel-name my_kernel --launch-count 1 --set full ./my_app

需要注意,ncu 会串行化内核执行并多次重放,测得的耗时不能当作真实性能,只用于分析瓶颈;真实性能仍以 nsys 或应用内计时为准。

四、远程开发配置

Jetson 通常作为边缘设备部署,直接接显示器调试不便,远程开发是常态。推荐组合是 SSH + VS Code Remote-SSH + NFS/rsync。先在 Jetson 上启用 SSH:

sudo systemctl enable ssh
sudo systemctl start ssh
hostname -I        # 获取 IP,例如 192.168.1.50

在开发机上配置免密登录,便于脚本化部署:

ssh-keygen -t ed25519
ssh-copy-id nvidia@192.168.1.50

随后在 VS Code 安装 Remote-SSH 插件,连接 nvidia@192.168.1.50,即可在 Jetson 上直接编辑、编译、调试 CUDA 代码。若代码量大,建议用 NFS 挂载开发机目录,避免频繁同步;若只是小规模迭代,用 rsync -avz --exclude build/ ./ nvidia@192.168.1.50:~/work/ 更简单。远程调试时,可在 launch.json 中配置 cuda-gdb,实现断点与内存检查。

建议在 Jetson 上创建独立工作目录 ~/work,并把 jetson_clocks、nvpmodel、nsys、ncu 的常用命令写成脚本,减少重复输入。

五、环境搭建的验证闭环

完成上述步骤后,用一个最小 CUDA 程序做端到端验证:编译通过、运行结果正确、nsys 能生成时间线、ncu 能采集到内核指标、VS Code 能远程断点。这五项都通过,才说明环境真正可用。下图给出从刷机到远程开发的完整链路与关键验证点。

Jetson 开发环境搭建链路与验证点 ① JetPack 安装 SDK Manager / SD 镜像 L4T + CUDA + cuDNN jetson_clocks / nvpmodel 验证:nvcc --version ② CUDA Samples deviceQuery matrixMul 带宽/算力 Result = PASS 验证:GFlop/s 输出 ③ Nsight 部署 nsys 时间线分析 ncu 内核剖析 perf_event_paranoid 验证:报告可生成 ④ 远程开发 SSH 免密 VS Code Remote rsync / NFS 验证:断点调试 工具链与观测对象对应关系 nsys CPU/GPU 时间线、API 回答“时间花在哪” ncu SM 占用、访存、停顿 回答“内核为何慢” jetson_clocks 锁定频率 保证数据可复现 nvpmodel 功耗模式 MAXN 决定算力上限 端到端验证闭环(五项全过才算环境可用) 编译通过 nvcc 无报错 结果正确 与 CPU 比对 nsys 时间线 报告可打开 ncu 采集 指标非空 远程断点 VS Code 可调试

最后给出一个可直接使用的环境自检脚本,把关键检查项串起来,每次换机或升级 JetPack 后跑一遍即可:

#!/bin/bash
echo "== L4T =="; cat /etc/nv_tegra_release
echo "== CUDA =="; nvcc --version | tail -2
echo "== cuDNN =="; dpkg -l | grep cudnn | awk '{print $2,$3}'
echo "== 功耗模式 =="; sudo nvpmodel -q
echo "== 时钟 =="; sudo jetson_clocks --show | head -5
echo "== nsys =="; nsys --version
echo "== ncu =="; ncu --version
echo "== deviceQuery =="; /usr/local/cuda/samples/1_Utilities/deviceQuery/deviceQuery | tail -3