Warp与寄存器
CPU 和 GPU 在管理寄存器时有本质的区别。当 CPU 切换线程时,它会把当前线程的寄存器状态保存到内存中(Context Switch 开销很大)。而 GPU 为了追求极致的并行,它的上下文切换是零开销的
这是因为 GPU 会把庞大的物理寄存器堆分成很多份,永远分配给当前驻留在 SM 上的每一个 Warp(DXC编译器分析shader的ALU复杂度,根据复杂度分配寄存器个数)。当Warp A 等待贴图加载卡住时,硬件调度器(Scheduler)切换到 Warp B,不需要搬运任何数据,调度器只需把指针指向 Warp B
而CPU之所以开销很大,是因为CPU 的核心极其强大,但它的物理寄存器非常少,当 CPU 要从线程 A 切换到线程 B 时,它必须做一件非常耗时的事情:把线程 A 当前寄存器里的所有数据,打包保存到内存(L1 Cache 或 RAM)里;然后再把线程 B 之前保存的数据,从内存里读回到寄存器中
- Warp: GPU 执行的基本单位,由 32 个线程组成
- 寄存器:线程使用寄存器来执行ALU
Warp、寄存器、共享内存会共同影响SM的占用率
Top-Level Throughput
Top-Level Throughput 代表利用率(Utilization),反映 GPU 各个硬件的繁忙程度
在Top-Level Throughput还能看到其他性能指标

可以将这些分成三大类:
- 核心计算与调度(SM, RTCORE, Front End):反映了 GPU 的“大脑”和专用加速器的繁忙程度
- Front End:负责指令解码、任务分发
如果Front End高,说明 CPU 提交的小命令(Draw Call )太多
-
SM:通用计算核心
如果 SM Throughput 高,通常是 计算受限(Compute Bound),说明有复杂、冗余的数学计算(如sin、cos)
-
RTCORE:光追核心,处理 BVH 遍历和射线/三角面相交测试
若RTCORE 高但 SM 不高,说明光追的求交是瓶颈,需要优化 BVH 结构或减少射线发射密度
若RTCORE 高且SM Instruction Throughput也高,说明很健康
若RTCORE 高Wait on Memory 或 Dependency Stall高,说明硬件在空等数据返回
- 存储与数据交互(VRAM, L2, PCIe)
-
VRAM:显存带宽
如果 VRAM 高,说明读写显存非常频繁
-
L2:二级缓存, 是SM、纹理单元和显存之间的缓冲区
如果 L2 高但 VRAM 低,说明缓存命中率高,是健康的。如果 L2 和 VRAM 齐高,说明 L2 无法承载当前的数据吞吐,发生了大量的缓存失效
-
PCIe:总线带宽,CPU 和 GPU 之间的数据交换
如果这里高,检查是否在每一帧都在上传巨大的顶点 Buffer 或纹理,或者频繁从 GPU
Readback数据
- 固定功能管线(World Pipe, Screen Pipe)
-
World Pipe:顶点着色器、曲面细分、几何着色器
如果这里高,说明三角形太多或几何负载过重
-
Screen Pipe:光栅化、像素着色器、混合
如果这里高且 ROP(后端)也高,说明 Overdraw(过度绘制) 严重,或者填充率(Fill-rate)达到了上限
SM Warp Occupancy
表示SM 中实际活跃的 Warp 数量与 SM 理论最大支持 Warp 数量的比值,是衡量 SM 运行效率的最关键指标
- 占用率低:通常意味着 SM 内部资源受限。可能是每个线程使用的 Register(寄存器) 太多,或者是 Shared Memory 分配量过大,导致 SM 无法容纳更多的 Warp
SM 里面驻留的 Warp 少一点不行吗?只要算得快不就好了?
在前文提到过,GPU 零开销的上下文切换,正是仰仗Warp,当warp少了极有可能出现所有warp都在等待的情况,而从导致GPU也需要等待,无法做到延迟掩盖
-
占用率高但性能差:说明虽然 Warp 很多,但它们都在等待(Stall),比如在等显存读取数据(Memory Latency)
-
如果是因为寄存器受限,尝试优化着色器逻辑以减少局部变量;如果是为了掩盖延迟,保持适当的高占用率是有益的
Unallocated Warps in Active SMs
虽然 SM 已经在运行(Active),但由于某种原因,它还有多少剩余的“坑位”没有被填满,反映了 GPU 的“闲置潜力”
- 数值高:说明 GPU 并没有满载。原因可能是 Global Dispatch 限制(比如当前 Dispatch 的总线程数不够多,分不到所有 SM 上)或者是 前端分发(Front End)速度 跟不上
- 与 SM WarpOccupancy 结合:如果 Occupancy 很低且 Unallocated 很高,说明是任务总量太少(比如渲染一个很小的图标);如果 Occupancy 已经触顶,Unallocated 应该接近于零
Pixel Warps、Vertex/Tess/Geometry Warps
展示了当前 GPU 正在忙着处理哪种类型的任务
- Pixel Warps
如果 Pixel Warps 占比极大,说明当前是 后端的像素着色瓶颈。检查是否存在严重的 Overdraw(过度绘制)或极其复杂的材质计算
-
Vertex / Tess / Geometry Warps
如果 Vertex Warps 异常高,检查是否模型面数过多或没做 LOD。
如果 Tessellation/Geometry Warps 很高,通常意味着曲面细分级别过高,产生了大量微小三角形,导致严重的硬件浪费
通用策略
| 现象 | 瓶颈 | 优化 |
|---|---|---|
| CS Warp Occupancy 极低 | 资源受限 | 减少 Shader 寄存器使用或 Shared Memory 大小 |
| Unallocated Warps 极高 | 任务量不足 | 增加任务并行度,或合并小型 Draw Call |
| Pixel Warps >> Vertex Warps | 像素受限 | 优化材质 Shader,检查不透明物体的渲染顺序 |
| Vertex Warps 占满总线 | 几何受限 | 使用 Mesh Shader 或 LOD |
存储与带宽
它们有PCIe Bandwidth、PCIe Incoming BAR Accesses、VRAM Bandwidth、L2 Bandwidth per Source Unit、L2 Sector Hit-Rate (Total)、L2 Sector Hit-Rate from L1TEX
它们反映了数据在 CPU、显存和各级缓存之间的流动效率
VRAM Bandwidth
- 含义:物理(显卡)显存(Video RAM)的读写带宽
- 分析
- 对比显卡的理论带宽。如果达到 70%-80% 以上,说明是显存带宽受限
PCIe Bandwidth
- 含义:表示CPU 与 GPU 之间通过 PCIe 总线交换数据的带宽
- 分析
- 高 PCIe + 低 VRAM:说明瓶颈在总线。可能每帧都在上传大量的动态顶点数据或更新庞大的CBuffer
- 低 PCIe + 高 VRAM:说明数据交换主要发生在 GPU 内部。这是现代渲染器的常态,说明瓶颈在显存带宽
PCIe Incoming BAR Accesses
- 含义:CPU 通过 Base Address Register 直接访问 GPU 内存的次数
- 分析
- 高频访问可能意味着CPU 正频繁地修改GPU中的小块数据,这通常比一次性大批量传输更低效
L2 Bandwidth per Source Unit
- 含义:每个请求源(如 SM、显示引擎)访问 L2 缓存的带宽
- 分析
- VRAM Bandwidth、L2 Bandwidth per Source Unit都很高,说明显存读写任务多
- L2 Bandwidth per Source Unit达到极限,即使VRAM Bandwidth没有,会产生瓶颈
L2 Sector Hit-Rate (Total)
- 含义:L2 缓存的总命中率
- 分析
- 若VRAM Bandwidth低,L2 Sector Hit-Rate高,说明L2利用率高,且越高越好,效率很高
- 若VRAM Bandwidth高,L2 Sector Hit-Rate低,说明存在缓存抖动,利用率低,效率很低
通用策略
| 现象 | 瓶颈 | 优化 |
|---|---|---|
| VRAM BW 高 + L2 Hit-Rate 低 | 带宽受限(Bandwidth Bound) | 压缩纹理格式 ,减少 Render Target ,使用Z-Prepass |
| VRAM BW 低 + L2 Hit-Rate 低 + Latency 高 | 内存延迟受限 (Latency Bound) | 优化采样点分布,减少随机跳转访问,使用更紧凑的数据结构 |
| PCIe BW 持续高位 | 总线受限 (PCIe Bound) | 使用 GPU Driven,减少 CPU 端的资源上传频率 |
指令执行与线程效率
它们有SM Instruction Throughput、Active Threads Per Warp、Warps Launched、Compute In Flight
它们反映了SM(流式多处理器)处理代码的速度
SM Instruction Throughput
- 含义:SM 实际执行指令的吞吐量,衡量 GPU 到底有没有“在干活”的核心指标
- 分析
- 如果很低,说明 SM 在等待或者没活干(需要看Avg Warp Latency 、Occupancy )
- 若 Throughput 低,Avg Warp Latency高,ALU被延迟卡住
- 若Throughput 低,Occupancy 低,任务量不足
- 如果极高,说明GPU的ALU满载,需要优化数学公式
- 波动较大,说明存在等待
Active Threads Per Warp
- 含义:每个 Warp 中实际活跃(未被掩码屏蔽)的线程数(最大 32)
- 分析
- 如果该值远低于 32,说明存在 Branch Divergence(分支分歧),即
if/else导致线程在串行执行
- 如果该值远低于 32,说明存在 Branch Divergence(分支分歧),即
Warps Launched
- 含义:创建新 Warp 的频率
Compute In Flight
- 含义:当前 GPU 执行的计算任务总量
通用策略
| 现象 | 瓶颈 | 优化 |
|---|---|---|
| SM Instruction 低 + Active Threads < 32 | 分支分歧受限 | 减少分支深度 |
| SM Instruction 低 + SM Warps Stalled (Math) | 算术指令延迟 | 检查是否有高延迟指令(如 long long 运算或复杂的 exp/log),尝试降低精度 |
| SM Instruction 高 + SM Warp Occupancy 低 | 资源受限但高效 | 虽然 Shader 跑得快,但因为寄存器占用多,无法并行。尝试减少局部变量 |
| SM Instruction 极低 + Compute In Flight 极低 | 任务量不足 | 将小的 Dispatch 合并,或者在计算时增大线程组(Thread Group)的大小 |
占用率与延迟
表示硬件资源的调度情况
SM Warp Occupancy
- 含义:SM 实际承载的 Warp 数量占硬件最大理论值的百分比
SM Warp Occupancy (sampled)
- 含义: 采样得到的瞬时占用率
- 分析
- 高占用率有助于隐藏延迟。如果占用率低,通常是因为 Shader 使用了太多Register或Shared Memory
Avg Warp Latency
- 含义:一个 Warp 从发射到完成的平均耗时
- 分析
- 延迟高并不一定意味着慢,只要 Occupancy 够高,GPU 就能在等待延迟时切换到其他 Warp 工作
通用策略
| 现象 | 瓶颈 | 优化 |
|---|---|---|
| 高 Latency + 高 Occupancy | 健康。虽然延迟大,但有足够的任务在跑,掩盖了等待 | 如果性能仍不达标,考虑减少指令总量 |
| 高 Latency + 低 Occupancy | 瓶颈。SM 在空转,因为没活干,又在等内存 | 最高优先级优化。减少寄存器占用(提占用率)或优化缓存命中 |
| 低 Latency + 低 Occupancy | GPU没任务 | 增加工作负载,合并小的 Dispatch |
| 低 Latency + 高 Occupancy | 完美。极速计算且满载 | 无需优化,已达硬件上限 |
停顿原因
SM Warps Stalled at Issue Stage
- 含义:Warp 已经准备好,但在“指令发射”阶段停顿
-
分析
可能有多个原因,如:
- Wait on Memory:Warp 正在执行
Load操作,数据还没从缓存或显存里回来 - Wait on Math:ALU瓶颈,即使 Warp 准备好了,也得排队等计算单元
- Wait on Barrier/Sync:通常出现在 Compute Shader 中,Warp 在等
GroupMemoryBarrierWithGroupSync()
- Wait on Memory:Warp 正在执行
Warp Can't Launch
- 含义:新的 Warp 无法被塞进 SM
-
分析
可能有多个原因,如:
- Register Limited:Shader 声明了太多局部变量。每个 SM 的寄存器总量是固定的(例如 64KB),如果每个 Warp 占用太多,能塞进去的 Warp 总数就会下降
- Shared Memory Limited:申请了大量的 groupshared 内存,SM 会因为存不下更多 Thread Group 而拒绝新的 Warp
- Max Blocks/Warps Per SM:达到了硬件设计的上限(如某些架构每个 SM 最多 32 个 Blocks)
TRAM Allocation Stalled / ISBE Allocation Stalled
- 含义:GPU 内部分配的缓冲区溢出
-
TRAM Allocation Stalled (Texture RAM): GPU 内部处理纹理/缓存请求的一个中间缓冲区
原因:
- 在短时间内发起了大量的、非连续的、且带有复杂地址计算的纹理采样请求,TRAM 可能会被占满
- ISBE Allocation Stalled (Instruction Scoreboard Event):ISBE 是硬件用来追踪“指令依赖关系”的记分板
原因:
- Shader 指令链非常长,且相互之间有复杂的依赖关系(例如:A 的结果给 B,B 给 C,C 再给 D... 同时还有几十个这样的链并行),记分板的条目被耗尽
- 分析
- 如果这两个指标高,通常意味着着色器逻辑极其复杂,超出了硬件内部管理小块临时数据的能力
通用策略
| 现象 | 瓶颈 | 优化 |
|---|---|---|
| Wait on Memory 高 + L2 Hit-Rate 低 + VRAM BW 低+Avg Warp Latency 极高 | 内存延迟受限。访问地址太随机 | 提升局部性:缩小采样半径或使用更紧凑的缓存友好布局 预取数据:利用groupshared 预加载块数据 |
| SM Instruction Throughput低+ Unallocated Warps高+SM Warp Occupancy低+Warp Can't Launch高 | 寄存器压力受限。局部变量太多,导致 SM 物理空间虽有,但塞不下更多 Warp,无法掩盖任何执行延迟 | 减少局部变量定义,复用变量寄存器 将一个超长 Shader 拆分为两个 Pass 使用 [branch] 减少展开后的寄存器占用 |
| L2 Bandwidth per Source Unit高+VRAM Bandwidth高+Wait on Memory 与 L2 Hit-Rate 呈现负相关 | 显存带宽受限 | 压缩纹理 利用共享内存减少重复读取 |
| SM Instruction Throughput高+Math Pipe Busy 高+Stalls at Issue Stage 主要是 Wait on Math | ALU满载 | 算法简化 精度折衷 LUT |
快速分析
- 看 Top-Level Throughput 视图中哪个单元的柱状图最高:确定是SM 还是 Memory 忙
- SM > 80%:计算受限 (Math/Shader Bound)。Shader 代码太重
-
VRAM/L2 > 70%:带宽受限 (Memory Bound)。在疯狂搬运数据
-
World Pipe 高:几何受限 (Geometry Bound)。三角形太多或顶点 Shader 太慢
-
RT Core 高:光追受限 (RT Bound)。BVH 遍历太久或射线太多
-
Front End 高:调度受限 (Command/API Bound)。Draw Call 太碎
- 核心效率交叉验证
-
占用率 (Occupancy) 与 停顿 (Stalls)
- 高占用率 + 高吞吐量:完美状态。硬件正在全速跑
- 低占用率 + 高延迟 (Latency):危险状态。SM 因为资源压力塞不下更多 Warp,导致无法掩盖内存延迟,产生大量空转
- 带宽 (Bandwidth) 与 命中率 (Hit-Rate)
- 高 VRAM 带宽 + 低 L2 命中率:缓存抖动
- 低 VRAM 带宽 + 低 L2 命中率 + 高延迟:延迟瓶颈。虽然没塞满路,但每次请求都很慢
- 寻找“性能杀手”:当发现性能不达标且 SM 忙碌时,扫视 Stall Reasons 面积图
| 看到哪个面积大? | 快速结论 |
|---|---|
| Wait on Memory | 都在等数据 |
| Warp Can't Launch | Shader 太胖,临时变量多 |
| Wait on Math | 计算单元排队 |
| Wait on Dependency | 指令链太长 |
P3方法论
Nvidia有期视频分享过一个方法,找到距离硬件理论极限(光速,Speed of Light / SOL)最近的那个指标,那就是当前的唯一瓶颈
为什么这么说呢?在解决最高瓶颈之前,优化其他较低 SOL 的部分,对整体帧率的提升是 0
P3将复杂的 GPU 工作负载归类为三种基本的瓶颈状态:
- 算力瓶颈
- 表现: SM的 SOL 极高(>80%),而 Memory SOL 相对较低。GPU 的计算单元正在满负荷进行数学运算
- 优化策略
- 精简指令
- 降精度
- 消灭无用计算
- 带宽/内存瓶颈
- 表现: VRAM 或 L2 Cache 的 SOL 极高(>80%),而 SM SOL 偏低。计算单元在空转,等待数据从显存中搬运过来
-
优化策略
- 压缩数据:使用更紧凑的顶点格式(如 16-bit 浮点数存 UV),使用 ASTC/BC 纹理压缩
-
利用共享内存:将频繁跨线程读取的全局数据预加载到 Shared Memory
-
优化缓存局部性:改变 Compute Shader 的线程组 Swizzling 方式,确保同一 Warp 内的线程读取相邻的内存地址
现代 GPU 的核心执行单元是 Warp(在 NVIDIA 架构中是 32 个线程并排执行),当一个 Warp 里的 32 个线程去读取显存时,GPU 并不是分 32 次去读,而是希望这 32 个线程请求的内存地址是连续的。如果不能连续,会发起内存请求,从而导致延迟
如何手动保证是连续的呢?这就需要先了解Compute Shader是如何执行的,当我们写一个全屏后处理 Compute Shader 时,通常会这样调用:
Dispatch(屏幕宽度 / 8, 屏幕高度 / 8, 1),GPU 硬件分发器默认“逐行扫描”。它会先执行第一行的所有 Thread Group(从左到右),然后再执行第二行,当 GPU 处理第一排的像素时,它会把相关的数据加载到 L2 Cache ,当处理完第一行,准备开始处理第二行的最左侧像素时,**之前缓存的第一行的数据早就因为L2容量不够,被踢出 L2 Cache **。由于后效常常需要读取上下左右的像素,这会导致海量的 Cache Miss优化:在 Shader 内部通过数学公式篡改线程组的 ID (
SV_GroupID),将线性的执行顺序,强制映射成“分块(Tile)”执行
- 延迟瓶颈
- 表现:SM 和 Memory 的 SOL 都不高(例如都在 40% 左右)。这意味着 GPU 既没被算力卡住,也没被带宽卡住,而是因为某些原因停滞(Stall)
- 优化策略
- 寄存器压力过大:Shader 中使用了过多的临时变量,导致 Warp 占用率极低,无法掩盖固有的内存延迟
- 气泡:
ResourceBarrier插入不当 - 分支发散




Comments | NOTHING