第1章:GPU架构基础——从Fermi到Hopper的演进之路

各位同学,欢迎来到《从零搭建GPU性能分析流水线》的第一章。

说实话,我刚开始接触GPU编程那会儿,面对各种架构名词也是一头雾水。什么SM、Warp、共享内存,听着就头大。但后来我发现,搞懂这些底层结构,恰恰是写出高性能代码的关键。

这一章,我们就从NVIDIA GPU的架构演进讲起,然后深入SM内部,看看Warp是怎么调度执行的,最后把内存层次结构理清楚。嗯,内容不少,但都是硬核干货。

1.1 NVIDIA GPU架构演进:从Fermi到Hopper

先聊聊架构演进。我习惯把NVIDIA的架构分成几个关键节点,每个节点都解决了一些核心痛点。

架构代号 发布年份 核心改进 我的评价
Fermi (GF100) 2010 首个完整SM架构,支持C++、ECC 奠定了现代GPU的基础
Kepler (GK104) 2012 SMX单元,动态并行 能效比大幅提升
Maxwell (GM204) 2014 改进调度器,降低功耗 我当年做项目最爱的架构
Pascal (GP100) 2016 NVLink,统一内存 大模型训练的起点
Volta (GV100) 2017 Tensor Core,独立线程调度 AI计算的转折点
Turing (TU102) 2018 RT Core,混合精度 实时光线追踪的突破
Ampere (GA100) 2020 第三代Tensor Core,稀疏性支持 目前最通用的架构
Hopper (GH100) 2022 Transformer Engine,DPX指令 专为大模型而生

你想想看,从Fermi到Hopper,差不多12年时间。每一代都在解决上一代的瓶颈。我个人觉得,理解这些演进背后的逻辑,比死记硬背参数重要得多。

核心观点:架构演进的核心驱动力是「计算密度」和「内存带宽」的平衡。每次新架构都在试图打破这个瓶颈。

1.2 SM(流式多处理器)架构详解

SM是GPU的心脏。说白了,一个GPU就是由多个SM组成的。每个SM内部包含:

  • CUDA Core:执行整数和浮点运算
  • Tensor Core:执行矩阵乘加运算(Volta开始引入)
  • Warp Scheduler:负责Warp的调度
  • Register File:寄存器文件,容量巨大
  • Shared Memory:共享内存,可编程缓存
  • L1 Cache:一级缓存,与共享内存共享容量

我记得在Ampere架构上,每个SM有128个CUDA Core和4个Tensor Core。但到了Hopper,SM内部结构又做了调整,增加了DPX指令单元,专门加速动态规划算法。

实战技巧:写代码时,我习惯先查一下目标GPU的SM数量。比如A100有108个SM,每个SM有64个FP32 CUDA Core。这样就能估算出理论峰值性能。

1.3 Warp调度与执行机制

Warp是GPU执行的基本单位。一个Warp包含32个线程,它们以SIMT(单指令多线程)方式执行。

为什么会是32?嗯,这是NVIDIA经过大量测试后选定的最优值。太小了调度开销大,太大了分支发散严重。

Warp调度有几个关键点:

  1. 零开销上下文切换:SM可以快速切换Warp,隐藏内存延迟
  2. Warp Divergence:如果Warp内线程走不同分支,性能会下降
  3. Occupancy:SM上活跃Warp数量与最大Warp数量的比值

我曾经在一个项目中,因为没注意Warp Divergence,导致性能只有预期的30%。后来把分支条件改成按Warp对齐,性能直接翻了3倍。这个坑,你们一定要记住。

避坑指南:我曾经在写归约操作时,让Warp内32个线程都去访问全局内存的不同地址。结果因为Warp内线程执行顺序不确定,导致数据竞争。后来改用Warp Shuffle指令才解决。

1.4 内存层次结构

GPU的内存层次,说白了就是「越靠近计算单元,越快但越小」。我们从上往下看:

寄存器(Register)

最快,每个线程私有。每个SM有数万个寄存器。但要注意,寄存器溢出会打到Local Memory(实际在全局内存中),性能会暴跌。

共享内存(Shared Memory)

可编程缓存,同一个Block内的线程可以共享。延迟极低(约20-30个时钟周期)。容量有限,通常每个SM 48KB-164KB不等。

我建议你把频繁访问的数据放到共享内存里。比如矩阵分块计算,用共享内存做Tile,能减少全局内存访问次数。

L1/L2缓存

L1缓存和共享内存共享硬件资源。L2缓存是所有SM共享的。在Ampere上,L2缓存达到了40MB,对很多场景已经够用了。

全局内存(Global Memory)

最大,但最慢。延迟400-800个时钟周期。访问时要尽量合并访问(Coalesced Access),让Warp内线程访问连续地址。

内存类型 延迟 容量 作用域
寄存器 1个周期 每个SM 64K-256K 线程私有
共享内存 20-30个周期 每个SM 48-164KB Block内共享
L1缓存 30-40个周期 与共享内存共享 SM私有
L2缓存 200-300个周期 40MB (A100) 全局共享
全局内存 400-800个周期 40-80GB 全局

性能口诀:能放寄存器不放共享内存,能放共享内存不放全局内存。这个顺序搞反了,性能就悬了。

1.5 本章小结

这一章我们走马观花地看了GPU架构的演进,深入了SM内部,理解了Warp调度机制,也理清了内存层次结构。

你可能会觉得内容有点多。没关系,后面每一章都会用到这些知识。我建议你把这张内存层次表格打印出来贴在工位上,写代码时对照着看。

下一章,我们会搭建第一个性能分析工具——NVIDIA Nsight Systems。到时候你会看到,这些底层知识如何指导我们定位性能瓶颈。

课后思考:为什么NVIDIA不把共享内存做得更大?如果共享内存有1MB,会有什么问题?想明白这个问题,你就理解了GPU架构设计的权衡之道。

好了,第一章就到这里。记住,理解架构是写出高性能代码的第一步。我们下章见。