阶段 4 · 计算基础18 分钟
原子操作与归约:安全汇总并行结果
atomicAdd、直方图与竞争条件
大量 invocation 并行统计随机数据直方图,用 atomicAdd 避免多个线程覆盖同一计数。
LEARNING GOALS
先明确本课目标
1
声明 atomic<u32>
2
使用 atomicAdd
3
理解竞争条件与分层归约
MENTAL MODEL · 再建立心智模型
普通读-改-写不是一个不可分割操作;原子操作把竞争更新串成安全结果,但热点地址会限制并行度。
MINIMAL EXAMPLE
很多线程同时给同一个计数器加一会发生什么?
普通的读—改—写可能互相覆盖,形成 Race Condition。Atomic 操作保证一次修改不可分割,使并发 invocation 对共享整数的更新不会丢失。
wgsl@group(0) @binding(0)
var<storage, read_write> bins: array<atomic<u32>>;
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) id: vec3u) {
let bin = values[id.x] % 8u;
atomicAdd(&bins[bin], 1u);
}- atomic 类型 WGSL 原子值使用 atomic<u32> 或 atomic<i32>,不能把普通 u32 当原子操作。
- 地址传入 atomicAdd 接收目标原子变量的引用与增量。
- 正确但可能争用 大量 invocation 命中同一地址时会序列化,分层归约通常更快。
所有 bin 的计数总和等于输入元素数量,不再因并发覆盖而随机变小。
OPTIONAL · 按需查阅完整术语表与概念边界遇到陌生术语时再展开,不打断主线实验。
展开⌄
LIVE EXPERIMENT · 先获得反馈
边改边运行
浏览器加载后会激活代码编辑器、独立运行环境与实时输出。
本课挑战
实现并行最大值与平均值归约。
回到原理,解释刚才发生了什么
先用结果建立反馈,再按运行流程、核心原理和知识关系逐层深入。
CORE IDEA
核心讲解
何时需要原子
多个 invocation 可能同时更新同一地址时,普通加法会丢失更新。原子操作保证每次修改不可分割。
原子不是免费的
所有线程争用少数计数器会序列化。常见优化是先在 workgroup 内局部统计,再合并到全局。
OPTIONAL · 迁移时查阅WebGPU 与 WebGL2 对照主线实验完成后,再用完整映射处理跨平台或跨 API 迁移。
展开⌄
WebGL2 没有通用 shader 原子与共享内存归约;WebGPU 可原生表达并发写冲突。
WebGL
通常用 blending、多 Pass 金字塔或 CPU 汇总绕开冲突,算法形状受渲染目标限制。
WebGPU
atomic 类型、workgroup memory 和 barrier 能构建直方图与分层归约。
不要原样搬运多 Pass 纹理归约;先判断是否可用组内归约减少全局原子竞争。