Occupancy 与 Launch 配置:藏住延迟

Lesson 06 · CUDA 性能调优 · 回收"延迟瓶颈"

前五节把访存优化讲透了。但还记得 第 2 节那个最易看漏的诊断吗——访存和计算利用率两个都低? 那通常不是访存或计算的问题,而是并行度不够、延迟没被藏住。这一节回收它,讲 GPU 怎么靠 occupancy 把延迟藏起来。

本节目标(一个可带走的胜利): 你能解释 occupancy 是什么、它如何藏延迟、 受哪三种资源限制,并理解一条反直觉的铁律——occupancy 不是越高越好

1. GPU 藏延迟的方式:换 warp,不是等

访问全局内存要 ~400-800 周期(第 4 节的金字塔)。 CPU 遇到这种停顿会用乱序执行、大缓存硬扛。GPU 的哲学完全不同:不等,直接切到另一个 warp 干活。[1]

一个 warp 因为等内存停住(stall),调度器立刻挑另一个就绪的 warp 发射指令。 只要随时有就绪的 warp,SM 就一直在干活,内存延迟被"藏"在了其他 warp 的计算后面。

所以藏延迟的前提是:SM 上得有足够多的 warp 可供切换。warp 太少,大家都在等内存、没人能补上, SM 就空转——这正是延迟瓶颈:两个利用率都低。

2. Occupancy 的定义

Occupancy = 每个 SM 上活跃 warp 数 ÷ 该 SM 能容纳的最大 warp 数[1]
就是"这个 SM 的并行席位坐满了几成"。A100 每 SM 最多 64 个 warp(= 2048 线程)。

它衡量的是调度器有多少候选 warp 可切换。occupancy 越高,可切换的 warp 越多,越能藏住延迟。 但"坐满席位"靠的是资源——而资源是有限的。

3. 限制 occupancy 的三种资源

一个 SM 的资源是固定的,三种资源各自会成为"先用完的那个",从而卡住 occupancy:[1]

资源怎么卡住 occupancyNsight 字段
寄存器/线程每线程用太多寄存器 → SM 寄存器池装不下那么多线程limit due to registers
shared memory/block每 block 用太多 shared → 同时驻留的 block 变少limit due to shared mem
block 大小block 太小 → 撞上"每 SM 最多 N 个 block"的上限,warp 席位坐不满limit due to block size

典型场景:你的 kernel 每线程要 64 个寄存器,而 SM 寄存器池只够 1024 个线程同时用 64 个—— 那 occupancy 就被锁在 50%(1024/2048),与 block 怎么配无关。这是"寄存器压力"限制 occupancy。

4. 反直觉铁律:不是越高越好

新手最大的误区是把 occupancy 当成"分数",拼命冲 100%。官方明确说:occupancy 高不一定快。[1]

原因有二:

正确心态: occupancy 是藏延迟的手段之一,不是目标。 只在"延迟瓶颈 + occupancy 偏低"时才去提它; 若已经能藏住延迟(利用率高),再堆 occupancy 没有意义。先测量,再决定。

但有一条是确定的:occupancy 过低一定伤性能——候选 warp 太少,延迟藏不住。[1] 所以它是个"下限要保证、上限别强求"的指标。

5. Launch 配置的实用建议

kernel 启动时你要定 <<<gridDim, blockDim>>>。落地经验:

选择建议
block 大小32 的倍数(warp 对齐),常用 128 / 256;别太小(撞 block 数上限)
grid 大小足够多 block 覆盖全部数据,且 ≥ SM 数量,让每个 SM 都有活干
不确定时cudaOccupancyMaxPotentialBlockSize() 让运行时推荐一个 block 大小[1]
// 让 CUDA 替你算出能最大化 occupancy 的 block 大小
int minGrid, blockSize;
cudaOccupancyMaxPotentialBlockSize(&minGrid, &blockSize, myKernel, 0, 0);
// 再据此算 grid:覆盖 n 个元素
int grid = (n + blockSize - 1) / blockSize;
myKernel<<>>(...);
Nsight Compute 的 Occupancy 段会同时给你理论 occupancy(资源算出的上限)和 实测 achieved occupancy。两者差距大,通常是负载不均(尾部效应、block 数不够)。[1]

6. 练习:综合判断

混合了 occupancy 与前几节的诊断。点选项看反馈。

💬 随时问我。 寄存器数怎么查(-Xptxas -v)?ILP 具体怎么写? 理论 vs 实测 occupancy 差很多怎么办?什么是尾部效应(tail effect)?我是你的老师,尽管问。

主源推荐(本节精读)

📘 CUDA C++ Best Practices Guide — Occupancy ——讲清 occupancy 定义、计算与"不是越高越好"。配合 Nsight Compute 的 Occupancy 段实践。