GPU硬件架构深度拆解:从SM到显存带宽的底层原理
做GPU相关开发这几年我最大的感受是如果不懂硬件原理和架构光会调用框架接口遇到问题就抓瞎。不管是跑深度学习训练时GPU利用率死活上不去还是打游戏突然弹“d3d设备已移除”又或者部署推理服务时换了一张新卡就报unsupported gpu这些坑追到底全都指向同一个根源——你对GPU硬件本身理解得够不够。这个系列文章就是想把GPU从里到外拆开讲清楚。第一篇先从硬件架构入手后续再聊CUDA编程模型、显存管理、多卡互联和性能分析。适合需要做驱动开发、AI工程化、性能优化或者想弄明白显卡到底怎么工作的同学。1. 先回答一个最基础的问题GPU架构到底在讲什么1.1 一张显卡里到底装了些什么很多人习惯把“显卡”和“GPU”混着叫严格来说GPU是显卡上一颗核心芯片。以一张普通游戏卡为例PCB板上除了GPU芯片还有显存颗粒、供电模块、散热器、显示输出接口以及一堆用于视频信号处理和供电管理的辅助芯片。真正干活的是那颗面积巨大、密布晶体管的GPU芯片。GPU芯片内部并不是一个简单的“大号计算器”它集成了非常多的硬件模块海量的流式处理单元也就是常说的CUDA Core、张量计算单元Tensor Core、各级缓存L1/L2、显存控制器还有PCIe控制器、NVLink控制器数据中心卡有、显示引擎、视频编解码引擎以及负责命令解析和任务调度的前端模块。驱动开发里经常提到的“命令缓冲区”“DMA传输”“中断通知”本质都是围绕这些硬件模块展开的。应用程序把要做的事交给驱动驱动把它翻译成GPU能懂的“命令流”命令流进入GPU前端再由内部调度器分发到各个计算单元。所以如果只看软件你很难理解为什么同样的代码在不同显卡上表现差异巨大但如果反过来从硬件角度看一切都顺理成章。1.2 从GPC到TPC再到SMGPU的“厂区-车间-班组”结构NVIDIA的GPU芯片内部计算单元不是一盘散沙而是一套严格的层级组织。大体上分成三级GPCGraphics Processing Cluster图形处理簇相当于一个厂区包含多个TPC以及一些光栅化、裁剪等图形处理固定功能单元。TPCTexture Processing Cluster纹理处理簇相当于车间通常包含两个SM在图形渲染时还承担纹理采样相关工作。SMStreaming Multiprocessor流式多处理器相当于班组是真正执行计算指令的基本单元。为什么要把计算单元组织成这种树状结构一方面是芯片设计的物理约束——晶体管太多必须分区布线、分区供电否则时钟频率上不去另一方面是任务调度的需要——不是每个任务都动用全部计算单元按组划分可以更灵活地分配资源。这个知识什么时候能用到读显卡白皮书或者官方规格表时你会看到“该芯片包含xx个SM”这类参数这就是估算性能的基础。另外超频和功耗控制也是按区域来管理的不同GPC之间供电和时钟可以独立调节这也是为什么GPU频率不是恒定值而是会动态变化的硬件原因。1.3 读规格表不再头大手算一张卡的FP32算力了解架构之后最直接的收益就是能读懂规格表并且亲手验算。理论浮点算力有一个非常简单的公式FP32算力TFLOPS SM数量 × 每SM的FP32核心数 × 2 × 核心频率GHz为什么乘2因为FMA融合乘加指令一次同时完成乘法和加法对应两次浮点操作硬件指标都按FMA算两次计。举个例子RTX 4090128个SM每个SM有128个FP32核心Boost频率约2.52GHz那么FP32算力128×128×2×2.52≈82.6 TFLOPS与官方标称基本一致。再看A10040GB版本108个SM每SM 64个FP32核心频率约1.41GHz算力108×64×2×1.41≈19.5 TFLOPS。这么一比游戏卡的FP32算力比数据中心卡还高为什么A100反而贵那么多因为算力只是其中一个维度。A100的FP64算力、Tensor Core吞吐、显存带宽、NVLink互联、ECC纠错、虚拟化支持、稳定性验证这些都是游戏卡完全不具备的。硬件架构的差异从来不是单看一个数字就能概括的后面我会逐个层面展开。2. 微观视角SM内部到底长什么样2.1 SM的“工位”布局计算单元、调度器、寄存器堆一个SM内部就像一个小而精的处理器但它不是单核而是高度并行的多线程执行引擎。SM内部主要包括这些模块FP32算术单元做单精度浮点和整数运算就是宣传里最常见的“CUDA Core”。INT32算术单元专做整数运算有些架构里和FP32单元物理分离有些则共用数据通路。Tensor Core专为矩阵乘加设计的张量计算单元是AI计算的主力。SFU特殊函数单元处理三角函数、倒数、开方等复杂数学函数。LD/ST单元负责生成访存请求访问共享内存或全局显存。Warp调度器和分发单元决定下一个时钟周期让哪些warp执行什么指令。寄存器堆Register FileSM内最快的存储空间为每个线程私有。共享内存Shared Memory和L1缓存SM内可编程的快速存储配合L1缓存共享同一块物理内存可以通过API配置比例。你可能会有疑问为什么一个SM内部要塞这么多种计算单元直接全部用统一核心不行吗原因是不同类型的指令需要不同的硬件路径。如果让一个通用单元去执行三角函数它会占用大量寄存器且跑很慢有专门的SFU一条指令就能出结果。这也是GPU架构演进的重要思路——不是一味堆通用核心而是针对高频场景加入专用单元。2.2 Warp调度32个线程“绑成一捆”干活SM执行计算的最小调度单位叫WarpNVIDIA固定为32个线程。也就是说一次指令分发会让同一个Warp里的32个线程同时执行同一条指令。假如一个Warp中的32个线程需要走不同分支比如一部分线程做A操作、另一部分做B操作硬件只能先把走A分支的线程调度完再调度走B分支的线程这个现象叫分支发散branch divergence。打个比方一个班组里有32个人班长喊一声“按方案A干”所有人一起动手效率最高。但如果有人要按方案A、有人要按方案B班长只能让先按A干的人做完再让按B的人干另一部分人只能干等着。GPU的空泡、流水线停顿很大一部分就是这么来的。一个SM通常有4个Warp调度器每个调度器在一个时钟周期内选取一个可执行的Warp并分发指令。这意味着每个时钟周期SM最多能同时执行4条不同的Warp指令。这也是为什么30系之后NVIDIA把每SM的FP32核心数一路加到128——核心再多调度器分发能力不够计算单元也会饿着。2.3 Tensor Core与RT Core为AI和图形加出来的“专用车间”传统CUDA Core走的是通用计算路线处理各种标量运算都很均衡。但AI时代训练和推理的核心计算是矩阵乘法如果用通用单元逐元素计算效率很低。Tensor Core就是专门做矩阵乘加的高吞吐单元一次指令可以完成一个4×4甚至更大规模的矩阵乘加。以A100为例每个SM有4个第三代Tensor Core官方宣传的深度学习算力远高于FP32算力。大模型训练突破算力瓶颈靠的就是这类专用单元。H100、Blackwell架构更进一步把张量核心的精度支持扩展到了FP8、FP4推理吞吐又能翻几倍。图形侧也有类似逻辑RT Core专门做光线与包围盒的求交计算让光线追踪性能提高了几个数量级。从通用计算到专用加速这是架构设计的大趋势。理解这一点再看厂商宣传的“XX TOPS”“XX TFLOPS”时就能区分哪些是通用算力、哪些是专用算力两者不能画等号。2.4 不同代际的SM对比A100、H100与RTX 4090直接看一张对比表方便理解架构差异项目A100 (GA100)H100 (GH100)RTX 4090 (AD102)架构代号AmpereHopperAda LovelaceSM数量108132128每SM FP32核心64128128每SM Tensor Core第3代 4个第4代 4个第4代 4个L2缓存40MB50MB72MB显存类型HBM2e 40/80GBHBM3 80GBGDDR6X 24GB显存带宽1.6TB/s / 2TB/s3.35TB/s1.01TB/sFP32算力(约)19.5 TFLOPS67 TFLOPS82.6 TFLOPS同样的SM数量级别游戏卡的FP32算力比数据中心卡还高但H100的显存带宽是4090的3倍多而且支持NVLink、ECC、MIG虚拟化。这说明数据中心卡和游戏卡的设计目标完全不同游戏卡追求高频率、大L2、高性价比数据中心卡追求带宽、可靠性、多卡扩展能力。谁要说“游戏卡性价比高所以能取代数据中心卡”拿这张表对比一下就知道差距在哪里。3. 存储金字塔与显存带宽GPU性能的真正瓶颈3.1 从寄存器到显存GPU的“存储金字塔”GPU的性能瓶颈往往不在算力而在数据搬运。理解这一点需要先看懂GPU的存储结构。GPU内部同样有一个存储金字塔越靠近计算单元越快、越小越远离计算单元越慢、越大。存储层级容量以主流卡为例延迟量级作用范围生命周期寄存器每SM数百KB约1个时钟周期线程私有线程执行期间共享内存/L1每SM 128KB~256KB约20~30个时钟周期Block内共享Block存续期间L2缓存40MB~72MB约200个时钟周期全芯片统一动态替换显存6GB~80GB约400~600个时钟周期所有线程可见程序运行期间系统内存数十GB更高需经PCIe/NVLinkCPU与GPU共享程序运行期间这张表解释了为什么很多优化手段都围绕“减少显存访问”展开。寄存器够快但容量极小显存够大但延迟是寄存器几百倍。GPU要高效运行就得尽量让数据待在离计算单元更近的地方这就是局部性原理在GPU场景下的体现。3.2 显存带宽怎么算GDDR6X和HBM的差异显存带宽是GPU最核心的指标之一计算公式很直白带宽GB/s 显存等效频率Gbps× 位宽bit÷ 8RTX 4090用的GDDR6X显存等效频率是21Gbps位宽384bit代入公式21×384÷81008GB/s也就是约1TB/s。A100 80GB版用的是HBM2e等效频率3.2Gbps但位宽高达5120bit算下来带宽约2TB/s。H100的HBM3带宽更是到了3.35TB/s。GDDR和HBM的核心差异就在这里GDDR走的是高频率、窄位宽路线像一条高速公路车道少但每辆车跑得快HBM走的是低频率、超宽位宽路线像很多条并行的运输线把DRAM芯片堆叠起来用硅通孔TSV连接所以位宽能做到几千bit。HBM成本高、制造难度大所以目前主要在数据中心卡和旗舰计算卡上使用。实际工程里带宽决定了很多任务能不能跑满。比如大模型推理模型权重从显存搬到计算单元的速度往往才是瓶颈也就是常说的memory-bound。就算Tensor Core算力再高带宽不够计算单元仍然只能饿肚子。3.3 访存模式决定性能上限合并访问与缓存命中GPU的高带宽不是所有情况下都能用上它建立在“合并访问”的前提上。所谓合并访问是指同一个Warp内32个线程访问的显存地址尽可能连续。硬件会把一次Warp访问合并成尽可能少的内存事务地址越集中一次事务取回的有效数据越多实际带宽利用率越高。反过来如果32个线程各自访问间隔很远的地址比如按步长跳跃访问硬件要发起多次事务才能取回数据带宽利用率断崖式下降。我在实践中见过同样一个矩阵转置算子naive实现和优化后的tiled实现性能差一个数量级根源就是访存模式从“离散”变成了“合并”。所以看GPU利用率时也要多留个心眼。nvidia-smi显示的计算利用率高只代表SM的计算单元有活干不代表带宽被充分利用了。很多性能问题恰恰相反计算单元利用率不高但内存带宽已经打满这时候加算力毫无意义优化访存才是出路。4. 理解硬件之后那些工程问题瞬间就通了4.1 那些莫名其妙的报错硬件的锅还是软件的锅工程里最常见的GPU报错其实都能从硬件架构的角度找到原因。我整理了三个典型场景读者可以对照排查现象常见原因排查方向游戏或应用弹“d3d设备已移除”、“GPU发生崩溃”显存不足或损坏、供电不稳、温度过高触发降频/保护、驱动超时无响应(TDR)看事件查看器、查温度功耗曲线、重装干净驱动、检查显存体质新游戏/新工具提示“Unsupported GPU”显卡算力太老不支持Required的DX12/OpenCL版本或显卡太新、驱动软件识别不到查驱动版本、确认显卡代数、看官方支持列表PyTorch/CUDA报“requires device with capability (9, 0) but your gpu has capability (12, 0)”软件编译时按CUDA算力上限9.0Hopper编译但你的GPU算力是12.0Blackwell新卡“太新了”升级PyTorch/相关库到支持新算力架构的版本比如Photoshop Camera Raw的“使用GPU加速”选项灰掉很多情况下不是软件坏了而是显卡过老、不支持OpenCL 1.2或DirectX 12。再比如深度学习环境配置装PyTorch前先查清楚自己GPU的算力版本Compute Capability再选对应的CUDA Toolkit和PyTorch wheel能省掉一半安装报错。这类问题的通病是底层硬件能力与上层软件支持不匹配。理解了GPU的算力代际、指令集演进和驱动适配关系排查方向就清晰多了。4.2 驱动开发到底在开发什么UMD、KMD与命令流很多人一听到GPU驱动开发就发怵其实驱动本质上是硬件和操作系统之间的翻译层要做的事主要有三块。第一块是命令解析和传递。应用程序通过CUDA Runtime或者图形API发出请求用户态驱动UMD比如Linux下的libcuda.so负责把这些API调用翻译成GPU可以执行的命令流写入命令缓冲区。内核态驱动KMD则负责提交命令、处理中断以及和操作系统的内存管理、进程调度打交道。第二块是显存管理。GPU有自己的显存空间也有自己的页表。KMD要为每个进程或容器维护显存映射分配、回收、换入换出还要处理GPU和CPU之间的内存拷贝。显存管理做得不好就会出现显存泄漏或者跨进程访问越界。第三块是上下文切换。多个进程共享一块GPU时从一个程序的执行状态切到另一个程序需要保存和恢复大量状态包括显存映射、寄存器、命令队列等。这就是为什么GPU虚拟化和容器调度如此依赖底层硬件的隔离能力。为什么GPU驱动更新这么频繁因为硬件架构在快速迭代新架构带来新的指令集比如FP8、新的Tensor Core驱动和运行时必须同步支持。驱动开发的核心能力其实就是读懂硬件手册TRM把硬件特性准确暴露给上层。4.3 k8s调GPU、MIG与vGPU资源怎么分才合理在集群环境里跑AI经常要面对“GPU资源如何切分”的问题。Kubernetes调度GPU时底层靠的是Device Plugin机制NVIDIA的device plugin以DaemonSet方式运行在每个节点上向kubelet上报GPU数量kubelet再把GPU作为nvidia.com/gpu资源分配给Pod。容器启动时nvidia-container-toolkit负责把GPU设备节点和驱动库注入容器。如果多个任务想共享一张卡那就涉及更细的切分方案。有两个方向一个是时间片切分所有进程轮流用整块GPU隔离性弱、性能受影响另一个是空间切分把GPU从硬件层面拆成多个实例。MIGMulti-Instance GPU就是NVIDIA在A100/H100上做的空间切分技术每个实例拥有独立的SM、L2切片和显存控制器隔离性很好。为什么游戏卡用不了MIG因为MIG需要硬件层面的电源域、时钟域和显存控制器分区支持消费级芯片在设计时根本没做这些模块。同理vGPU在虚拟化场景下的SR-IOV直通也需要网卡、GPU等设备在硬件里实现虚拟化功能。资源调度的可行性最终还是由硬件架构决定的。5. 写在最后我的一些个人实操体会这个系列的第一篇我把精力都放在“硬件长什么样、为什么这么设计”上。写的过程中也在不断提醒自己规格表上的数字只能作为起点真正重要的是理解架构背后的取舍逻辑。给读者一个建议不要只盯着官方的TFLOPS和显存容量拿到一张卡先用nvidia-smi -q -d COMPUTE看看算力版本再用GPU-Z看一下SM数量、显存带宽、温度功耗曲线有条件的话用Nsight抓一次实际负载你会发现硬件设计远比纸面参数有意思。我自己的经验是很多看似玄学的性能问题最后都能在硬件架构层面找到解释。比如同样的模型在A卡上比N卡慢、为什么某种算子在小卡上反而更快、为什么多卡训练通信开销这么大背后全是SM数量、缓存容量、显存带宽和互联拓扑的差异。搞清楚这些底层逻辑再去看CUDA编程和性能调优很多方案你就能自己推导出来而不是靠背模板。这个系列后面几篇我计划重点写CUDA执行模型与存储模型包括线程束如何映射到SM、shared memory怎么分配才能提高occupancy、矩阵乘法怎么优化访存模式以及多卡互联NVLink/PCIe对分布式训练的影响。如果你手头也有类似的硬件困惑欢迎在评论区留个问题我挑典型的在后续文章里一起拆。