GPU 架构学习

第一课:GPU 为什么存在,以及它和 CPU 有什么区别

先从“GPU为什么存在”讲起,再逐步进入线程、SIMT、SM、Warp、存储层次和指令执行。先建立整体框架,不急着看具体英伟达架构。

目录一、计算机为什么需要 GPU二、CPU 和 GPU 的根本区别三、为什么 GPU 不直接使用大量 CPU 核心四、GPU 不是简单地堆很多“核心”五、先认识 GPU 的三个层次Thread:线程Block:线程块Grid:网格六、软件线程怎样映射到硬件七、Warp 是什么八、GPU 如何隐藏访存延迟九、目前需要记住的核心结论

导语:先从“GPU为什么存在”讲起,再逐步进入线程、SIMT、SM、Warp、存储层次和指令执行。先建立整体框架,不急着看具体英伟达架构。

可以把 GPU 架构的学习分成下面几层,由外到内逐渐深入:

  1. GPU 为什么存在,和 CPU 有什么区别
  2. Thread、Block、Grid 等编程概念
  3. SIMT、Warp 和线程束调度
  4. SM 的内部组成和执行过程
  5. 寄存器、共享内存、缓存、显存
  6. 分支发散、数据冒险和访存合并
  7. CUDA Core、Tensor Core、RT Core
  8. 多 SM、多 GPU 和 NVLink
  9. 从 RTL、ASIC 和微架构角度理解 GPU

今天先讲第一部分:GPU 到底是什么,以及它为什么比 CPU 更适合并行计算。


一、计算机为什么需要 GPU

假设需要计算两个长度为 100 万的数组:

C[i]=A[i]+B[i]C[i]=A[i]+B[i]

其中每一个元素的计算都互不影响:

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 都可以执行加法、乘法、访存和跳转,但设计目标不同。

对比项目CPUGPU
核心数量少量复杂核心大量相对简单的计算单元
优化目标单个任务尽快完成大量任务总体吞吐量高
控制逻辑很复杂相对简化
缓存很大相对较小
分支处理相对较弱
并行线程数量较少成千上万
典型任务操作系统、复杂程序图形、矩阵、神经网络

可以简单理解为:

CPU:
少数几个能力很强的工人
每个工人可以独立处理复杂任务

GPU:
大量擅长重复工作的工人
适合同时处理很多相似任务

CPU 关注的是低延迟

一个任务提交之后,尽快给出结果。

GPU 关注的是高吞吐率

单个任务不一定最快,但单位时间内完成的任务总量非常大。


三、为什么 GPU 不直接使用大量 CPU 核心

CPU 核心中有很多复杂硬件,例如:

  • 分支预测器;
  • 乱序执行单元;
  • 大容量缓存;
  • 复杂的指令调度逻辑;
  • 推测执行逻辑;
  • 精确异常处理机制。

这些结构可以提高单线程性能,但需要占用大量芯片面积和功耗。

假设一块芯片的面积固定:

三、为什么 GPU 不直接使用大量 CPU 核心

图示:三、为什么 GPU 不直接使用大量 CPU 核心

GPU 则减少单个执行单元的复杂度,把更多面积用于计算:

三、为什么 GPU 不直接使用大量 CPU 核心

图示:三、为什么 GPU 不直接使用大量 CPU 核心

因此,GPU 用较弱的单线程性能,换取了更高的总体并行吞吐率。


四、GPU 不是简单地堆很多“核心”

经常看到这样的描述:

某 GPU 有 10000 个 CUDA Core
某 CPU 有 16 个 CPU Core

这两个数字不能直接比较。

CPU Core 是一个完整、复杂、能够独立执行程序的处理器核心。

CUDA Core 更接近一个浮点或整数运算单元,也就是执行加法、乘法等运算的 ALU。它通常不能独立运行完整程序,需要由更上层的调度和控制硬件统一管理。

因此:

1 个 CPU Core ≠ 1 个 CUDA Core

更合理的层次关系是:

四、GPU 不是简单地堆很多“核心”

图示:四、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 需要非常多的线程,并不只是为了同时计算,也为了在部分线程等待数据时,让其他线程继续使用计算单元。


九、目前需要记住的核心结论

第一,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 从启动到执行完成的全过程。

第二十六课:如何从零设计一个简化的 SIMT GPU

这一课从 RTL 设计角度搭建一颗最小可运行的 SIMT GPU。重点是模块边界、状态机和实现顺序,而不是一开始复制完整商用 GPU。

第二十七课:GPU 架构学习总结与统一分析框架

最后一课将把前面所有内容压缩成一套统一分析框架,并给出面向 GPU 架构与 RTL 设计的后续实践路线。

第二十五课:稀疏计算、不规则访存,以及 GPU 不擅长什么

这一课是倒数第三课,重点解释“计算量更少”为什么不一定更快,以及不规则访存、原子冲突和负载不均衡为何会削弱 GPU 优势。