GLSL subgroupBarrier Execution and Memory Sync
In GLSL compute and graphics pipelines,
subgroupBarrier() provides fine-grained synchronization
exclusively among invocations within the same subgroup (warp or
wavefront). This article details how subgroupBarrier()
coordinates program counter execution and enforces memory ordering
across active invocations, highlighting the difference between
subgroup-level coordination and standard workgroup barriers, as well as
the rules governing divergent control flow.
Instruction Execution Synchronization
A subgroup represents a small collection of invocations—typically 16,
32, or 64 threads—executing concurrently on SIMD or SIMT hardware. When
an invocation reaches subgroupBarrier(), it halts execution
until every active invocation within that subgroup arrives at the same
barrier instruction.
This mechanism acts as an execution fence. It guarantees that no invocation in the subgroup can advance to subsequent instructions until all peers have finished their preceding instructions, preventing execution divergence where faster threads outpace dependent calculations.
Memory Ordering and Visibility
In modern GPUs, memory operations are non-blocking and out-of-order by default. Simply synchronizing execution is insufficient when invocations exchange data via buffer storage, shared memory, or images.
subgroupBarrier() acts as a combined execution and
memory barrier. It enforces that:
- Write Visibility: All memory writes (to buffer variables, shared memory, or storage images) issued by any invocation in the subgroup prior to the barrier become visible to all invocations in the subgroup before any execution resumes past the barrier.
- Access Ordering: Memory reads and writes placed
after
subgroupBarrier()cannot be reordered by the compiler or the GPU hardware to execute before the barrier. Similarly, operations before the barrier cannot be deferred past it.
While GLSL also offers targeted functions like
subgroupMemoryBarrierBuffer() or
subgroupMemoryBarrierShared() to restrict memory
synchronization to a single storage type without an execution stall,
subgroupBarrier() enforces a universal memory barrier
across all memory classes in conjunction with the execution halt.
Differences from Workgroup Barriers
Standard GLSL barrier() enforces synchronization across
an entire workgroup (local size), which often spans multiple subgroups.
Synchronizing an entire workgroup involves hardware-wide communication
across multiple compute units, incurring measurable latency.
By contrast, subgroupBarrier() operates solely within
the physical execution unit. Because subgroup lanes reside on the same
execution core, the hardware can resolve the barrier rapidly—often via a
single machine instruction or hardware scoreboard—avoiding global
pipeline stalls.
Divergence and Uniformity Rules
Like all execution barriers, subgroupBarrier() must be
executed in uniform control flow relative to the subgroup. If a branch
diverges within a single subgroup—meaning some invocations take a path
that hits the barrier while others do not—the behavior is undefined and
often causes GPU hangs or deadlocks. To safely execute
subgroupBarrier(), any surrounding conditional statement
must evaluate identically across every active invocation in that
subgroup.