memoryBarrierShared vs groupMemoryBarrier in GLSL

In OpenGL Shading Language (GLSL) compute shaders, managing the order of read and write operations across threads is critical for avoiding race conditions. While both memoryBarrierShared() and groupMemoryBarrier() are memory ordering intrinsics designed for use within compute workgroups, they differ in the types of memory they affect: memoryBarrierShared() synchronizes accesses specifically to shared memory variables, whereas groupMemoryBarrier() synchronizes accesses across all memory resources—including shared memory, shader storage buffer objects (SSBOs), and image variables—relative to the calling workgroup.

Execution Barriers vs. Memory Barriers

Before comparing the two functions, it is important to distinguish memory barriers from execution barriers. Functions like memoryBarrierShared() and groupMemoryBarrier() only ensure that memory transactions issued before the call are committed and visible before operations issued after the call. They do not halt thread execution or synchronize the progress of threads.

To pause execution until all invocations in the workgroup reach the same point in the code, GLSL provides the execution barrier barrier(). Consequently, memory barriers are often paired with barrier() when sharing data between different invocations.

What is memoryBarrierShared()?

The memoryBarrierShared() function enforces ordering exclusively for variables declared with the shared storage qualifier.

When a thread calls memoryBarrierShared():

  • Any writes to shared variables issued prior to the barrier are guaranteed to be committed before any memory transactions issued after the barrier.
  • It does not guarantee ordering for writes to buffers (SSBOs) or images (image2D, image3D, etc.).

This narrow scope makes memoryBarrierShared() lighter weight than broader barrier functions, as the GPU only needs to flush the local on-chip shared memory cache or registers associated with workgroup data.

What is groupMemoryBarrier()?

The groupMemoryBarrier() function provides a broader synchronization scope. It orders memory transactions across all memory types accessed by the shader that could be observed by other invocations in the same workgroup.

When a thread calls groupMemoryBarrier():

  • Writes to shared memory variables are ordered.
  • Writes to shader storage buffer objects (SSBOs via buffer blocks) are ordered.
  • Writes to images (via imageStore) are ordered.

Effectively, groupMemoryBarrier() acts as a combined barrier for memoryBarrierShared(), memoryBarrierBuffer(), and memoryBarrierImage(), ensuring that any modification to global or shared data made by the thread is ordered relative to subsequent operations.

Key Differences

  • Scope of Memory: memoryBarrierShared() strictly affects workgroup shared variables. groupMemoryBarrier() affects shared variables, SSBOs, and image loads/stores simultaneously.
  • Performance: memoryBarrierShared() typically incurs lower overhead because shared memory is local to the compute unit. groupMemoryBarrier() may force the GPU to wait for global memory controllers and L2 caches to resolve writes to global buffers and textures.
  • Intended Use Case: Use memoryBarrierShared() when an algorithm only shares intermediate data between workgroup threads using local shared arrays (such as in local reductions or blur filters). Use groupMemoryBarrier() when an algorithm writes to both shared variables and global buffers or images, and subsequent steps depend on all of those writes being committed.