GPU基础:GPU架构演进、CUDA核心、显存层次与计算特性
在AI算力集群的语境下,GPU早已不是单纯的图形加速卡,而是承载大规模并行计算的核心引擎。理解GPU的架构演进、计算单元组织方式以及显存层次,是后续理解集群互联、资源调度和性能调优的基石。本章聚焦于GPU的硬件本质,厘清“为什么GPU适合AI计算”以及“它的性能边界在哪里”。
2.1 架构演进:从图形流水线到通用并行计算
GPU的架构演进可以概括为一条从“专用图形硬件”向“通用并行处理器”转变的路径。早期GPU(如NVIDIA GeForce 2/4时代)采用固定功能流水线,顶点变换、像素填充等操作由专用电路完成,程序员无法灵活控制。2006年,NVIDIA推出GeForce 8800(G80架构),首次引入统一的着色器模型,将顶点和像素处理统一到同一组流处理器上,并开放了CUDA(Compute Unified Device Architecture)编程接口,标志着GPU正式进入通用计算时代。
此后,架构演进围绕三个核心目标展开:提高并行吞吐量、增强通用计算能力、优化数据复用效率。以NVIDIA的架构代际为例:
| 架构代号 | 发布年份 | 关键特性 | 对AI计算的意义 |
|---|---|---|---|
| Fermi | 2010 | 引入真正可用的L2缓存、ECC内存支持、双精度浮点单元 | 首次具备科学计算所需的可靠性 |
| Kepler | 2012 | SM(Streaming Multiprocessor)内引入寄存器文件分区、动态并行 | 降低内核启动开销,支持更细粒度任务 |
| Volta | 2017 | 引入Tensor Core,独立于CUDA Core的专用矩阵乘加单元 | 深度学习训练吞吐量提升一个数量级 |
| Ampere | 2020 | 第三代Tensor Core支持TF32、BF16精度,MIG(多实例GPU) | 混合精度训练成为主流,GPU可分区隔离 |
| Hopper | 2022 | Transformer Engine、DPX指令、NVLink 4.0 | 针对Transformer模型深度优化,集群扩展能力增强 |
从架构演进可以看出,GPU的每一次代际更新都在强化其作为“并行计算引擎”的定位,而AI计算(尤其是矩阵乘法)逐渐成为架构设计的首要考量。
2.2 CUDA核心与线程组织模型
CUDA核心是GPU中最基本的整数/浮点运算单元,但它的工作方式与CPU核心有本质区别。CPU核心是“大而强”,每个核心拥有独立的控制单元、大容量缓存和分支预测器,适合执行复杂逻辑;而CUDA核心是“小而众”,它们被组织在SM(Streaming Multiprocessor)中,共享控制逻辑和缓存,以SIMT(Single Instruction, Multiple Thread)模式执行。
一个典型的SM包含以下关键组件:
- CUDA Core:执行整数和单精度浮点运算,数量通常为64~128个。
- Tensor Core:执行矩阵乘加运算(D = A × B + C),支持FP16、BF16、TF32、FP8等精度。
- 共享内存(Shared Memory):SM内部的高速可编程缓存,容量通常为64~228KB。
- 寄存器文件(Register File):每个SM拥有256KB左右的寄存器,分配给线程使用。
- 调度器(Warp Scheduler):负责将线程束(Warp,通常32个线程)分发到执行单元。
CUDA的线程组织模型是理解GPU编程的关键。线程从低到高分为三层:
Thread(线程)→ Warp(线程束,32个线程)→ Block(线程块)→ Grid(网格)
GPU以Warp为基本调度单位,一个Warp中的32个线程在同一时刻执行同一条指令(SIMT模式)。如果线程间出现分支分歧(即部分线程走if分支,部分走else分支),则两个分支会被串行执行,导致性能下降。因此,避免Warp内分支分歧是GPU编程的重要优化原则。
以NVIDIA A100为例,其关键计算参数如下:
SM数量:108
每个SM的CUDA Core:64
每个SM的Tensor Core:4(第三代)
每个SM最大线程数:2048
每个SM最大线程块数:32
全局显存带宽:1.6 TB/s(HBM2e)
L2缓存:40 MB
从软件视角看,程序员通过CUDA或OpenCL定义内核函数(Kernel),并指定Grid和Block的维度。硬件调度器负责将Block分配到SM上执行,Block内的线程通过共享内存和同步原语(__syncthreads)协作。这种“软件定义并行度,硬件自动调度”的模式,使得GPU能够自适应不同规模的并行任务。
2.3 显存层次:从寄存器到全局内存
GPU的显存层次与CPU的缓存层次类似,但设计目标不同:GPU更强调带宽而非延迟。因为GPU通过大量并行线程隐藏内存访问延迟——当一个Warp等待数据时,调度器立即切换到另一个Warp执行,从而保持计算单元忙碌。
完整的显存层次从快到慢、从近到远排列如下:
- 寄存器文件(Register File):每个SM内部,访问延迟约1个时钟周期,容量有限(每个线程最多255个寄存器)。
- 共享内存(Shared Memory):SM内部可编程缓存,延迟约20~30个时钟周期,容量为64~228KB。用于Block内线程间数据共享。
- L1缓存:与共享内存共用物理存储,用于缓存全局内存访问,延迟约30~40个时钟周期。
- L2缓存:所有SM共享,容量为40~50MB,延迟约200~300个时钟周期。用于跨SM的数据复用。
- 全局内存(Global Memory):即显存(HBM2e/HBM3),容量为40~80GB,延迟约400~600个时钟周期,带宽极高(1.6~3.35 TB/s)。
理解显存层次对AI推理和训练的性能优化至关重要。以矩阵乘法为例,如果每次从全局内存读取一个矩阵元素,带宽将成为瓶颈;而如果利用共享内存将数据分块(Tiling)加载,并在Block内复用,则可以将全局内存访问次数降低一个数量级。这也是cuBLAS等高性能库的核心优化手段。
2.4 计算特性:为什么GPU适合AI负载
AI计算(尤其是深度学习的训练和推理)具有三个显著特征:高并行度、高容错性、高计算密度。GPU的架构恰好与这些特征匹配。
首先,神经网络中的矩阵乘法、卷积运算天然具有数据并行性——每个输出元素的计算相互独立,可以分配到不同线程执行。GPU拥有数千个CUDA Core和Tensor Core,能够同时执行数万个线程,远超CPU的几十个核心。
其次,AI计算对精度要求相对宽松。训练过程中,FP32(单精度)甚至BF16(半精度)已经足够,推理阶段更可以使用INT8量化。GPU的Tensor Core专门针对低精度矩阵运算设计,其吞吐量远高于FP32的CUDA Core。以A100为例,其FP32算力为19.5 TFLOPS,而Tensor Core的FP16算力达到312 TFLOPS,相差16倍。
最后,GPU的显存带宽远高于CPU内存带宽。A100的HBM2e显存带宽为1.6 TB/s,而同期服务器CPU的DDR4内存带宽约为200 GB/s,相差8倍。对于大模型推理中的权重读取和激活值传输,高带宽是保证低延迟的关键。
然而,GPU并非万能。其计算特性也带来了一些限制:
- 分支处理能力弱:SIMT模式下,分支分歧会导致性能下降,因此GPU不适合逻辑复杂、分支密集的任务。
- 单线程性能低:单个CUDA Core的频率和IPC远低于CPU核心,串行任务在GPU上表现不佳。
- 显存容量受限:单卡显存(80GB)远小于CPU内存(数百GB),大模型需要多卡并行或模型并行策略。
2.5 案例:从架构视角理解A100与H100的差异
以NVIDIA A100(Ampere架构)和H100(Hopper架构)的对比为例,可以直观看到架构演进对AI计算的影响。
指标 A100 (Ampere) H100 (Hopper)
FP32算力 19.5 TFLOPS 67 TFLOPS
FP16 Tensor Core 312 TFLOPS 990 TFLOPS
显存容量/带宽 80GB / 1.6TB/s 80GB / 3.35TB/s
L2缓存 40MB 50MB
NVLink带宽 600 GB/s 900 GB/s
Transformer Engine 无 有(支持动态精度选择)
H100相比A100的算力提升主要来自三个方面:更高的SM数量(132 vs 108)、更先进的制程(4nm vs 7nm)、以及第四代Tensor Core的改进。更重要的是,H100引入了Transformer Engine,它能够在Transformer模型的不同层之间自动切换FP8和FP16精度,在保证精度的前提下进一步提升吞吐量。
从集群视角看,H100的NVLink带宽提升到900 GB/s,使得多卡间的梯度同步和模型并行通信更加高效。这意味着在构建AI算力集群时,H100不仅单卡性能更强,其可扩展性也更好。
2.6 常见误区与复盘
在理解GPU架构时,初学者常陷入以下误区:
一个典型的复盘案例:某团队在A100上部署BERT推理服务,初始实现使用FP32精度,测得延迟为12ms。通过分析发现,计算利用率仅为35%,而内存带宽利用率达到90%。优化方案包括:切换到FP16精度(利用Tensor Core)、将权重矩阵分块加载到共享内存、使用CUDA Graph减少内核启动开销。优化后延迟降至3.2ms,计算利用率提升至78%。
这个案例说明,理解GPU的架构特性(Tensor Core、共享内存、线程调度)是性能优化的前提。盲目增加并行线程数或依赖编译器优化,往往无法触及性能瓶颈的本质。