并行计算实践路线:从单核优化到多机多卡 / A Parallel Computing Project Path from Single-Core to Multi-Node GPUs
📅 创建时间:2026-07-20 🏷️ 标签:#学习路线 #并行项目 #CUDA实践 #MPI实践 📚 前置知识:[[11-application-cases]]
1. 学习目标
并行计算不能只靠阅读。建议为每一层建立“串行基线、并行版本、性能证据和正确性测试”。
最终应该能够独立完成:
- 设计可重复的 Benchmark
- 使用多线程和 SIMD 优化 CPU 程序
- 编写并分析 CUDA Kernel
- 用 MPI 把问题扩展到多个进程
- 解释性能没有线性扩展的原因
- 输出包含环境、数据和结论的性能报告
2. 实验一:内存访问与 Cache
内容
- 比较连续访问、跨步访问和随机访问
- 比较行优先与列优先矩阵遍历
- 改变数组规模,观察超过不同 Cache 容量后的变化
记录
- 每秒处理字节数
- Cache Miss
- 不同步长的耗时曲线
目标:建立“计算量相同,数据访问不同,性能可以差很多”的直觉。
3. 实验二:CPU 多线程归约
实现数组求和:
- 串行版本
- 每次加法使用全局锁的错误并行版本
- 每线程局部求和、最后归约
- OpenMP Reduction
比较 1、2、4、8、16 个线程的加速比和效率,并解释锁竞争与内存带宽上限。
4. 实验三:SIMD 与编译器向量化
- 编译同一循环的 Debug 和 Release 版本
- 查看编译器向量化报告
- 添加循环依赖让向量化失败
- 调整数据对齐和数据布局
- 比较标量、自动向量化和手写 Intrinsics
重点不是追求某个数字,而是能用报告证明编译器做了什么。
5. 实验四:CUDA 向量与矩阵运算
第一阶段
- Vector Add
- 边界判断
- CUDA 错误检查
- 使用 Event 测量 Kernel 时间
第二阶段
- 朴素矩阵乘法
- Shared Memory 分块
- 不同 Block 尺寸
- 与 cuBLAS 比较
目标:理解自写 Kernel 与成熟库之间的差距来自哪些优化。
6. 实验五:并行算法原语
依次实现:
- Map
- Reduce
- Histogram
- Prefix Scan
- Stream Compaction
重点观察同步范围、原子操作、共享内存和多 Kernel 分阶段执行。
7. 实验六:CPU-GPU 流水线
构建批量图像处理程序:
CPU 读取 → CPU 解码 → GPU 滤波 → CPU 编码逐步加入:
- Pinned Memory
- 异步拷贝
- 两个或多个 Stream
- 双缓冲
- 批处理
用时间线证明计算与传输是否真正重叠。
8. 实验七:MPI 网格计算
实现二维热传导或简单 Jacobi 迭代:
- 按行划分网格
- 相邻 Rank 交换边界
- 从阻塞通信改为非阻塞通信
- 重叠内部区域计算与 Halo Exchange
- 测试强扩展和弱扩展
记录每个 Rank 的计算、通信和等待时间。
9. 综合项目建议
项目 A:异构图像处理引擎
关键词:线程池、SIMD、CUDA、流水线、性能面板。
项目 B:并行有限差分求解器
关键词:Stencil、OpenMP、CUDA、MPI、Halo Exchange、VTK 输出。
项目 C:小型分布式训练实验
关键词:PyTorch DDP、NCCL、数据并行、梯度同步、Profiler。
项目 D:并行性能教学可视化
关键词:Amdahl、Roofline、Cache、Warp 发散、通信拓扑。
10. 每个项目的交付物
README
├─ 问题定义
├─ 硬件与软件环境
├─ 串行基线
├─ 并行设计
├─ 正确性验证
├─ Benchmark 方法
├─ 性能图表
├─ 瓶颈分析
└─ 已知限制与下一步没有正确性测试的加速结果没有意义,没有测量方法的性能数字也无法复现。
11. 推荐工具链
| 层级 | 工具 |
|---|---|
| C++ 构建 | CMake、GCC/Clang/MSVC |
| CPU 并行 | std::thread、OpenMP、oneTBB |
| CPU 分析 | perf、VTune、火焰图 |
| GPU 编程 | CUDA、cuBLAS、Thrust |
| GPU 分析 | Nsight Systems、Nsight Compute |
| 分布式 | Open MPI / MPICH、NCCL |
| 数值验证 | Python、NumPy、单元测试 |
| 可视化 | Python、Matplotlib、VTK |
历史项目实践补充
本页保留合并前的实验清单。
当前项目指南增加仓库结构、CI、失败注入和报告规范。
串行基线
先实现清晰且正确的串行版本。
它用于 Oracle、性能基线和调试。
输入覆盖
- 空;
- 小;
- 典型;
- 大;
- 非对齐;
- 倾斜;
- 错误;
- 资源边界。
正确性
使用精确答案、容差和不变量。
并行版本重复运行,发现数据竞争和非确定性。
基准
保存:
- Commit;
- 编译器;
- 硬件;
- 输入;
- 并发资源;
- 预热;
- 样本;
- 原始结果;
- 正确性。
Profile
CPU 使用 WPA、VTune 或 Perf。
NVIDIA GPU 使用 Nsight Systems 和 Nsight Compute。
先系统级定位,再对热点下钻。
内存实验
比较:
- 顺序和随机;
- Stride;
- AoS/SoA;
- 工作集;
- False Sharing;
- NUMA。
CPU 实验
比较:
- 串行;
- SIMD;
- 静态线程;
- 动态调度;
- 不同线程数;
- 亲和性。
CUDA 实验
覆盖:
- 索引边界;
- Block;
- 合并访问;
- Shared Memory;
- Stream;
- 传输;
- 错误检查。
MPI 实验
覆盖:
- 分区;
- Point-to-Point;
- Collective;
- Halo;
- 强扩展;
- 弱扩展;
- 最慢 Rank。
失败注入
- OOM;
- 文件失败;
- 无设备;
- 非法参数;
- 取消;
- 超时;
- Rank 故障;
- 部分输出。
仓库交付
包含:
- README;
- 构建;
- 源码;
- 测试;
- Benchmark;
- Profile 脚本;
- 结果索引;
- 报告。
当前课程映射
当前版本增加:
- 标准目录结构;
- 输入矩阵;
- Correctness Gate;
- CI;
- 可复现脚本;
- 扩展曲线;
- 失败注入;
- 报告模板。
归档复现
旧命令和工具版本需要在目标环境重新确认。
复现结果记录与历史差异,不覆盖原始结论。
每次项目复现还应保存数据校验和、构建产物哈希和完整运行日志。 无法获得原硬件时,报告说明替代平台及其影响。 性能差异不得在没有正确性对照时解释为优化收益。 旧结果只作为参考,不自动升级为当前回归基线。 项目报告应列出尚未验证的假设。 完成标准以可复现证据为准。
12. 完成专题后应该能回答
- 为什么增加核心后加速比会逐渐下降?
- 为什么连续访问比随机访问快?
- CPU SIMD 和 GPU SIMT 有什么区别?
- 为什么高 Occupancy 不保证高性能?
- 什么时候程序受内存带宽限制?
- CPU-GPU 传输为什么可能抵消加速收益?
- AllReduce 为什么会限制多机训练扩展?
- 怎样用证据说明一个优化确实有效?
如果能结合自己的实验回答这些问题,就已经建立了较完整的并行计算知识框架。
返回专题首页:[[00-parallel-computing-overview]]