
一块GPU动辄上千亿个晶体管但如果拆开看真正“干活儿”的东西核心就是那一排排不起眼的执行单元Execution Unit简称EU。NVIDIA叫它CUDA CoreAMD叫它Stream ProcessorIntel在Xe架构里则直接叫Execution Unit名字不同本质上都是做同一件事的硬件模块。这篇文章就围绕“GPU执行单元”这个话题从硬件结构、执行模型、在AI芯片里的角色以及日常怎么观察它是否被喂饱做一个系统的拆解。适合做AI芯片架构、GPU驱动开发、大模型推理部署或者刚入坑异构计算的读者看完你会对“GPU算力到底是怎么出来的”有一个非常具体的认知也能用工具自己验证一个Kernel是不是真正把执行单元用满了。1. 执行单元到底是个什么东西GPU里的“生产工人”1.1 硬件构成不是一个大核而是一排小流水线执行单元是GPU中真正执行算术和逻辑运算的硬件模块你可以把它理解成芯片里的“最小计算车间”。一个典型的执行单元内部通常包含这些东西运算管道比如FP32、FP64、INT32、INT8这些不同数据宽度和精度的ALU/FPU管道、寄存器堆Register File存放指令操作数的“临时工作台”、发射端口接收调度器分发指令的入口、访存接口和写回缓冲负责数据读取和结果写回。这些部件按流水线方式组织一条指令从发射到写回大致经过取指、解码、发射、执行、写回这几个阶段。流水线让执行单元可以同时处理多条指令的不同阶段这是GPU能保持高吞吐的关键。如果你把CPU核心类比成“一个能处理复杂事务的全能店小二”那GPU执行单元更像是“流水线上只负责拧一颗螺丝的工人”。CPU核心追求的是单线程延迟尽可能低所以晶体管大量花在乱序执行、分支预测、缓存一致性这些复杂逻辑上GPU执行单元则极端追求吞吐量把芯片面积几乎都给了计算管道和寄存器堆调度逻辑高度精简、深度流水化。单个执行单元出厂速度不算惊人但几万个一起开工总吞吐就非常恐怖。1.2 从执行单元到整块GPU一层套一层的“生产车间”从单个执行单元往上走GPU是一个典型的树状层级结构理解这个层级比记住各种营销名词重要得多。拿NVIDIA举例子最底层是执行单元CUDA Core若干个执行单元会组成一个处理块处理块再组合成SMStreaming Multiprocessor多个SM再通过GPCGraphics Processing Cluster等结构组织起来。AMD那边对应的层级是Stream Processor组成CUCompute Unit多个CU再组成Shader Engine。Intel Xe架构也有类似设计执行单元EU组成Sub-Slice多个Sub-Slice组成Slice低端核显可能只有一个Slice高端计算卡则堆很多Slice。为什么要搞这么多层级两个原因一是物理布局和布线限制。芯片不是无限大的计算单元不能全部直接连到片上网络必须分簇、分层用局部互连保证信号延迟可控。二是调度需要层次化。GPU的线程调度也是从上往下逐级分发大调度器管大块小调度器管小块这种多级调度结构是GPU能在几千核上保持高效运行的关键。搞懂了这层嵌套关系再看“GPU算力是什么”就有概念了它本质上就是“单执行单元吞吐 × 执行单元数量 × 并行度利用率”的乘积而不只是某个主频数字。2. SIMT执行模型让执行单元高效运转的核心逻辑2.1 Warp和Wavefront为什么指令要成组发射如果你只记住一个关于GPU的概念那应该是SIMTSingle Instruction, Multiple Threads单指令多线程。它和CPU的SIMD单指令多数据很像但有本质区别。SIMD是在一个核里同时处理多条数据一条指令就搞定4个或者8个元素的运算SIMT则是把一堆线程组织成组每组在一个执行单元集群上以锁步lockstep方式执行。NVIDIA把32个线程组成一个WarpAMD把64个线程组成一个WavefrontIntel在Xe里也有类似的双子执行单元组合调度模式。为什么要这么做因为调度是有成本的。如果GPU像CPU那样每个线程单独取指、单独发射指令那调度器会瞬间被海量指令淹没功耗也扛不住。把32个线程绑成一个Warp后调度器每次只需要取一条指令、发一条指令然后让32个执行单元同时处理这32个线程各自的数据调度开销一下子摊薄了32倍。这就是为什么GPU特别适合做并行度高的任务矩阵相乘、卷积、图像像素处理都是天然的数据并行SIMT跟它们是绝配。2.2 分支分歧执行单元“摸鱼”的真正原因SIMT模型有一个天生的软肋就是分支分歧Branch Divergence。如果Warp里32个线程走入了不同的分支比如if (tid % 2 0)成立和不成立的两部分人执行单元没法同时两路都执行只能先执行A分支再执行B分支整个Warp的32个执行单元在其中一个分支执行时另一半对应的执行单元就在“空转”。这就是为什么GPU编程里经常强调“尽量避免Warp级分支”的原因。一个Warp里面的执行单元本来是一起干活的一旦产生分歧执行效率就直接打折。从AI芯片的角度看这个问题也很关键。神经网络计算中很多算子是齐整的矩阵乘法和规整的张量运算天然没有分歧所以能吃到接近100%的执行单元利用率。但像稀疏计算、动态Shape模型、控制流复杂的算子如果编译器没做好重组和优化执行单元的闲置率会非常明显。2.3 Tensor Core的诞生为什么AI芯片不再只靠通用执行单元如果你只看CUDA Core这类通用执行单元会发现AI算力再怎么堆功率、面积、内存带宽都会遇到瓶颈。原因很简单矩阵乘法和卷积本质上大量依赖乘累加运算FMA如果全部用通用执行单元的FP32/F16管道去一条一条执行指令发射、取数、写回的开销非常大利用率很难做满。NVIDIA在Volta架构开始引入Tensor Core本质上就是在SM内部额外增加一套专用的矩阵乘累加单元。它不再逐条指令执行而是通过MMAMatrix Multiply Accumulate等指令让硬件一次性完成一个矩阵小块的计算。Ampere时代常用的m16n8k8这种形态就是一次算16×8×8的矩阵乘累加相当于把几十上百次普通乘加运算打包进了一条指令。AMD的Matrix Core、Intel Xe矩阵扩展XMX也是同一个思路。到了专用AI芯片里谷歌TPU的脉动阵列Systolic Array、华为昇腾的AI Core都是这个理念的极端版把执行单元的形态从通用ALU改成专用的乘累加阵列。所以现在说AI芯片的算力光看通用执行单元的数量已经没有意义了还得看它有没有专用执行单元、专用执行单元的矩阵精度和吞吐是多少。执行单元这个词现在其实是一个很广的概念既包括通用ALU也包括专用的矩阵乘累加器。2.4 三大家的执行单元设计对比不同厂商的执行单元设计理念差别非常大我用一张表来说明问题厂商执行单元名称典型Warp/调度尺寸执行单元特性专用矩阵单元NVIDIACUDA CoreSM内32线程/WarpFP32/INT32/FMA管道附Tensor Core有Tensor CoreAMDStream ProcessorCU内64线程/Wavefront大力堆FP32/RDNA演进WMMA矩阵指令有Matrix CoreIntelExecution Unit / Xe Vector Engine8或16线程/子调度指令级多发射EU可执行Vector/Matrix混合有XMX专门AI芯片如AI Core等视各自指令集面向矩阵运算设计通用逻辑精简是核心单元这张表想说明的其实是一件事执行单元的设计没有绝对最优通用计算能力强的在灵活性和带宽匹配上有优势专用矩阵单元强的在AI计算密度上占优但不能兼顾所有算子。做架构选型时看的其实是你的业务负载到底长什么样。3. 实操观察怎么判断执行单元到底“忙不忙”3.1 三板斧工具nvidia-smi、Nsight Compute、Roofline搞GPU编程和部署的人一开始最容易犯的一个错误就是只看nvidia-smi的GPU利用率那一栏觉得“利用率95%我的GPU已经跑满了”。其实nvidia-smi那个利用率只是GPU整体处于活跃状态的时间占比它不是执行单元真正在计算的比例。它甚至可以很高——比如数据拷贝、显存等待这些行为也可能让GPU处于Active状态但执行单元在等数据根本没在算。想看执行单元的真是利用率至少要上专业工具NVIDIA用Nsight Computencu里面有SM Busy和SM Pipe Utilization统计。SM Busy看的是SM上有没有在执行指令Pipe Utilization看的具体是FMA、ALU、Tensor这些专门管道占用率。执行单元满不满看这个才对。AMD有rocprof和OmniTrace可以看Wavefront的发射率和VALU向量ALU利用率。Intel在oneAPI的生态里有VTune和XETLA分析工具可以看到EU的Active/Stalled状态。还有一个非常好用的理论工具叫Roofline模型它可以帮你判断一个Kernel到底是受执行单元算力限制Compute Bound还是受内存带宽限制Memory Bound。一个Kernel如果是Memory Bound那你把执行单元的优化做到天上去性能也上不来——因为执行单元一直在等数据根本没活干。用Roofline的办法很简单算出运算密度一次运算需要多少字节的访存再对比设备的算术强度拐点就知道瓶颈在哪一侧了。3.2 Occupancy与执行单元利用率之间的差距在GPU优化里经常听到Occupancy占用率这个指标指的是活跃Warp数量与硬件能容纳最大Warp数量的比例。很多人误以为Occupancy越高执行单元利用率就越高。这个说法在小规模矩阵计算时大致成立但在真实负载里不一定。因为Occupancy只说明“有多少Warp在SM上准备好了”不代表这些Warp的指令真的在持续往执行单元里灌。有时候Occupancy很高但大量Warp都在等显存数据回来执行单元一样是空转的。真正要盯紧的指标是“指令级流水线有没有断供”执行单元每周期能不能拿到可发射的指令。这取决于三件事Occupancy有没有足够多的线程在顶上、每线程的ILP指令级并行一个线程里能不能连续发射多条独立指令、以及访存延迟隐藏是否足够。我在做算子优化时习惯的做法是先用Nsight Compute跑一个SpeedOfLight报告看看SM的Issue利用率是多少。如果Issue利用率低优先查访存和线程调度如果Issue利用率高但Pipe Utilization不高那查具体是哪个管道在瓶颈是FMA堵了还是ALU堵了然后考虑调整指令混合。3.3 大模型推理和微调里的执行单元瓶颈结合大模型这一波实际应用来看现在跑LLM推理时很多人特别在意显存占用但其实执行单元的使用情况同样关键。推理阶段有一个特征batch size小的时候模型是Memory Bound执行单元的运算量不大大部分时间在等权重从显存搬过来batch size大了以后计算量上来了执行单元才会逐渐成为主要瓶颈。所以同一块GPU跑Llama这种模型你用小batch做在线推理nvidia-smi利用率可能看着只有20-30%但这是正常的不代表执行单元没在工作而是它的工作任务里访存占了大头。微调Fine-tuning阶段则完全不同。训练涉及反向传播和梯度计算浮点运算量比推理大好几倍这时候如果执行单元的数量不够或者精度设置不对性能瓶颈就会非常明显。我试过用FP16和INT8混合精度做微调执行单元利用率可以差出接近一倍因为INT8矩阵指令在同样的搬运带宽下吞吐更高。所以在大模型任务里判断执行单元是否够用不能只看显存是否爆要结合模型形态、batch size、精度三个变量去看。如果执行单元的瓶颈和显存带宽的错误匹配性能损失会非常隐蔽。3.4 从执行单元视角做优化怎么把人家的Kernel写快一个Kernel执行得慢如果已经排除了Memory Bound那剩下通常就是执行单元层面的问题。我按踩坑频率排序给几个最常见的原因和改法第一指令发射不够。这通常是因为Warp数量太少或者Occupancy被寄存器占用压下来了。解决办法是限制单个线程的寄存器数或者增加并行线程维度让调度器手里有更多可发射的Warp。第二分支分歧。这个在前面讲过对策主要是让线程粒度与数据排列匹配尽量让同一个Warp的线程走相同路径。遇到动态Shape或稀疏场景可以考虑特别处理比如把非规则数据单独打包成一个Kernel处理。第三向量化不够。GPU执行单元对128位或更宽的数据装载吞吐更高。我用CUDA的经验是尽量让每个线程处理4个连续的float比每个线程处理1个float在大部分访存密集场景都能直接提速因为访存指令更少执行单元的负载也更均衡。第四依赖链太长。如果每个线程内部的指令有很强的数据依赖流水线会频繁停顿。把计算拆开提高ILP让多条独立运算流水线同时跑是最后一步常用的优化手段。4. 常见误区和排查经验在真实场景里踩过的坑4.1 高GPU利用率不代表执行单元真的满载这个坑几乎每个人都会踩。我在排查过一个看起来GPU利用率为98%、但生成速度远低于预期的推理服务数据一出来就发现问题了虽然显卡Active时间很长但SM Pipe Utilization只有30%左右大量时间都在等数据拷贝和内存访问。这个案例说明报告里的利用率数字只能代表“设备在工作”解释不了“设备在执行计算”还是那句话要拆开看管道占用率才能定位真正的瓶颈。注意如果你在部署服务时遇到“GPU很忙但不出活”的情况先跑一下在线的Profile工具比如Nsight Compute或AMD的rocprof看看Pipe Utilization到底多少别急着加GPU实例。4.2 显存容量不能替代执行单元算力热词里反复出现“显存容量是测算推理还是训练用的”这个问题很典型。显存容量和计算能力是GPU两个完全不同的维度显存大只能说明能装下更大的模型或更大的batch不说明算得快。举个例子我用过一块老显卡显存24G装一个7B模型绰绰有余但生成token的速度比另一块显存只有16G但执行单元翻倍的新卡慢了一半还多。因为生成式模型的延迟主要由两个东西决定一个是权重能否全部放得下决定能不能跑另一个就是执行单元能做多少矩阵操作决定跑多快。所以规划资源时要分开看两个指标显存容量定的是模型规模上限执行单元数量和频率定的才是计算吞吐。租GPU时如果预算有限建议先看算力参数再看显存。4.3 GPU实例化/虚拟化到底减少了什么“GPU实例化到底减少的是什么”这个热词问得非常有水平。很多人以为GPU实例化就像虚拟机一样只是把一块卡的逻辑切成了几块其实不只是这样。以NVIDIA MIGMulti-Instance GPU为例MIG的特点是把一块物理GPU的执行单元、显存带宽、L2缓存完全隔离成多个实例。被切分的实例它使用的执行单元是物理上独立的子集所以不会互相干扰但代价是单个实例能用的执行单元数量变少了。也就是说实例化牺牲的是“整卡的微秒级突发算力”换来的是一段时间内稳定的隔离性能。对AI推理来说这种隔离很适合多租户场景。但如果你跑的负载是偶尔需要短时间吃满整卡算力的训练任务MIG可能会让训练时间延长不少因为执行单元资源被物理切死了无法借旁边的算力。4.4 执行单元相关的稳定性问题和“GPU Crash Dump”热词里有一个“GPU crash dump triggered”这个我在实际跑大规模分布式训练时遇到过好几次。这类问题的本质是执行单元在执行某条指令时发现内部状态不合法了触发一个异常保护机制。常见原因有同一块显存被多个Kernel同时写导致数据竞争、超频或供电不稳导致执行单元时序漂移、或者驱动对某些新算子的映射逻辑有bug。排查思路一般是这样先用CUDA的compute-sanitizer或者ASAN检查有没有未定义内存访问然后看日志里是哪条指令异常检查是不是驱动版本和CUDA Toolkit版本不匹配。这类稳定问题往往不是执行单元本身坏了而是它接触到的数据或指令不符合预期。如果一出现crash就怀疑硬件反而容易走弯路。4.5 常见问题速查表我整理了一张速查表都是实际运维和开发中经常会问到的问题只写最核心的判断思路和经验现象核心判断思路常见处理GPU利用率高但性能上不去可能Memory Bound或Pipe Waiting用Nsight Compute看Memory和Pipe占比显存够但生成速度慢执行单元算力不够或带宽不足换算力更强的卡或提升精度效率多个任务同时跑互相抢性能可能共享执行单元且无隔离用MIG或换多卡方案GPU Crash Dump数据竞争或驱动/算子映射问题检查越界访问和驱动版本显存很大但训练速度反而慢可能小batch导致执行单元喂不饱增大batch或提高调度并行度微调时执行单元利用率不高可能是流水线调度、精度、访存问题用profiler定位检查Mixed Precision这张表只是排查入口。真实的性能问题往往不是单一原因要辅以细致的数据采集和实验对比才能下结论。5. 最后再分享一点实际使用心得做GPU相关开发这几年我最大的体会是执行单元这个概念是连接硬件细节和软件优化的一座桥。很多CUDA性能问题、推理调度问题、资源规划问题如果回到“执行单元是怎么工作”的层面去看往往会变得很清楚。比如我最早调算子性能时总爱盯着利用率数字看后来养成了用profiler看管道占用率、用roofline判断瓶颈的习惯之后很多问题一眼就能定位方向。还有一个小技巧新拿到一块卡或者一个新的驱动版本先跑一段覆盖FP32、INT8、Tensor Core、访存密集这几类典型负载的基准脚本记录执行单元在不同负载下的表现。这样将来遇到性能回归或者换个型号的卡你能很快判断是新环境问题还是负载本身变化了。这个习惯帮我省下过不少排查时间也推荐给大家。