GPU 架构学习
第一课:GPU 为什么存在,以及它和 CPU 有什么区别
先从“GPU为什么存在”讲起,再逐步进入线程、SIMT、SM、Warp、存储层次和指令执行。先建立整体框架,不急着看具体英伟达架构。
导语:先从“GPU为什么存在”讲起,再逐步进入线程、SIMT、SM、Warp、存储层次和指令执行。先建立整体框架,不急着看具体英伟达架构。
可以把 GPU 架构的学习分成下面几层,由外到内逐渐深入:
- GPU 为什么存在,和 CPU 有什么区别
- Thread、Block、Grid 等编程概念
- SIMT、Warp 和线程束调度
- SM 的内部组成和执行过程
- 寄存器、共享内存、缓存、显存
- 分支发散、数据冒险和访存合并
- CUDA Core、Tensor Core、RT Core
- 多 SM、多 GPU 和 NVLink
- 从 RTL、ASIC 和微架构角度理解 GPU
今天先讲第一部分:GPU 到底是什么,以及它为什么比 CPU 更适合并行计算。
一、计算机为什么需要 GPU
假设需要计算两个长度为 100 万的数组:
其中每一个元素的计算都互不影响:
C[0] = A[0] + B[0]
C[1] = A[1] + B[1]
C[2] = A[2] + B[2]
...
这些加法不需要按照顺序执行。
理论上,如果有 100 万个计算单元,就可以同时完成 100 万次加法。GPU 正是为了处理这种具有大量独立计算的数据而设计的。
常见的 GPU 工作负载包括:
- 图像中大量像素的处理;
- 神经网络中的矩阵乘法;
- 三维图形中的大量顶点和像素计算;
- 科学计算中的向量、矩阵和网格运算。
它们的共同特点是:
同一种运算,需要对大量不同数据重复执行。
这种特征称为数据并行性。
二、CPU 和 GPU 的根本区别
CPU 和 GPU 都可以执行加法、乘法、访存和跳转,但设计目标不同。
| 对比项目 | CPU | GPU |
|---|---|---|
| 核心数量 | 少量复杂核心 | 大量相对简单的计算单元 |
| 优化目标 | 单个任务尽快完成 | 大量任务总体吞吐量高 |
| 控制逻辑 | 很复杂 | 相对简化 |
| 缓存 | 很大 | 相对较小 |
| 分支处理 | 强 | 相对较弱 |
| 并行线程数量 | 较少 | 成千上万 |
| 典型任务 | 操作系统、复杂程序 | 图形、矩阵、神经网络 |
可以简单理解为:
CPU:
少数几个能力很强的工人
每个工人可以独立处理复杂任务
GPU:
大量擅长重复工作的工人
适合同时处理很多相似任务
CPU 关注的是低延迟:
一个任务提交之后,尽快给出结果。
GPU 关注的是高吞吐率:
单个任务不一定最快,但单位时间内完成的任务总量非常大。
三、为什么 GPU 不直接使用大量 CPU 核心
CPU 核心中有很多复杂硬件,例如:
- 分支预测器;
- 乱序执行单元;
- 大容量缓存;
- 复杂的指令调度逻辑;
- 推测执行逻辑;
- 精确异常处理机制。
这些结构可以提高单线程性能,但需要占用大量芯片面积和功耗。
假设一块芯片的面积固定:

图示:三、为什么 GPU 不直接使用大量 CPU 核心
GPU 则减少单个执行单元的复杂度,把更多面积用于计算:

图示:三、为什么 GPU 不直接使用大量 CPU 核心
因此,GPU 用较弱的单线程性能,换取了更高的总体并行吞吐率。
四、GPU 不是简单地堆很多“核心”
经常看到这样的描述:
某 GPU 有 10000 个 CUDA Core
某 CPU 有 16 个 CPU Core
这两个数字不能直接比较。
CPU Core 是一个完整、复杂、能够独立执行程序的处理器核心。
CUDA Core 更接近一个浮点或整数运算单元,也就是执行加法、乘法等运算的 ALU。它通常不能独立运行完整程序,需要由更上层的调度和控制硬件统一管理。
因此:
1 个 CPU Core ≠ 1 个 CUDA Core
更合理的层次关系是:

图示:四、GPU 不是简单地堆很多“核心”
在 NVIDIA GPU 中,真正接近“核心处理器”的结构是 SM,Streaming Multiprocessor,流式多处理器,而不是单个 CUDA Core。
五、先认识 GPU 的三个层次
从软件角度,一个 GPU 程序通常分为:
Grid
└── Block
└── Thread
Thread:线程
Thread 是程序员看到的最小执行单位。
例如计算数组加法时,可以让每个线程处理一个元素:
C[i] = A[i] + B[i];
线程 0 计算第 0 个元素,线程 1 计算第 1 个元素,以此类推。
Block:线程块
多个线程组成一个 Block。
例如:
Block 0:Thread 0 ~ Thread 255
Block 1:Thread 256 ~ Thread 511
Block 2:Thread 512 ~ Thread 767
同一个 Block 中的线程可以:
- 使用共享内存交换数据;
- 执行线程同步;
- 共同完成一个子任务。
Grid:网格
多个 Block 组成一个 Grid。
一次 GPU Kernel 启动通常会产生一个 Grid。
Grid
├── Block 0
├── Block 1
├── Block 2
└── Block 3
六、软件线程怎样映射到硬件
GPU 硬件中有多个 SM。
GPU
├── SM 0
├── SM 1
├── SM 2
└── SM 3
Block 会被分配到 SM 上执行:
Block 0 → SM 0
Block 1 → SM 1
Block 2 → SM 2
Block 3 → SM 3
如果 Block 数量多于 SM 数量,后面的 Block 会等待前面的 Block 执行结束。
例如有 4 个 SM,但有 100 个 Block:
第一轮:Block 0~3
第二轮:Block 4~7
第三轮:Block 8~11
...
一个 Block 一旦被分配给某个 SM,通常会在该 SM 上执行到结束,不会执行到一半迁移到另一个 SM。
七、Warp 是什么
虽然程序员创建的是 Thread,但 GPU 通常不会单独调度每一个线程。
NVIDIA GPU 会把若干线程组成一个 Warp。一个 Warp 通常包含 32 个线程:
Warp 0:Thread 0 ~ Thread 31
Warp 1:Thread 32 ~ Thread 63
Warp 2:Thread 64 ~ Thread 95
Warp 中的线程通常执行同一条指令,但处理不同的数据:
Thread 0:C[0] = A[0] + B[0]
Thread 1:C[1] = A[1] + B[1]
Thread 2:C[2] = A[2] + B[2]
...
Thread 31:C[31] = A[31] + B[31]
可以看成:
同一条指令:执行加法
不同的数据:A[0]~A[31]、B[0]~B[31]
这种执行方式称为 SIMT:
Single Instruction, Multiple Threads
单指令、多线程
它与 SIMD 很相似,但程序员看到的是多个独立线程,而硬件内部把这些线程组合成 Warp 执行。
八、GPU 如何隐藏访存延迟
显存访问可能需要几百个时钟周期。如果 CPU 遇到这样的延迟,通常依靠缓存、乱序执行等机制缓解。
GPU 使用另一种方法:同时保存大量 Warp,快速切换执行。
例如:
Warp 0:正在等待显存
Warp 1:可以执行计算
Warp 2:正在等待数据
Warp 3:可以执行计算
调度器会暂时跳过等待数据的 Warp,执行其他已经准备好的 Warp:

图示:八、GPU 如何隐藏访存延迟
这称为延迟隐藏。
所以 GPU 需要非常多的线程,并不只是为了同时计算,也为了在部分线程等待数据时,让其他线程继续使用计算单元。
九、目前需要记住的核心结论
第一,GPU 适合具有大量数据并行性的任务。
第二,CPU 优化单线程延迟,GPU 优化总体吞吐率。
第三,CUDA Core 只是运算单元,SM 才是更完整的硬件执行组织。
第四,软件执行层次是:
Grid → Block → Thread
第五,硬件执行层次可以暂时理解为:
GPU → SM → Warp → Thread
第六,在 NVIDIA GPU 中,32 个线程通常组成一个 Warp。
第七,GPU 通过同时驻留大量 Warp,在一个 Warp 等待访存时执行其他 Warp,从而隐藏延迟。
下一步应重点学习:
Thread、Block、Grid、Warp 和 SM 之间究竟如何对应,以及一个 CUDA Kernel 从启动到执行完成的全过程。