GPU基础架构:从硬件组成到调度单元

要理解GPU利用率为何波动,首先必须看清GPU内部是如何组织的。与CPU面向串行任务、追求单核极致性能不同,GPU面向海量并行任务,其设计哲学是“用大量简单核心 + 精细调度”来换取吞吐量。本章将拆解GPU的硬件组成、计算与内存层次,以及流处理器与调度单元的工作机制,为后续分析利用率波动打下硬件基础。

2.1 GPU硬件整体组成

以NVIDIA Ampere架构(如A100)和Hopper架构(如H100)为例,一颗GPU芯片由以下关键区域构成:

  • GPU芯片(Die):包含所有计算单元、缓存、互联总线,通过PCIe或NVLink与主机通信。
  • 内存系统:包括显存(HBM2e/HBM3)、L2缓存、各SM内部的L1/共享内存。
  • GPC(图形处理簇):芯片被划分为多个GPC,每个GPC包含若干TPC(纹理处理簇)。
  • TPC:每个TPC包含两个SM(流式多处理器),是实际执行计算的单元。
  • 内存控制器:管理显存读写,与L2缓存直接相连。

下图展示了从芯片到SM的层级结构,以及数据通路:

GPU硬件层级结构与数据通路 GPU 芯片 (Die) GPC 0 TPC 0.0 SM 0 128 CUDA核 4 调度器 共享内存 SM 1 128 CUDA核 4 调度器 共享内存 TPC 0.1 SM 2 128 CUDA核 4 调度器 共享内存 SM 3 128 CUDA核 4 调度器 共享内存 GPC 1 TPC 1.0 SM 4 128 CUDA核 4 调度器 共享内存 SM 5 128 CUDA核 4 调度器 共享内存 TPC 1.1 SM 6 128 CUDA核 4 调度器 共享内存 SM 7 128 CUDA核 4 调度器 共享内存 L2 缓存 (40MB, 所有SM共享) 数据聚合点 / 跨SM通信枢纽 内存控制器 0-3 HBM2e 通道 内存控制器 4-7 HBM2e 通道 HBM2e 显存 (40GB, 带宽 1.6TB/s) 数据流方向

从图中可以看到,SM是GPU最基本的计算单元,多个SM组成TPC,多个TPC组成GPC。所有SM通过L2缓存与显存交互。这种层级结构决定了数据访问路径的长度,也直接影响利用率——如果数据频繁在SM与显存之间搬运,计算单元就会等待,利用率自然下降。

2.2 计算与内存层次

GPU的内存层次设计遵循“越靠近计算单元,速度越快、容量越小”的原则。理解这一层次对定位利用率瓶颈至关重要。

内存层级 容量(A100典型值) 带宽/延迟 访问方 用途
寄存器文件 256KB / SM ~19TB/s(极低延迟) 单个线程 线程私有变量
共享内存 164KB / SM ~19TB/s(低延迟) 同一SM内线程块 线程块内数据共享、协作
L1缓存 与共享内存共用 ~19TB/s SM内所有线程 缓存全局内存访问
L2缓存 40MB(全芯片) ~5TB/s(延迟约200周期) 所有SM 跨SM数据共享、原子操作
HBM显存 40GB 1.6TB/s(延迟约400-600周期) 所有SM 全局数据存储

关键认知:SM访问显存的延迟是访问寄存器的数百倍。当线程需要从显存加载数据时,如果数据不在L2/L1缓存中,该线程就会停滞等待。GPU通过大量线程的并行切换来隐藏这种延迟——当一个线程等待内存时,调度器立即切换到另一个就绪线程执行。

2.3 流处理器与调度单元

每个SM内部包含多个流处理器(CUDA Core),以及负责指令分发的调度单元(Warp Scheduler)。以A100的SM为例:

  • 128个CUDA Core:分为4个处理块,每个块32个核心。
  • 4个Warp Scheduler:每个调度器管理一组线程束(Warp)。
  • Warp(线程束):32个线程组成一个Warp,这是GPU调度和执行的基本单位。
  • 指令分发单元:每个调度器每个时钟周期可向对应处理块发射一条指令。

调度机制的核心逻辑如下:

// 伪代码:Warp调度器工作逻辑
每个时钟周期:
  1. 从就绪队列中选择一个Warp
  2. 检查该Warp下一条指令所需操作数是否就绪
  3. 若就绪,发射指令到执行单元
  4. 若未就绪(等待内存/同步),标记为阻塞,选择下一个Warp
  5. 若所有Warp都阻塞,SM进入空闲(Stall)状态

这里有一个关键概念:Occupancy(占用率)。它表示SM中活跃Warp数量与最大可容纳Warp数量的比值。高占用率意味着有更多Warp可供调度器切换,从而更好地隐藏内存延迟。但占用率并非越高越好——过高的占用率可能导致寄存器溢出或缓存抖动。

实践提示:使用 nvidia-smi 查看的“GPU利用率”实际上是“SM上有活跃Warp的时间比例”。如果显示100%,说明每个时钟周期都有Warp在执行指令;如果显示30%,说明70%的周期SM处于空闲或等待状态。

2.4 案例:一个典型kernel的执行流程

假设我们启动一个包含1024个线程的kernel,GPU会如何执行?

  1. 线程块分配:1024个线程被组织为若干线程块(Block),例如8个Block,每块128线程。
  2. Block调度到SM:GPU的全局调度器将Block分配到各个SM。一个SM可以同时容纳多个Block(受资源限制)。
  3. Warp划分:每个Block内的128线程被划分为4个Warp(每Warp 32线程)。
  4. Warp执行:SM的4个调度器各负责一个Warp组,每个时钟周期发射一条指令。
  5. 内存访问:当Warp执行到需要访问显存的指令时,该Warp进入等待状态,调度器切换到其他就绪Warp。

假设每个线程需要从显存读取一个float(4字节),1024线程共需读取4KB数据。如果数据在L2缓存中,延迟约200周期;如果在HBM中,延迟约500周期。在等待期间,调度器可以执行其他Warp的指令——这就是GPU隐藏延迟的基本原理。

2.5 常见误区与复盘

误区一:GPU利用率100% = 性能最优
实际上,100%利用率可能意味着计算单元满载,但如果此时大量指令是低效的(如非合并内存访问、过度同步),实际吞吐量可能并不高。利用率只是“活跃时间比例”,不代表“有效工作比例”。
误区二:增加线程数一定能提高利用率
当SM中的Warp数量超过一定阈值后,再增加线程数反而可能导致资源竞争(寄存器不足、共享内存溢出),迫使部分Block排队等待,甚至降低整体利用率。
复盘案例:某训练任务在A100上GPU利用率仅45%。通过 ncu 性能分析发现:
- 每个线程块仅128线程,SM中活跃Warp数仅8个(最大64个),占用率仅12.5%
- 大量时间花在等待全局内存访问(stall原因:long scoreboard)
- 优化方案:将线程块增大到512线程,并使用共享内存做数据分块(tiling),将全局内存访问次数减少80%
- 优化后利用率提升至92%,训练时间缩短55%