CUDA × JetPack 初学者全指南
📖 推荐博主书籍:《Yocto项目实战教程:高效定制嵌入式Linux系统》
🎥 更多学习视频请关注 B 站:嵌入式Jerry
CUDA × JetPack 初学者全指南
面向零基础到入门开发者:帮你在一篇文章里建立全局心智图,并给出可直接实践的命令与代码。
0. 你将获得什么
- 两层“Host/Device 与 Host/Target”概念一次搞懂。
- 在 x86 主机上编译并运行 CUDA 样例(完整可复制)。
- JetPack 的组成、何时需要它,以及无板也能学的路线。
- 常见报错的源头 → 现象 → 修复对照表。
- 交叉编译 Jetson 的标准做法(可以后再用)。
1. 两个“Host / Device”别混淆
1.1 CUDA 编程层(同一台机器里)
CPU(Host) ──控制/准备数据──► GPU(Device)◄─拷回结果────────
- Host:CPU + 主存,运行你的 C/C++ 代码(
main()
、cudaMemcpy
…)。 - Device:GPU + 显存,运行
__global__
标记的 kernel。
1.2 开发部署层(两台设备之间)
开发机(Host PC, Ubuntu x86_64) ──构建/烧写/部署──► 目标机(Target, Jetson)
- Host(开发机) 与 Target(Jetson 目标板) 是两台设备的关系。
- 当你只有 PC 时,只做 CUDA Host 练习;需要上板时再用 JetPack/交叉编译。
记忆法:CUDA 的 Host/Device = CPU/GPU;JetPack 的 Host/Target = 开发机/目标板。
2. CUDA 的核心最小知识集
知识点 | 一句话说明 | 你需要记住的要点 |
---|---|---|
__global__ | 声明 GPU 内核(kernel),CPU 调用,GPU 执行 | 必须 void 返回;用 <<<grid, block>>> 启动 |
线程索引 | i = blockIdx.x * blockDim.x + threadIdx.x | 每个线程处理一小块数据 |
内存模型 | Host 内存 & Device 显存分离 | 用 cudaMemcpy 在两者间拷贝 |
同步与错误 | cudaGetLastError() + cudaDeviceSynchronize() | 不检查=静默失败=打印垃圾值 |
nvcc 编译 | nvcc 驱动 host 编译器 + GPU 编译链 | 必要时指定 -arch=sm_XX (与你的 GPU 匹配) |
常见 -arch
对照(示例):Pascal 6.1→sm_61
,Turing 7.5→sm_75
,Ampere 8.6→sm_86
,Ada 8.9→sm_89
。
3. 在 x86 主机上立即可跑的最小闭环
3.1 环境自检
nvidia-smi # 能看到 GPU 列表与驱动版本
which nvcc && nvcc --version
若
nvidia-smi
异常,先安装/修复 NVIDIA 驱动;仅装 CUDA Toolkit 但没有驱动,是跑不起来的。
3.2 最小可运行样例(带错误检查 + 注释)
#include <cuda_runtime.h> // CUDA 运行时 API 头文件
#include <cstdio> // 标准输入输出// 宏:检查每一次 CUDA API 调用是否返回错误
#define CHECK(x) do{ cudaError_t e=(x); if(e!=cudaSuccess){ \std::fprintf(stderr,"[CUDA ERROR] %s:%d %s\n",__FILE__,__LINE__,cudaGetErrorString(e)); \std::exit(1);} }while(0)// __global__ 表示这个函数在 GPU(Device) 上执行,由 CPU(Host) 调用
__global__ void add(const int* a,const int* b,int* c,int n){int i = blockIdx.x*blockDim.x + threadIdx.x; // 全局线程索引if(i<n) c[i] = a[i] + b[i]; // 每个线程计算一个元素
}int main(){// 1. 检查系统是否有可用的 GPUint devs=0; CHECK(cudaGetDeviceCount(&devs));if(devs==0){ std::puts("No CUDA device."); return 1; }// 2. 在 Host 上准备数据const int N=8; int ha[N],hb[N],hc[N]={0};for(int i=0;i<N;i++){ ha[i]=i; hb[i]=10*i; }// 3. 在 Device 上分配内存int *da=nullptr,*db=nullptr,*dc=nullptr; size_t bytes=N*sizeof(int);CHECK(cudaMalloc(&da,bytes));CHECK(cudaMalloc(&db,bytes));CHECK(cudaMalloc(&dc,bytes));// 4. 将数据从 Host 拷贝到 DeviceCHECK(cudaMemcpy(da,ha,bytes,cudaMemcpyHostToDevice));CHECK(cudaMemcpy(db,hb,bytes,cudaMemcpyHostToDevice));// 5. 定义线程组织方式并启动核函数dim3 block(128), grid((N+block.x-1)/block.x);add<<<grid,block>>>(da,db,dc,N);CHECK(cudaGetLastError()); // 检查内核启动错误CHECK(cudaDeviceSynchronize()); // 等待 GPU 完成// 6. 将结果从 Device 拷贝回 HostCHECK(cudaMemcpy(hc,dc,bytes,cudaMemcpyDeviceToHost));// 7. 输出结果for(int i=0;i<N;i++) std::printf("%d ",hc[i]); std::puts("");// 8. 释放 Device 内存CHECK(cudaFree(da));CHECK(cudaFree(db));CHECK(cudaFree(dc));
}
编译与运行(sm_XX
改为你的 GPU 架构):
nvcc -std=c++14 -arch=sm_86 vecadd.cu -o vecadd_x86
./vecadd_x86 # 输出:0 11 22 33 44 55 66 77
3.3 观察性能与正确性
- 运行
compute-sanitizer ./vecadd_x86
(旧名cuda-memcheck
)可以抓越界等错误。 - 用 Nsight Systems(
nsys-ui
或nsys profile
)看 kernel 启动与时间线。
4. JetPack 是什么、何时需要它
4.1 定义与组成
JetPack SDK = Host 端 + Target 端 的完整开发/运行环境
┌───────────────────────────┬───────────────────────────┐
│ Host Components(PC) │ Target Components(Jetson) │
├───────────────────────────┼───────────────────────────┤
│ 交叉编译工具链、nvcc │ L4T(内核+BSP+驱动) │
│ Nsight 系列 │ CUDA/TensorRT Runtime │
│ VPI/CUDA 开发包 │ VPI/OpenCV/多媒体API │
└───────────────────────────┴───────────────────────────┘
- 只有 PC 时:用 Host 端内容完成学习(本地 CUDA、VPI Host 示例、Nsight)。
- 要在 Jetson 上跑:安装 Target 端(驱动、Runtime、BSP),或用 SDK Manager 烧写。
4.2 典型应用场景
- 机器人/自动驾驶:视觉 + 推理 + 传感器融合。
- 工业检测/边缘计算:高帧率视频编解码 + 算法推理。
5. 无板也能学——建议路线
- 主机 CUDA:完成 §3 的最小闭环 → 熟练内存拷贝、线程组织、错误检查。
- 工具链:学会用 Nsight Systems/Compute 观测 kernel 与瓶颈。
- 视觉/多媒体:安装 VPI Host 包,运行 CPU/GPU 可执行的 sample(不涉及 Jetson 专有单元时可在 PC 上跑)。
- 工程化:用 CMake/Makefile 管理
-arch
、Debug/Release、多架构 fatbin。 - 预备交叉编译(可先不做):了解 sysroot 与
--target-dir=aarch64-linux
的概念即可。
6. 以后需要上板:交叉编译到 Jetson(速记版)
等你有板子再做,此处先留存步骤。
6.1 安装交叉包(Host)
sudo apt-get install -y cuda-cross-aarch64-12-6 aarch64-linux-gnu-g++
6.2 准备 sysroot(从板子 rsync 或 L4T rootfs + apply_binaries.sh
)
$SYSROOT/usr/lib/aarch64-linux-gnu ...
$SYSROOT/lib/aarch64-linux-gnu ...
6.3 编译命令
nvcc app.cu -o app_arm64 \-ccbin=aarch64-linux-gnu-g++ \--compiler-options="--sysroot=$SYSROOT" \--linker-options="--sysroot=$SYSROOT" \--target-dir=aarch64-linux -arch=sm_87
CMake 工程可使用 toolchain 文件固定搜索路径,避免主机库混入。
7. 常见问题:源头 → 现象 → 修复(速查表)
源头 | 现象 | 修复 |
---|---|---|
无 GPU/驱动未加载 | 输出一堆随机数;或 No CUDA device | 安装并加载匹配驱动,nvidia-smi 正常 |
-arch 不匹配 | invalid device function | 用 nvidia-smi 查 compute capability,改 -arch=sm_XX |
未同步/未检查错误 | 结果不稳定/偶发垃圾值 | 加 cudaGetLastError() + cudaDeviceSynchronize() 并检查返回值 |
找不到 cudafe++/cicc/ptxas | not found | 安装 cuda-toolkit-XX + cuda-compiler-XX ,确保 /usr/local/cuda/bin 在 PATH |
cuda_runtime.h 不存在 | 头文件路径错误/Host Toolkit 未装 | 使用 /usr/local/cuda/include ;安装 Host Toolkit |
交叉编译链接失败 | 缺 sysroot 或混入主机库 | 准备好 $SYSROOT ,并传 --sysroot 、用 --target-dir=aarch64-linux |
8. 一页流程图(从开发到部署)
编写 CUDA 代码 → nvcc 编译(选对 -arch) → 本机运行 & Nsight 分析│▼(需要上板时)
准备 sysroot → 交叉编译 --target-dir=aarch64-linux → 传到 Jetson 运行
9. 术语速览(迷你词典)
- Kernel:GPU 上执行的函数,
__global__
声明,CPU 用<<<>>>
启动。 - Grid/Block/Thread:CUDA 的三层并行层级(网格/线程块/线程)。
- PTX/CUBIN:GPU 中间表示/二进制;
nvcc --ptx/--cubin
可导出。 - L4T:Linux for Tegra,Jetson 的 Linux(内核+BSP+驱动)。
- JetPack:Jetson 平台完整 SDK(Host + Target)。
10. 结语
先在 PC 上把 CUDA 的 Host↔Device 数据流、kernel 启动与同步练熟;再用 Nsight 观察;最后再走 JetPack/交叉编译到 Jetson。这样学习曲线最平滑、也最省坑。祝你练习顺利!
📖 推荐博主书籍:《Yocto项目实战教程:高效定制嵌入式Linux系统》
🎥 更多学习视频请关注 B 站:嵌入式Jerry