Understanding GLSL subgroupMul for Lane Products

This article provides an overview of the subgroupMul function in GLSL, exploring its purpose, execution model, and performance benefits in GPU shader programming. The subgroupMul function is an arithmetic subgroup operation designed to compute the product of values across all active invocations—often referred to as lanes—within a single subgroup. By executing directly at the hardware execution-unit level, this built-in function enables fast cross-lane data reduction without requiring shared memory or barrier synchronization.

Purpose and Execution Model

In modern GPU architectures, compute threads are grouped into hardware-level SIMD units known as subgroups (or warps/wavefronts). The subgroupMul function is part of the GL_KHR_shader_subgroup_arithmetic extension, which allows lanes within the same subgroup to share and reduce data directly through hardware registers.

When a shader invocation executes subgroupMul(value), the following process occurs:

  1. Input Collection: The GPU gathers the specified value parameter from every currently active invocation in the subgroup.
  2. Product Calculation: The values are multiplied together in parallel across the execution unit.
  3. Result Broadcast: The final calculated product is returned to all active invocations within that subgroup.

If an invocation is inactive—such as when divergent branching disables certain lanes—it does not contribute to the multiplication, and its state does not affect the final result.

Supported Types and Behavior

The subgroupMul function supports multiple data types, including:

  • 32-bit and 64-bit floating-point scalars and vectors (float, vec2, vec3, vec4, double, dvec*)
  • Signed and unsigned integers and vectors (int, uint, ivec*, uvec*)
  • 16-bit floating-point and integer types when the corresponding extensions (e.g., GL_EXT_shader_explicit_arithmetic_types) are enabled.

When applied to vector types, subgroupMul performs component-wise multiplication independently across each corresponding vector component for all active lanes.

Comparison with Shared Memory Reductions

Traditionally, computing a combined product across multiple GPU threads required writing lane values to workgroup shared memory (shared), followed by explicit execution barriers (barrier()) and multi-step tree reductions.

subgroupMul replaces this multi-step pattern with a single intrinsic operation. It avoids latency overheads associated with shared memory bank conflicts and memory fences, leveraging dedicated register-level shuffle networks natively present on modern graphics processing hardware.

Practical GLSL Usage

To use subgroupMul, the shader must enable the subgroup arithmetic extension if not already provided by the core GLSL version:

#version 450
#extension GL_KHR_shader_subgroup_arithmetic : enable

layout(local_size_x = 64) in;

layout(set = 0, binding = 0) buffer InputBuffer {
    float values[];
};

layout(set = 0, binding = 1) buffer OutputBuffer {
    float results[];
};

void main() {
    uint globalID = gl_GlobalInvocationID.x;
    
    // Read input per lane
    float localVal = values[globalID];

    // Compute the product across all active subgroup lanes
    float subgroupProduct = subgroupMul(localVal);

    // Write back result from the first lane of each subgroup
    if (subgroupElect()) {
        results[gl_SubgroupID] = subgroupProduct;
    }
}

In this pattern, each lane provides its individual value to the operation, and subgroupMul computes the entire subgroup's product in a single step. Combined with subgroupElect(), it allows a designated single lane to store the aggregated result, completing an efficient parallel reduction stage.