13-compute-shaders
第 13 篇:计算着色器(Compute Shaders)
原文:Part 13
一句话定位
计算着色器脱离固定图形数据流,直接调度线程组;它复用 GPU 的宽执行、寄存器、缓存和内存系统,并显式暴露组内共享存储与同步。
调度层级
Dispatch
└─ 多个 Workgroup / Thread Group ← 组之间通常无执行顺序保证
├─ 多个 Invocation / Thread ← 每个有 ID 与私有寄存器
└─ 被硬件切成一个或多个 Wave/Warp ← 真正的执行/调度批次
“GPU thread”不是 OS 线程:通常没有独立程序计数器和栈,也不被逐线程独立调度。硬件以 wave/warp 运行同一指令,用掩码表达分支。API 定义的 workgroup 大小与硬件 wave 宽度不是同一概念;一个 workgroup 可包含多个 wave。
输入从哪里来
CS 的固有输入主要是 dispatch/workgroup/local/global ID。真实数据由 shader 根据 ID 从 buffer、image、constant/uniform 或 descriptor 指向的资源中读取。这种模式让同一程序可覆盖规则数据域,也把地址计算和边界检查责任交给程序。
组内共享存储与屏障
workgroup/shared/local memory 是同一组 invocation 可见的低延迟片上存储,适合 tile、归约、直方图和数据复用。它容量有限,并会影响同时驻留的 workgroup 数量。
屏障要拆成两个问题:
- 执行同步:组内参与者是否都到达某一点?
- 内存可见性:某类写入何时、对哪个作用域的读者可见?
只解决其中一个,程序仍可能错误。组内 barrier 通常不能同步不同 workgroup;跨组依赖一般要拆成多个 dispatch,并通过 API barrier/queue dependency 建立全局顺序,或使用目标 API 明确定义的原子与前进保证。
SIMT 效率
- 分支分歧会让同一 wave 的路径串行化;
- 全局内存访问若能合并为连续事务,带宽利用更好;
- 共享存储可减少重复全局访问,但可能有 bank conflict;
- 寄存器、共享存储和 workgroup 大小共同限制 occupancy;
- 更多 occupancy 只是在需要隐藏延迟时有益,不是独立性能目标。
与图形流水线的关系
CS 与 VS/PS 往往共享通用执行核心,但输入/输出协议不同。图形阶段由固定功能单元自动产生顶点、quad 和插值输入;CS 直接由 dispatch 产生逻辑线程,并显式读写资源。计算结果若随后被图形管线读取,驱动必须安排 cache 可见性、布局转换和执行依赖。
术语对照
| 通用称呼 | Vulkan/OpenGL | D3D | 中文理解 |
|---|---|---|---|
| workgroup | workgroup | thread group | 共享局部存储与组内 barrier 的线程集合 |
| invocation | invocation | thread | 逻辑执行实例 |
| subgroup | subgroup | wave/warp(厂商术语) | 硬件近似锁步执行子集 |
| shared memory | shared/workgroup memory | groupshared | 组内共享片上存储 |
| barrier | barrier | GroupMemoryBarrier 等 | 执行和/或内存顺序原语 |
| atomic | atomic operation | interlocked operation | 对共享位置的不可分割更新 |
面向 UMD/KMD 的阅读提示
- 编译器:追踪 local size、subgroup lowering、shared-memory layout、barrier 和 atomic 的 ISA 映射。
- UMD:追踪 dispatch packet、descriptor、资源访问标记,以及 compute→graphics / graphics→compute barrier。
- KMD:观察 compute queue 是否是独立硬件队列、如何与 graphics queue 建立 fence/semaphore 依赖、抢占粒度是什么。
- 调试 hang 时优先检查:非一致 barrier、越界地址、无限自旋、跨 workgroup 假设,以及等待的信号是否可能永远不产生。
自测与实验
- 为什么把 workgroup size 设得越大不一定越快?
barrier()前后若只有部分 invocation 进入该分支,会发生什么?- 写 storage buffer 后立即把它作为 vertex buffer 使用,需要哪类执行与内存依赖?
- 比较 AoS 与 SoA 访问,观察内存合并和 cache 行利用差异。
本篇总结
计算着色器把 GPU 的批量并行能力直接交给程序员,也把数据布局、同步、负载均衡和资源占用的责任一并交了出来。

浙公网安备 33010602011771号