03 > 计算能力

计算能力是一代 GPU 的版本号。第 00 课到第 02 课里见过的功能和硬件上限都由它规定,比如线程束大小是 32,每个线程块最多 1024 个线程。每个 CUDA 功能都要求一个最低的计算能力,所以看这个数字就知道你的代码能用哪些功能。

这个数字的含义

它的格式是 major.minor,比如 Hopper 是 9.0,这些课使用的 L40S 是 8.9。主版本号变了,表示新一代架构和新的硬件。次版本号变了,表示同一代架构里的一次修订。

为 CC 7.0 编译的代码,可以在任何 CC 7.0 及以上的 GPU 上运行。用到 CC 9.0 功能的代码,则不能在更旧的 GPU 上运行。比如要求 CC 8.0 的程序可以在 L40S 上运行(8.9 高于 8.0),要求 CC 9.0 的程序就不行。

[!TIP] 想知道你的 GPU 的计算能力,可以运行 nvidia-smi --query-gpu=name,compute_cap --format=csv。

GPU 的各代产品

下表列出了从 Pascal 到 Blackwell 的数据中心 GPU,另外加上这些课的输出所用的 L40S。

规格 P100 (CC 6.0) V100 (CC 7.0) A100 (CC 8.0) L40S (CC 8.9) H100 (CC 9.0) B100 (CC 10.0)
GPU Tesla P100 Tesla V100 A100 L40S H100 B100
代号 GP100 GV100 GA100 AD102 GH100 GB100
架构 Pascal Volta Ampere Ada Lovelace Hopper Blackwell
每个线程束的线程数 32 32 32 32 32 32
每个 SM 最多线程束数 64 64 64 48 64 64
每个 SM 最多线程数 2048 2048 2048 1536 2048 2048
每个 SM 最多线程块数 32 32 32 24 32 32
每个 SM 最多寄存器数 65536 65536 65536 65536 65536 65536
每个线程块最多寄存器数 65536 65536 65536 65536 65536 65536
每个线程最多寄存器数 255 255 255 255 255 255
线程块最大线程数 1024 1024 1024 1024 1024 1024
每个 SM 的 FP32 核心数 64 64 64 128 128 128
每个 SM 的共享内存 64 KB 最多 96 KB 最多 164 KB 最多 100 KB 最多 228 KB 最多 228 KB

SM 指 Streaming Multiprocessor,也就是 GPU 内部运行线程块的处理器。FP32 指 32 位浮点数,KB 指千字节。

H100 和 B100 每个 SM 的线程上限和内存上限完全相同。每个 SM 的寄存器数在这几代之间一直没变。

[!NOTE] Blackwell 仍然比 Hopper 快,原因在于更多的 SM(B200 有 148 个,H100 SXM5 有 132 个)、第五代 Tensor Core、更快的 HBM3e,以及 NVIDIA 用来连接多块 GPU 的 NVLink 5.0。

每个线程束的线程数

线程束是 GPU 一起运行的 32 个线程(第 01 课)。32 这个数由硬件固定,是计算能力规范的一部分。GPU 从不调度单个线程,而是始终以 32 个线程组成的完整线程束为单位来调度。

从最早支持 CUDA 的 GPU(CC 1.0)开始,线程束大小就一直是 32。

每个 SM 的线程束数和线程数

SM 是运行线程块的物理处理器(第 02 课)。表中的数据中心 GPU 每个 SM 最多容纳 64 个活跃线程束,也就是 64 x 32 = 2048 个线程。L40S 则不同,CC 8.9 每个 SM 只允许 48 个线程束,也就是 48 x 32 = 1536 个线程。

一些线程束在等待内存时,线程束调度器可以挑别的线程束来运行。活跃线程束越多,越容易找到一个已经准备就绪的线程束,执行单元也就越忙。

[!WARNING] “每个 SM 64 个线程束”并不适用于所有 GPU,CC 8.6、8.9 和 12.0 只允许 48 个。按这个数做规划之前,先查一下你自己 GPU 的数值。

线程块大小上限

在第 02 课里,<<<1, 2048>>> 能通过编译,却什么都没启动,原因就是一个线程块最多只能有 1024 个线程。这是计算能力规范里的硬性规则,表中每一列都是 1024。

这个上限针对的是每个线程块,而不是每个 SM。只要线程来自多个线程块,一个 SM 能容纳的线程数就可以超过单个线程块的上限。在 L40S 上,一个 SM 能放下 1536 个线程,比如 3 个线程块、每块 512 个线程。在 A100 上能放下 2048 个线程,比如 2 个线程块、每块 1024 个线程。

每个 SM 的 FP32 核心

FP32 就是常用的 float 类型。Pascal、Volta 和 Ampere 数据中心 GPU 每个 SM 有 64 个 FP32 核心,Ada Lovelace(L40S)、Hopper 和 Blackwell 有 128 个。FP32 核心越多,每个 SM 在一个时钟周期里能完成的浮点运算就越多。

每个 SM 的共享内存

共享内存是每个 SM 内部的高速内存,同一个线程块里的所有线程都能使用。它的容量一代比一代大。

  • Pascal 上是 64 KB
  • Volta 上最多 96 KB
  • Ampere(A100)上最多 164 KB
  • Ada Lovelace(L40S)上最多 100 KB
  • Hopper 和 Blackwell 上最多 228 KB

共享内存越大,核函数就能把越多的数据留在芯片上,不必去访问全局内存。

术语表

  • 计算能力(compute capability,CC):一个版本号(major.minor),说明一块 GPU 支持哪些 CUDA 功能、硬件上限是多少。
  • SM(Streaming Multiprocessor,流式多处理器):GPU 内部的物理处理器,所有线程都在 SM 上运行。
  • 线程束(warp):GPU 一起调度、一起运行的 32 个线程。
  • 活跃线程束(active warps per SM):一个 SM 能同时容纳的线程束数。大多数数据中心 GPU 是 64,CC 8.6、8.9 和 12.0 是 48。
  • FP32(32-bit floating point,32 位浮点)核心:每个时钟周期完成一次 32 位浮点运算的硬件单元。
  • 共享内存(shared memory):每个 SM 内部的高速片上内存,由同一个线程块里的所有线程共享,比全局内存(设备端内存)快得多。
  • 寄存器(register):SM 寄存器堆里的一个高速存储位置,存放线程的一个局部变量。一个 SM 有 65536 个 32 位寄存器,一个线程最多能用 255 个。
  • KB(kilobyte,千字节):1024 字节。
  • HBM3e(High Bandwidth Memory 3e,高带宽内存):新一代的 HBM,也就是 B200 这类数据中心 GPU 使用的高速堆叠式显存。
  • 主版本号(major version):CC 的第一个数,比如 8.9 里的 8。主版本号变了,表示新一代架构。
  • 次版本号(minor version):CC 的第二个数,比如 8.9 里的 9,表示同一代架构里的一次修订。
  • 架构(architecture):一代 GPU 的设计。NVIDIA 用科学家的名字给它们命名,比如 Pascal、Volta、Ampere、Ada Lovelace、Hopper、Blackwell。
  • nvidia-smi:随驱动程序一起安装的 NVIDIA 命令行工具。它会列出你的 GPU,加上 --query-gpu=compute_cap 还会打印出它们的计算能力。
  • 线程束大小(warp size):一个线程束里的线程数,到目前为止每款 NVIDIA GPU 上都是 32。核函数可以通过 warpSize 读到它。
  • 线程束调度器(warp scheduler):每个 SM 里负责挑出一个就绪的线程束来运行的单元。一个线程束在等待内存时,它就换另一个线程束上来。
  • 线程块大小(thread block size):一个线程块里的线程数,也就是 <<<blocks, threads>>> 里的第二个数。表中每款 GPU 上最多都是 1024。
  • 时钟周期(clock cycle):处理器时钟的一次跳动。2 GHz 的时钟每秒有 20 亿个周期。
  • 全局内存(global memory):GPU 上容量很大的主显存,L40S 上是 GDDR6,H100 上是 HBM。每个线程都能访问它,但它比共享内存慢得多。
  • GPU(Graphics Processing Unit,图形处理器):拥有成千上万个小核心、用来运行核函数的处理器。
  • CUDA(Compute Unified Device Architecture,统一计算设备架构):NVIDIA 的平台,让你在 GPU 上运行自己的代码。