GPU 执行模型 3:运算与内存子系统

首先我们对商品 GPU 种类、GPU 的整体结构、GPU 核心结构、SM 和 partition 结构进行简单介绍。然后我们我们详细介绍运算子系统和内存子系统。

背景知识

商品 GPU 种类

GPU 整体结构

包括 A40 在内的 PCIe GPU 由以下部分组成:

所以 GPU die 本身只是整张卡上最核心但面积占比并不大的一部分。大量的板面积留给了喂饱这颗 die 所需要的供电和显存。

GPU 核心结构

SM 和 Partition 结构

1 GA102 is composed of:

1 SM is composed of:

In compute mode, the GA10x SM will support the following configurations for L1 d-cache/Shared Memory:

运算子系统

GA102 的 partition 里,做运算的单元主要有四种:

除了 partition 内部的运算单元,每个 SM 还有:

写高性能 kernel 的时候理想状态是让这几种单元同时忙起来,不要让任何一种成为瓶颈。

背景知识

CUDA Core

Tensor Core:矩阵运算

Tensor core 的硬件实现

如何调用 tensor core

PTX 指令的矩阵大小支持

PTX 指令到 SASS 指令

我们只讨论 f16 MMA PTX 指令生成 sm_86 机器码,命令为 nvcc -arch=sm_86 -cubin -keep mma_f16.cu -o mma_f16.cubin。

对于 16x8x8 16x8x16 的 MMA PTX 指令,ptxas 会翻译成对应的 tensor core SASS 指令:

HMMA.16816.F16 R18, R4.reuse, R2.reuse, R8 ;  /* 0x000000020412723c */
                                              /* 0x0c4ff00000000808 */
HMMA.16816.F32 R20, R4, R2, R8 ;              /* 0x000000020414723c */
                                              /* 0x008fe20000001808 */
HMMA.1688.F16 R6, R4.reuse, R2.reuse, R8 ;    /* 0x000000020406723c */
                                              /* 0x0c0fe80000000008 */
HMMA.1688.F32 R12, R4, R2, R8 ;               /* 0x00000002040c723c */
                                              /* 0x000ff60000001008 */

对于 8x8x4 的 MMA PTX 指令,ptxas 会翻译成 CUDA core SASS 指令。

SASS 指令到 tensor core 的映射

只讨论 f16。8x8x4 这个大小忽略,这个形状是专门为 CC 7.0 设计的。这个大小在 CC 8.6 上可以跑,但是只会跑在 CUDA core 上。

f16 剩下的矩阵大小为 8x32x16 16x8x8 16x8x16 16x16x16 32x8x16,挑一个分析一下。其实主要问题是,一次只能计算小矩阵,大矩阵的计算 C = A x B 怎么分解成小矩阵的计算。

注意,一个 SASS 指令从进入 tensor core 流水线到算出最后所有结果,除了上面算出来的周期数之外,还要加上 tensor core 本身的流水线级数。

Special Function Unit

专门做超越函数运算的,比如 sin、cos、exp、log、rsqrt 这些。这些函数如果用 ALU 去算需要很多个时钟周期做迭代,SFU 用硬件查找表加插值的方式几个周期就能出结果。

你写 CUDA 时调用 __expf() 或 __sinf() 这些 intrinsic 走的就是 SFU。

对 LLM 来说,GELU 和 Softmax 里的 exp 运算就会用到 SFU。

Load/Store Unit

ldst unit 严格说不算运算单元,它只负责地址计算和内存访问的执行,

kernel 每次读写 global memory、shared memory 都要经过这些单元。它们的数量直接影响 memory throughput。

参考资料

@INPROCEEDINGS{raihan,
  author={Raihan, Md Aamir and Goli, Negar and Aamodt, Tor M.},
  booktitle={2019 IEEE International Symposium on Performance Analysis of Systems and Software (ISPASS)},
  title={Modeling Deep Learning Accelerator Enabled GPUs},
  year={2019},
  volume={},
  number={},
  pages={79-92},
  keywords={Graphics processing units;Registers;Instruction sets;Computer architecture;Hidden Markov models;Deep learning;Tensor Core;Tesla Titan V;Turing RTX 2080;CUTLASS library;GPGPU-Sim},
  doi={10.1109/ISPASS.2019.00016}}

@misc{jiavolta,
      title={Dissecting the NVIDIA Volta GPU Architecture via Microbenchmarking},
      author={Zhe Jia and Marco Maggioni and Benjamin Staiger and Daniele P. Scarpazza},
      year={2018},
      eprint={1804.06826},
      archivePrefix={arXiv},
      primaryClass={cs.DC},
      url={https://arxiv.org/abs/1804.06826},
}

@misc{jiaturing,
      title={Dissecting the NVidia Turing T4 GPU via Microbenchmarking},
      author={Zhe Jia and Marco Maggioni and Jeffrey Smith and Daniele Paolo Scarpazza},
      year={2019},
      eprint={1903.07486},
      archivePrefix={arXiv},
      primaryClass={cs.DC},
      url={https://arxiv.org/abs/1903.07486},
}

@misc{ampere,
      title={Demystifying the Nvidia Ampere Architecture through Microbenchmarking and Instruction-level Analysis},
      author={Hamdy Abdelkhalik and Yehia Arafa and Nandakishore Santhi and Abdel-Hameed Badawy},
      year={2022},
      eprint={2208.11174},
      archivePrefix={arXiv},
      primaryClass={cs.AR},
      url={https://arxiv.org/abs/2208.11174},
}

内存子系统

A40 Memory Hierarchy

LDST Unit

当一条 LDG 或 STS 指令带着 32 个线程的地址进入 LD/ST 单元时,它主要负责两件极其核心的工作:

完成这两步之后,LD/ST 单元的工作就彻底结束了!它会立刻拍拍屁股走人,清空内部的流水线寄存器,准备迎接下一条访存指令。

SM L1 Cache

合并后的请求,第一站就是当前 SM 私有的 L1 Cache。在现代 NVIDIA 架构中,这个部件叫 L1TEX Cache(统一的 L1 数据缓存和纹理缓存)。

(注:Shared Memory 也在这一层,物理上与 L1 共享同一块 SRAM,但 LDG/STG 是针对全局内存的指令,所以只走 L1 路径,完全不经过 Shared Memory)。

Crossbar

芯片内部有一张庞大且极速的交叉开关网络,称为 Crossbar (全互连网络)。

由于 GPU 上有几十上百个 SM,而 L2 Cache 在物理上是被切分成 12 个 Slice (切片) 的。Crossbar 负责根据内存地址的哈希值,把 SM 吐出来的请求,精准路由到对应的那个 L2 Cache 切片上。

Device L2 Cache

请求抵达对应的 L2 切片。

L2 Cache 是全局共享的,这意味着哪怕是不同的 SM 上的线程,只要请求同一个地址,也能在 L2 中实现数据的共享。

全局内存的原子操作(Atomic Operations,如 atomicAdd)通常也是在 L2 这一层由专门的 ALU 直接完成的,这样可以避免数据在不同的 L1 之间来回颠簸(Cache Coherence 开销)。

命中 (Hit):沿着 Crossbar 原路返回 L1 -> 寄存器。

缺失 (Miss):向显存发起最终请求。

DRAM

带宽