CS267 · HW2-3 导读

粒子模拟并行化(CUDA GPU)

将 CPU 上的粒子模拟搬到 GPU 上,体验 CUDA 数据并行编程模型——理解 SIMT 执行、内存层次与同步,并在 A100 上跑出 O(n) 的加速。

📅 截止:2025-03-23 🎓 对应:Lecture 9-10 💻 CUDA C++ 🖥 Perlmutter A100

这个 Homework 要你做什么?

一句话总结:同一个粒子模拟问题(HW2 的 OpenMP / MPI 版本),这次在 GPU 上并行化——你只能修改 gpu.cu 中的 simulate_one_step 函数,在 NVIDIA A100 GPU 上跑出 O(n) 的性能。

这是你第一次用 CUDA 写"真正"的并行程序。与 CPU 并行不同,GPU 编程需要你同时思考线程布局、内存访问模式和同步策略。

主要任务

任务对应课程核心概念
理解 GPU 硬件模型Lecture 9SM、Warp、SIMT、延迟隐藏
设计线程布局Lecture 9Grid / Block / Thread 映射
实现 simulate_one_stepLecture 10数据并行 kernel
优化内存访问Lecture 10合并访问、shared memory
性能评测与对比Lecture 10与 OpenMP / MPI 版本对比
撰写报告—log-log 图、扩展性分析

前置知识清单

课程知识

CUDA 编程基础


如何开始?

0

理解串行算法

回顾 HW2 的粒子模拟:每个时间步,每个粒子根据与其他粒子的相互作用更新位置和速度。核心循环是每个粒子遍历所有其他粒子——O(n²) 的朴素算法。理解这一步是设计 GPU kernel 的前提。

1

映射到 GPU 线程

最自然的映射是一个线程处理一个粒子:

// 全局线程 ID 计算
int i = threadIdx.x + blockIdx.x * blockDim.x;
if (i < n) {
  // 粒子 i 的更新逻辑
}

选择合适的 block size(通常 128 或 256 线程/block),计算所需的 block 数量。

2

管理内存

你需要:

3

优化访问模式

GPU 性能的关键是内存合并(coalesced access):

4

性能评测

用不同粒子数(100K - 10M)测试,绘制 log-log 图,与 HW2 的 OpenMP / MPI 版本对比。注意:小粒子数看不出差异,必须用大规模数据。


常见陷阱

忘记边界检查: 当 n 不是 blockDim 的整数倍时,最后一个 block 会有多余线程。必须用 if (i < n) 保护,否则会访问越界。

Host/Device 指针混用: 不能在 host 代码中解引用 device 指针,反之亦然。这是最常见的运行时错误。

分支发散: 如果 warp 内线程走不同分支(如某些粒子需要特殊处理),性能会大幅下降。尽量让 warp 内线程执行相同路径。

非合并访问: 如果线程 i 访问 data[i * stride] 且 stride > 1,会导致非合并访问,带宽利用率可能只有 1/32。

同步位置错误: __syncthreads() 必须在所有线程都能到达的位置调用。放在条件分支内可能导致死锁。


GPU 关键概念速览

线程层次映射

概念含义HW2-3 中的对应
Thread单个执行单元处理一个粒子的更新
Warp32 个线程的调度组锁步执行,注意分支发散
Block驻留在一个 SM 上的线程组共享 shared memory,可同步
Grid所有 block 的集合一次 kernel 启动的全部工作

内存层次选择

内存延迟用途
寄存器~1 cycle线程私有变量(循环计数器、累加器)
Shared Memory~5 cyclesBlock 内线程共享数据(tile 缓存)
Global Memory~400-800 cycles粒子位置/速度的主存储

性能分析方法

报告要求

关键指标


与课程的关系

Lecture 9 · CUDA 导论
↓
GPU 硬件模型 → 线程布局设计
Lecture 10 · 数据并行
↓
SIMT / 内存层次 → kernel 实现
HW2 · OpenMP / MPI
↓
串行算法 → GPU 并行版本对比
Lecture 3 · Roofline
↓
性能分析 → 瓶颈定位与优化

HW2-3 是 GPU 编程的入门体验。后续课程中你会见到更复杂的 GPU 应用:


学习心态

GPU 编程是一种思维转变。 在 CPU 上,你优化的是算法复杂度;在 GPU 上,你优化的是内存访问模式和并行效率。同一个算法,不同的实现方式,性能可能差 10 倍以上。

不要期望第一次就写出最优版本。先让它正确运行,再逐步优化——合并访问、shared memory、occupancy 调优。

卡住时的正确顺序: 复习 Lecture 9-10 → 读 handout → 从最小 kernel 开始 → 用 printf 调试 → 用 nsight compute 分析。


自测:你准备好了吗?

问题 1:GPU 中 warp 的大小是多少?当 warp 内线程走不同分支时会发生什么?

问题 2:为什么 GPU 编程强调"合并内存访问"(coalesced access)?

问题 3:__syncthreads() 的同步范围是什么?

问题 4:为什么 HW2-3 要求用 100K-10M 的大粒子数?


导读要点

目标

GPU 上实现 O(n) 粒子模拟

核心

线程布局 + 合并访问 + 同步

对比

与 OpenMP / MPI 版本性能对比

产出

log-log 图 + 设计解释 + 分析