☰
GPU架构入门:AI Infra工程师必懂的吞吐量与核心部件解析
2026/10/10 4:07:37 网站建设 项目流程

聊AI Infra,绕不开的就是GPU。这不是口号,而是所有做训练平台、推理服务、集群调度的人每天都要面对的现实:无论框架写得多漂亮,最后真正花钱、耗电、吃算力的,都是那一张张加速卡。作为AI Infra入门系列的第二篇,这篇把GPU架构这条主线捋清楚。适合刚入行、之前只调过接口却没见过硬件细节的工程师,也适合想从应用层往底层走、准备开始看官方架构白皮书的同学。读完你至少能搞清楚三个问题:GPU凭什么比CPU快这么多、一张卡上到底有哪些关键部件、以及拿到一张卡之后怎么估算它跑模型的实际能力。

很多人会把GPU架构当成“硬件工程师才需要懂的东西”,这个想法在AI Infra里是行不通的。你做K8s调度要判断节点资源够不够,你得知道算力单位怎么换算;你做推理服务要定batch大小和并发度,你得知道显存带宽和SM数量的上限;你做训练加速要决定开不开梯度累积、要不要优化通信开销,你得知道瓶颈在GPU计算还是PCIe传输。不懂GPU架构,这些决策全是蒙的。这篇我就从最基本的几个概念讲起,尽量用大白话,穿插实际项目里的计算方法和避坑经验。

1. GPU为什么会成为AI算力的主角

1.1 从CPU到GPU,是一次吞吐量思维的转变

CPU的设计目标是单个任务跑得快。为了这个目标,它塞入了大量逻辑控制单元、分支预测器、大容量的多级缓存,整个流水线都是为了降低延迟设计的。写一段Java业务代码,里面全是if else、循环、函数调用,CPU跑起来游刃有余,因为它本来就不适合大规模重复计算。

GPU的思路完全反过来。它的设计目标不是“单个任务快”,而是“同一时刻处理大量相似任务快”。GPU里放了几千甚至上万个核心,每个核心都很简单,没有复杂的乱序执行逻辑,也没有超大缓存,但它们可以同时干活。这种设计哲学叫吞吐量优先。

拿生活类比一下:CPU是一个什么都会的博士,Uber难题交给他没问题;GPU是一千个小学生,你让他们解一个复杂的微积分肯定不行,但让他们每人算一道口算题,一千道题瞬间出答案。深度学习训练的本质就是海量的口算题——矩阵乘法、卷积、注意力计算,基本全是重复的乘加运算,每一个元素的计算逻辑都一样,只是数据不同。这种工作量天生就是GPU的菜。

所以在AI Infra的语境下,评价硬件的核心指标从CPU时代的“低延迟”变成了GPU时代的“高吞吐”。你调度任务、设计批处理大小、做算子融合的时候,脑子里始终要有一个意识:我到底是把单个请求压得更低,还是把单位时间内处理的总请求量拉得更高。生成式AI的推理服务通常采用批处理来提升吞吐,就是因为GPU这数学性质决定的。

1.2 三个决定性的数字:并发、带宽、算力

做AI基础设施的人每天跟显卡打交道,其实核心就看三个数字。

第一个是并发度。一张旗舰加速卡能够同时容纳数万个线程,这些线程分成小组并行执行。算力再强,如果没法把足够多的任务喂进去,那也是浪费,所以框架内部要维持足够的并行度来“压满”硬件。

第二个是显存带宽。大家都知道GPU显存大,但更关键的是它跟计算单元之间的搬运速度。现代加速卡普遍采用HBM高带宽内存,带宽普遍在2TB/s到3TB/s以上。CPU这边哪怕是最新的内存也就几十GB/s,一个数量级的差距。AI模型里面的矩阵乘最怕“数据搬到一半算完了”的尴尬,带宽不够会直接把计算单元饿死。

第三个是浮点算力。以FP16半精度为例,现代加速卡的单卡算力已经从几十Tera-FLOPS涨到数百甚至上千Tera-FLOPS。这个数字决定了一个模型理论上的最短训练时间,也是你购买资源、申请显卡时最常看到的宣传指标。

这三个数字相互制约:带宽决定数据搬运上限,算力决定计算上限,并发度决定你能把多大的batch塞进去并且让硬件忙起来。做性能优化的人每天就是在调和这三个指标之间的矛盾。

2. 拆开GPU,核心架构单元一次讲清

2.1 SM——最基本的计算单元

一张GPU不是简单地把几万个核心摊开就能工作。以主流加速卡为例,芯片内部被划分成很多个处理单元,工业界一般叫SM(Streaming Multiprocessor,流式多处理器)。它就是GPU最基础的硬件计算单元,你可以把整卡想象成一个大型工厂,SM是里面的一个智能车间。

每个SM内部有几个固定组件:几十到上百个CUDACore算术单元(负责普通的加减乘除学运算)、Tensor Core张量核心(专门负责矩阵乘加)、SFU特殊功能单元(算平方根、倒数、三角函数)、寄存器文件和共享内存,以及负责调度线程的多个Warp Scheduler。

不同代产品的SM内部配置差异很大。比如老架构可能一个SM里只有几个调度器,新架构则把每个SM分成了多个处理分区,每个分区独立调度。但结构逻辑是稳定不变的:先由调度器从一堆线程里挑一组,发给计算单元执行指令,结果写回寄存器,再由共享内存或L1缓存汇总。

这背后可以解释一个实际现象:为什么很多性能优化经验都说“要增加占用率(occupancy)”?因为每个SM内的资源(寄存器、共享内存)是固定的,如果一个线程块占的资源太多,SM里能同时驻留的线程块就少;但如果线程数太小,又会喂不饱调度器。想要把SM跑满,你得在资源占用和调度并发之间找平衡。这就是为什么类似“每个线程块大小取128”这样的调整,会对性能产生巨大影响。调着调着你会发现GPU计算卡没换,只是把自己卡在某个资源瓶颈上,性能却能差出百分之几十。

2.2 Warp与SIMT,线程到底是怎么跑的

GPU里最小的调度单位不是单个线程,而是一组线程,这个组叫warp。主流架构里一个warp固定包含32个线程。它们的核心特点是:这32个线程在同一时刻执行同一条机器指令,只是操作的数据地址不同。这种模式叫SIMT——单指令多线程。

这意味着什么呢?举例来说,如果一个矩阵是按行分成若干个warp去处理,你最好保证每行长度是32的整数倍,或者每一块分配的线程数与32对齐。如果不齐,最后一个warp里就会有部分线程空转,而空转的线程仍然占用调度槽位,白白浪费吞吐能力。这就是为什么很多算子代码里会有“pad到32的倍数”“pad到16的倍数”这类的操作,背后全是warp对齐的考虑,而不是玄学。

还有一个必须懂的概念叫分支发散。一个warp里如果某些线程进入了if分支,另一些线程进入了else分支,这个warp的执行就会“分裂”:先执行if路径并屏蔽掉其他线程,再执行else路径。整个过程加起来等于一个warp干了两份活。所以写GPU算子时候,要尽量避免在warp内部出现基于线程ID的条件分支,更合理的办法是让整个warp统一走同一条路径,哪怕多算一点数据也比发散划算。

聊到这儿顺便补一句:不要用CPU裸线程的方式理解GPU。CPU线程之间几乎完全独立,而GPU线程的独立粒度其实很粗,真正高效利用硬件的关键是让同一个warp里的线程行为尽量一致。很多从CPU转过来写GPU代码的人,第一步优化的就是把“分支发散”理顺。

2.3 Tensor Core与混合精度,AI算力的加速引擎

早期GPU跑深度学习全靠普通的FP32算术单元硬算矩阵乘法。后来芯片设计者意识到AI里面的矩阵乘法太常见了,干脆在硅片上做了专用的矩阵运算电路,这个就是Tensor Core。

Tensor Core做的事情很聚焦:一次指令周期内完成一个小规模的矩阵乘加操作,比如常见的4x4矩阵乘以4x4矩阵再加上一个累加矩阵。看起来很小,但它是硬件级的专用通路,吞吐远高于用普通核心挤牙膏。用车间比喻就是,普通算术单元是一个一个工人在拧螺丝,Tensor Core是一条自动流水线,一上来就装好一组零件,一次性焊接完成。

这直接催生了混合精度训练。以前所有计算都用FP32,现在我们把权重和激活值用FP16存储和计算,梯度更新阶段再用FP32保存一个“高精度主副本”,配合损失缩放技术防止精度溢出。Tensor Core在FP16上的吞吐相比FP32往往有几倍到十几倍的差距,这就是为什么现代训练框架默认开启自动混合精度。

最近几代加速卡的Tensor Core还支持了TF32、BF16、FP8甚至更低的精度格式。FP8在训练大模型时能把吞吐再拉高一截,代价是精度链路更敏感,你需要仔细做损失缩放和溢出检查。所以在AI Infra的实际工作中,精度格式的选择不是随手一开,而是“算力翻倍、调参难度上升”的权衡。我个人建议,刚开始研究混合精度时先跑通FP16链路,看动态损失缩放的表现,再考虑往FP8上迈。

2.4 内存层次,为什么谁都比不上显存贵

GPU和CPU一样有内存层次结构,从快到慢分别是寄存器、共享内存、L1/L2缓存、显存。每一层之间存在数量级的速度差异。

寄存器文件位于计算单元内部,访问速度可以说是零延迟,但容量非常小,每个线程能用的寄存器数量是有限制的。共享内存是SM内部的显式管理存储区,延迟大约二三十个时钟周期,容量通常是几十KB到一百多KB。L2缓存是全卡共享的,容量几十MB,延迟大概两三百周期。最慢的就是HBM显存了,延迟几百个周期,但容量大(几十GB到上百GB),带宽也在2TB/s以上。

这引出GPU编程的一个重要观念:能复用的数据尽量往上层放。一次矩阵乘法中,如果数据块能从显存搬到共享内存再参与多次计算,就可以显著减少对HBM的访问。实际操作中,共享内存的使用是GemT等多种算子性能优化的分水岭。谁会用共享内存做分块和数据复用,谁就能把算子性能拉开一大截。类似“tiling”“block矩阵乘法”的优化手段,本质都是在试图把数据访问尽量锁在一层快速存储里。

很多做AI Infra的人容易只看显存容量,忽略了带宽和延迟层级。实际上算子的时间分布经常百分之六七十都花在等数据上,而不是真正计算。训练框架里做的梯度累积、算子融合、进算子的数据排布优化,有一大半都是为了“把数据在合适的时间放到合适的地点”。

3. 会算账才配调优,算力与带宽的估算方法

3.1 理论算力怎么算

看加速卡参数,经常遇到一堆“TFLOPS”的数字,但这个数字是怎么来的,很多人其实没算过。其实公式非常直白:

FLOPS = 核心数 × 每周期单核执行的浮点操作次数 × 核心主频(Hz)

注意如果是乘加操作,一次乘加包含乘法加加法两个浮点操作,所以要再乘以2。举个例子,某代主流产品有约6912个FP32核心,Boost主频约1.41GHz,那么FP32的理论峰值大约等于:

6912 × 2 × 1.41GHz ≈ 19.5 TFLOPS

这个数字和官方标称的FP32性能完全对得上。Tensor Core在FP16下的吞吐则高得多,官方给出的数字可能在FP32的十六倍左右,所以几乎所有的训练任务都会走Tensor Core路线。学习这类计算很有用,因为当你看到一款新产品标称“FP16算力990T”时,你可以反向推算它的Tensor Core频率、数量是否合理,防止被宣传稿忽悠。做成本测算时也习惯性用“半精度算力除以6倍模型参数”来估算训练多小时。

3.2 区分计算密集与访存密集,Roofline模型帮你定位瓶颈

算力再高也不代表什么任务都能跑快。实际上GPU程序有两种典型的瓶颈:一种是计算密集,算力不够;另一种是访存密集,数据搬不动。

判定方法是用算术强度(Arithmetic Intensity)这个指标:完成的浮点操作数除以访问的字节数。每个架构可以用一张roofline图表达自己的性能极限。横轴是算术强度,纵轴是可达性能。对于算术强度很低的任务,它受限于内存带宽这条45度直线;只有算术强度超过某个转折点之后,它才会撞到算力屋顶。矩阵乘法属于算术强度极高的任务,天然把带宽喂得满,所以GPU发挥得好;而一些向量元素逐个处理的算子,比如LayerNorm中的某些处理、简单element-wise操作,算术强度低,再强的卡也被内存带宽锁死。

落到实处的判断经验是:如果GPU利用率已经超过80%,先查它是被算力占满还是被访存占满。怎么查?最直接的办法是把任务里的计算量估一下,再结合理论峰值,算出理论时间;再用性能工具看实际跑出的时间和Memory Throughput,一比就明白瓶颈在哪里。很多人一上来就调并行策略,其实先分清楚自己任务是memory-bound还是compute-bound才是正经第一步。曾经我处理过一个大模型推理慢的案例,模型本身算子算术强度极高,但半天瓶颈在CPU侧的tokenize和采样上,GPU几乎在空等——这看起来是GPU优化问题,实际上是CPU负载问题。

3.3 用公式估算一次大模型训练所需时间

做训练平台的人经常要回答业务方“我有这么多卡,训练完这个模型要多久”的问题。这有一套工程估算公式,准确度谈不上精细,但用来做资源预算足够了。

先说大模型训练的浮点消耗经典口径:每个token大约需要6倍模型参数量的FLOPs。这个6倍来自前向传播约2倍,反向约4倍。训练一个7B模型、总共需要消耗3千亿token,那么总计算量是:

总FLOPs = 3e11 × 6 × 7e9 ≈ 1.26e22

假设我们用的是某款FP16算力约312T的加速卡,8张卡,MFU(模型可用算力利用率)按50%算:

时间 = 1.26e22 ÷ (3.12e14 × 8 × 0.5) ≈ 1.01e7秒 ≈ 117天

这里MFU是最容易拍脑袋的参数。训练大模型跑到50%的MFU已经算相当不错,很多分布式训练场景下MFU甚至只有20%到30%。MFU受两个东西影响:一是算子本身有没有打满Tensor Core,二是通信和等待时间占了多少。你不需要一个特别准的MFU,但要有这个概念,否则预算跟实际差好几倍都不知道为什么。另一个更简单的估算思路是拿单卡每秒能“消化”的token数来反推总耗时,这在推理服务容量规划里更常用,但逻辑同源。

4. 单卡之外,多卡互联与集群

4.1 为什么互联这么重要,大模型训练都是“集体行动”

单张卡算力再强,也放不下大模型的参数和优化器状态。数据并行、张量并行、流水线并行,本质上就是把一个大模型拆到多张卡上协同计算。而协同计算的前提是卡与卡之间能快速交换数据。

分布式训练里有个高频操作叫梯度同步:每张卡算完自己的反向传播,要把它计算出的梯度分享给其他所有卡,然后才能开始下一步更新。这个操作的实现逻辑在通信库一般叫AllReduce。模型越大,每次同步的梯度数据量越大,通信耗时在整个训练迭代里占比就越高。如果通信开销压不下来,加再多的卡也可能毫无收益,因为大家都在跑网络同步。

现实中有个常见教训:用几张卡训练时,损失曲线收敛速度没有按线性增长,甚至更多卡之后整体吞吐反而下降。多数情况下不是框架算力没吃满,而是跨卡通信占据了太高的时间比例。做AI Infra的人最应该掌握的技能之一,就是用时间线工具看训练迭代里compute time和communication time的比例。只要通信占比跑到20%以上,优化方向就该往通信倾斜了。

4.2 机内互联与跨节点互联,带宽差距决定拓扑设计

GPU互联有两条层次分明的链路。机内多卡之间,用的是厂商私有的高速互联通道加交换机,带宽极高,双向可达几百GB/s,能够实现全互联拓扑。相比之下,PCIe总线虽然通用性好,但在多卡彻底两两互联场景下带宽和拓扑都有明显短板,通常只用于传输控制命令、小数据包以及跨机访问外设。

机与机之间则是通过网络。常见选择是高性能以太网搭配RDMA技术,或者专用的InfiniBand网络。它们的单链路带宽通常在几十GB/s到几百GB/s之间,比机内的私有互联低一个数量级。所以想设计一个高效集群,最基本的思路就是尽量把通信流量限制在机内总线层面,把跨机流量降到最低。这就是为什么市面上几乎所有大模型训练框架都层架构上做了“分层通信”的优化,比如把梯度切块后只让部分数据走跨机链路。

以前我参与过一个小集群调试:原本将8卡分散在4台机器上,每台2卡,训练吞吐一般。后来把8卡集中到2台机器,使用机内高速互联连接,吞吐直接提升接近两倍。原因很简单——梯度AllReduce从大量走网络变成了基本走机内通道。这个案例说明了一个道理:遇到分布式性能问题,第一步先看通信拓扑,而不是盲目调超参。

4.3 集合通信库到底在干什么

做AI Infra的人一定会接触到集合通信库,比如官方提供的NCCL、各类国产加速卡对标实现的集合通信库。很多人只把它当成“装好就能用”的东西,但它其实是你控制通信效率的底层接口。

以AllReduce为例,朴素做法是每一张卡把自己的梯度广播给所有卡,这样做数据量爆炸。集合通信库则会把操作拆分成不同算法来降低通信量:最常见的有Ring AllReduce,把多张卡排成环,先分块做Reduce-Scatter,再做AllGather。总通信数据量是跨节点管线设计的核心。你再怎么优化框架,最终算来算去都是同一套通信数学模型。

理解集合通信库的价值在于:它有个重要参数叫通信原语覆盖和横向扩展能力。调大通信块大小、开启分层归约、调整环境变量、合理调度EP(进程组),这些都能显著影响收敛速度。当你看到训练日志里NCCL报错、超时、带宽打不上来的时候,不要慌着换卡,先检查拓扑感知是否开启,机器序号是否和分配到的GPU索引对得上——这类配置问题冒出来频率极高。之前踩过最大的坑就是机器上的PCIe拓扑和物理位置不一致,导致通信库默认走了低速链路,带宽只剩下原本的十分之一。

5. 实操经验,瓶颈定位与新手避坑

5.1 别被GPU利用率骗了

很多人看性能就是一句“GPU利用率100%”,然后就以为硬件吃满了。这个判断在AI Infra里面经常是错的。显卡监控工具显示的利用率,通常代表某个采样周期内至少有一个内核在SM上运行,或者有计算活动发生,并不代表SM上的所有计算单元都饱和。

真实情况往往是:显存带宽打满而计算单元空转;或者是大量短小kernel不断启动、停止,利用率看着100%,但每个kernel都在等数据;又或者是CPU侧数据处理太慢,GPU吃几口就饿一会儿。我见过一个推理服务,GPU利用率很高,但吞吐却不理想,排查后根因是频繁调用小算子导致kernel launch开销占了大半时间,而真正执行矩阵乘法的时间非常短。看一眼实效性能工具,把GPU主动计算的时间占比拉出来,才发现问题。所以建议所有初学者把“利用率”这个数字当成参考,而不是唯一真理,一定要配合其他指标一起看。

5.2 定位瓶颈的三板斧

第一板斧,先看内存带宽和算力利用率。显卡的监控工具能看到处理器活动、显存带宽、显存控制器活动等,如果内存带宽已经到80%以上而算力利用率不高,你优先确认算子访存模式,考虑做算子融合和分块优化。

第二板斧,看时间线里的kernel执行情况。用Nsight系列工具或者自带profiler跑一个代表性的训练步,观察每个kernel的执行时间、空档、CPU与GPU之间的同步等待。如果时间线里出现大量微小kernel与明显间隙,八成是launch overhead或host同步造成,这时候该做算子融合、减少kernel数、用CUDA Graph技术合并一系列操作。

第三板斧,看warp占用率和分支行为。用一个能统计SM内部活动的profiler,查occupancy和warp stall原因。如果stall原因全部是“等待数据”则往下追访存优化;如果是因为分支发散则改写算法路径;如果是因为寄存器溢出则调编译选项或减少每个线程的资源占用。这套方法论第一次用会感觉繁琐,但多跑两次就会形成条件反射:一看指标,二看时间线,三看内部溢出情况。

这三板斧不需要每次全上,但遇到疑难性能问题,按这个顺序排查成功率极高。拿一个真实案例说,之前有人反馈训练矩阵运算极慢,我一看时间线,发现瓶颈竟是在一次数据转置操作的kernel上——它访问显存的方式极其糟糕,造成大量bank冲突和缓存失效,GPU算力根本没参与。这个转置改了实现,整体训练速度提升2倍以上。

5.3 高频误区速查表

现象可能原因建议对策
GPU显示100%但吞吐不涨小kernel太多,launch开销占大头合并算子,使用CUDA Graph批量启动
显存占用高但SM很闲算子访存模型差,卡在带宽做共享内存分块、减少随机访问、增加数据复用
加更多卡吞吐不升反降通信占比过高,拓扑不匹配调整通信策略,检查机内互联是否启用
Tensor Core开了但提速不明显数据维度未对齐或精度格式不匹配检查矩阵维度是否满足对齐条件,切换亲和格式布局
损失突然变成NaN混合精度溢出开动态损失缩放,检查梯度值范围

表格里每一行背后都是真实踩过的坑。尤其“加卡不加速”这条,很多团队一遇到训练慢就盲目加卡,结果资源成本翻倍,吞吐原地踏步。先做小规模扩展测试,看每增加一张卡带来的吞吐增益,再决定要不要扩集群,这才是做AI Infra的基本素养。

5.4 我的一点学习建议

这几年看下来,我觉得系统学习GPU架构最有性价比的路径是:先把“SM、Warp、Tensor Core、内存层次、通信拓扑”这五个概念吃透,然后拿着官方性能分析工具,把自己手头跑得慢的算子一个个分析一遍,记录瓶颈原因。不需要一上来啃几百页的架构白皮书,看一遍忘掉大部分,纯浪费时间。反过来,带着“为什么我这段代码这么慢”的问题去查架构文档,查一次记住一次,效果是最好的。

另外,多注意看别人写的算子优化案例,甚至自己手写一个简单的矩阵乘算子,用共享内存做分块优化。这个过程能把前面所有概念串起来。等哪天你能看着一个算子的计算量和访存量,比较准确地说出它的理论耗时和实际差距,你就算是真正入了AI Infra的门道。

这些经验都是我反复折腾才积累下来的,写出来是希望后来的人少走些弯路。GPU架构永远在演进,新的精度格式、新的互联协议、新的指令集不断出现,但底层逻辑不会大变。把基础打扎实,后面看什么都快。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询