CUDA 10 · 库、多 GPU、项目与进阶路线
先用库,再决定是否写 Kernel
需求
├─ 高层框架已有高质量算子? → 使用并 profile
├─ CUDA-X/CCCL/CUTLASS 已覆盖? → 使用库,配置 stream/layout/workspace
├─ 能通过组合 + Graph 达到目标? → 组合并捕获
├─ 需要融合/特殊布局/特殊语义? → 自定义 kernel
└─ 需要架构极限或新指令研究? → CUTLASS/CuTe/PTX + 专属 dispatch成熟库通常已经处理多代架构、边界 shape、数值细节和算法选择。手写版本应先与库建立 correctness 和性能基线。
CUDA 生态地图
| 库/组件 | 领域 | 常见用途 |
|---|---|---|
| cuBLAS / cuBLASLt | 稠密线性代数 | GEMM、batched GEMM、epilogue |
| cuSPARSE | 稀疏线性代数 | SpMV/SpMM、稀疏格式转换 |
| cuSOLVER | 稠密/稀疏求解 | LU/QR/SVD/eigensolver |
| cuFFT | FFT | 信号、卷积、谱方法 |
| cuRAND | 随机数 | host/device RNG |
| cuDNN | 深度学习 primitives | convolution、attention、norm 等 |
| NCCL | 多 GPU collective | all-reduce、all-gather、reduce-scatter |
| NPP / nvJPEG | 图像与编解码 | 图像处理、JPEG pipeline |
| CUB | CUDA 并行 primitives | warp/block/device reduce、scan、sort |
| Thrust | C++ 并行算法 | 容器、iterator、transform/sort/scan |
| libcu++ | CUDA C++ 标准库扩展 | atomic、barrier、pipeline、标准设施 |
| CUTLASS / CuTe | 矩阵 kernel 组件 | GEMM、layout、Tensor Core、fusion |
CUB、Thrust 与 libcu++ 统一在 CCCL(CUDA C++ Core Libraries)生态下。CUB 的 warp/block/device 分层尤其适合自定义 kernel 内部复用。
Library 集成检查表
- handle 是否绑定到正确 stream;
- 输入/输出 layout、leading dimension、stride 是否准确;
- host pointer mode 还是 device pointer mode;
- workspace 的大小、对齐、生命周期;
- 算法是否要求特定对齐、dtype、CC;
- 是否允许 TF32/降精度/非确定性算法;
- capture 到 CUDA Graph 时该 API 是否支持;
- handle 是否在线程间安全共享;
- 错误状态是否检查;
- benchmark 是否包含 heuristic/autotune 的一次性成本。
多 GPU 的三层问题
算法层:数据并行 / 张量并行 / 流水线并行 / 任务并行
通信层:P2P copy / collective / host staging / 网络
拓扑层:PCIe switch / NVLink / NVSwitch / NUMA / NIC affinity多 GPU 不是把 device_count 乘上去。通信量、同步点、拓扑和负载均衡决定可扩展性。
显式设备管理
int count = 0;
cudaGetDeviceCount(&count);
for (int d = 0; d < count; ++d) {
cudaSetDevice(d);
cudaDeviceProp p{};
cudaGetDeviceProperties(&p, d);
// 每个 device 分别创建 stream、event、allocation、library handle。
}CUDA 对象通常与创建它的 device/context 有关联。多 host thread 管多 GPU 时,在线程开头显式 cudaSetDevice,不要依赖之前调用遗留的 current device。
Peer-to-Peer 与拓扑
int can = 0;
cudaDeviceCanAccessPeer(&can, dst, src);
if (can) {
cudaSetDevice(dst);
cudaDeviceEnablePeerAccess(src, 0);
}随后可使用 peer copy 或在受支持条件下访问 peer memory。P2P 可用不等于一定走最高速 NVLink,也不等于任意访问模式都高效;使用 nvidia-smi topo -m、NVML 和 Systems 验证实际拓扑与路径。
若 P2P 不可用,Runtime 可能需要 host staging,带宽和同步行为会不同。跨 NUMA CPU socket 的 pinned buffer 也应考虑内存亲和性。
Collective 与通信复杂度
训练中常见:
| Collective | 语义 | 用例 |
|---|---|---|
| AllReduce | 每个 rank 获得归约后的完整结果 | 数据并行梯度 |
| ReduceScatter | 归约后每个 rank 保留一个分片 | ZeRO/分片训练 |
| AllGather | 收集所有 rank 的分片 | 参数/激活重构 |
| Broadcast | root 向所有 rank 发送 | 初始化、模型同步 |
| AllToAll | 每个 rank 向所有 rank 发送不同分片 | MoE token dispatch |
NCCL 会按拓扑和消息规模选择算法/协议。优化重点不是自己重写 ring,而是:
- 减少通信字节(分片、压缩、低精度);
- 将大通信拆分并与计算重叠;
- 避免过细 collective;
- 正确设置 rank-device 映射与拓扑;
- 用 Systems/NCCL tracing 找关键路径;
- 保证所有 rank 以一致顺序调用 collective,避免死锁。
扩展效率
强扩展:总问题规模固定,GPU 增加;通信和串行部分最终主导。Amdahl 近似:
弱扩展:每 GPU 工作量固定,总问题规模随 GPU 增长;更关注通信增长和负载均衡。报告多 GPU 性能时至少给出:单卡基线、全局/每卡 batch、吞吐、延迟、通信占比和 scaling efficiency。
生产级 C++ 资源管理
使用 RAII 封装 device memory、stream、event 和 library handle,避免异常/早退泄漏:
class CudaStream {
public:
CudaStream() { CUDA_CHECK(cudaStreamCreateWithFlags(&s_, cudaStreamNonBlocking)); }
~CudaStream() { if (s_) cudaStreamDestroy(s_); }
CudaStream(const CudaStream&) = delete;
CudaStream& operator=(const CudaStream&) = delete;
CudaStream(CudaStream&& other) noexcept : s_(other.s_) { other.s_ = nullptr; }
cudaStream_t get() const { return s_; }
private:
cudaStream_t s_{};
};析构函数不能安全抛异常;异步工作与资源析构的先后必须由所有权协议保证。高频 allocation 使用内存池,长期 buffer 使用显式生命周期,避免隐式 device-wide 同步。
推荐工程结构
cuda-project/
├─ CMakeLists.txt
├─ include/
│ ├─ cuda_check.hpp
│ └─ ops.hpp
├─ src/
│ ├─ ops.cu # dispatch + kernels
│ ├─ reference.cpp # CPU reference
│ └─ main.cpp
├─ tests/
│ ├─ correctness.cpp
│ └─ sanitizer_cases.cpp
├─ benchmarks/
│ └─ bench_ops.cpp
└─ scripts/
├─ profile_systems.*
└─ profile_compute.*每个算子至少有:
- 清晰的 shape/dtype/layout/aliasing 合同;
- CPU 或可信库 reference;
- 边界和随机 correctness test;
- 独立 benchmark;
- profile 入口;
- 支持架构与 fallback;
- 数值精度和确定性说明。
架构 Dispatch
不要在业务各处散落 if (cc >= ...)。集中注册:
op()
├─ capability / dtype / shape 检查
├─ Blackwell/Hopper specialized path(若编译且设备支持)
├─ Ampere+ async-copy path
├─ portable baseline CUDA path
└─ unsupported → 明确错误或 CPU/library fallback编译时 __CUDA_ARCH__ 只在 device 编译阶段定义;host dispatch 应读取 device attributes。a/f 架构目标的兼容范围更窄,应保留 baseline code object 或独立二进制策略。
高级能力地图
| 能力 | 什么时候需要 | 先掌握什么 |
|---|---|---|
| Driver API / NVRTC | 动态加载、运行时编译、插件 | Runtime、context/module 生命周期 |
| CUDA Graph | 重复任务图、短 kernel 密集 | stream/event 与资源生命周期 |
| Cooperative launch | grid 级同步 | occupancy、所有 block 同时驻留约束 |
| Dynamic Parallelism | device 侧动态启动 | launch 成本、嵌套同步;通常先考虑其他调度 |
| Virtual Memory Management | 大型地址空间、稀疏映射、共享 | allocation/VA/physical mapping |
| IPC | 跨进程共享 memory/event | context、安全与生命周期 |
| Cluster/DSM | Hopper+ block 集群协作 | block 模型、cluster occupancy |
| TMA | Hopper+ 多维异步搬运 | tiling、barrier、pipeline |
| Memory sync domains | Hopper+ 隔离本地与远程流量的 fence 干扰 | CUDA memory model、system scope |
高级特性不是学习清单上的勋章。只有当基础 profile 指向它解决的问题时再引入。
四个递进项目
项目 1:图像处理流水线
- RGB→Gray、Gaussian blur、Sobel;
- global-only 与 shared halo 对照;
- pinned memory + streams 重叠批量图像;
- CUDA Graph replay;
- 指标:吞吐、端到端延迟、有效带宽。
项目 2:并行 primitives
- reduce、scan、histogram、compact;
- 自写版对照 CUB;
- 测均匀/热点/边界输入;
- Sanitizer 与浮点误差报告。
项目 3:GEMM 与 fused epilogue
- naive → shared tile → register tile;
- 对照 cuBLAS;
- 用 cuBLASLt 实现 bias + activation;
- 解释 Roofline 位置和资源限制。
项目 4:多 GPU mini-system
- 每卡独立 stream 与 buffer;
- P2P 或 NCCL AllReduce;
- 计算/通信 overlap;
- Systems 时间线标注 NVTX;
- 报告 1/2/4/8 卡 scaling efficiency。
12 周学习计划
| 周 | 主题 | 交付物 |
|---|---|---|
| 1 | 工具链、vector add、错误检查 | 可编译工程 + device query |
| 2 | SIMT、索引、grid-stride | 一维/二维 kernel |
| 3 | 合并访存、布局、带宽 | copy/transpose benchmark |
| 4 | shared、bank、warp primitive | tiled transpose + block reduce |
| 5 | reduce/scan/histogram | 自写与 CUB 对照 |
| 6 | stream/event/pinned | 三阶段 pipeline 时间线 |
| 7 | Graph/async allocator | 重复工作流 benchmark |
| 8 | naive/tiled GEMM | correctness + GFLOP/s |
| 9 | register tile/Tensor Core 生态 | cuBLASLt/CUTLASS 对照 |
| 10 | Roofline/Nsight | 完整性能分析报告 |
| 11 | 多 GPU/NCCL | scaling 报告 |
| 12 | 综合项目 | 文档、测试、benchmark、profile 全套 |
速查:遇到问题先去哪一章
| 问题 | 章节 |
|---|---|
| 编译、架构目标、PTX/cubin | 01 |
| warp、SM、occupancy 基础 | 02 |
| index、block shape、warp primitive | 03 |
| coalescing、shared、managed/pinned | 04 |
| race、atomic、stream、event、graph | 05 |
| reduce/scan/histogram/stencil | 06 |
| GEMM、Tensor Core、Attention | 07 |
| Roofline、stall、调参 | 08 |
| Sanitizer、Nsight、数值验证 | 09 |
| CUDA-X、多 GPU、工程化 | 本章 |
| CUDA C++、低精度、内存模型 | 11 |
| CUDA Python、DLPack、跨语言 stream | 12 |
| CUDA Tile | 13 |
| Unified Memory、memory pool、VMM | 14 |
| Cluster、DSM、TMA、高级异步 | 15 |
| Driver API、NVRTC、部署兼容 | 16 |
| CUDA-X 库调用范式 | 17 |
| 图像、Monte Carlo、BFS、Attention 案例与术语 | 18 |
| MIG/MPS、Green Context、fabric、图形互操作 | 19 |