Parallel Reduction is the fundamental parallel algorithm pattern that combines N input values into a single result (sum, max, min, dot product) using a logarithmic-depth tree of binary operations — requiring only O(log N) steps on N processors compared to O(N) for sequential reduction, making it the building block for virtually every aggregate computation in parallel and GPU computing.
The Reduction Tree
Given N = 1024 values to sum:
- Step 1: 512 threads each add pairs of adjacent elements → 512 partial sums
- Step 2: 256 threads add pairs of those partial sums → 256 values
- Step 3-10: Continue halving until 1 final sum remains
- Total: log2(1024) = 10 steps, with decreasing parallelism at each level
GPU Implementation Hierarchy
1. Warp-Level Reduction (Shuffle Instructions): Within a single warp (32 threads), CUDA's __shfl_down_sync() instruction allows threads to directly exchange register values without going through shared memory. A 32-element reduction completes in 5 shuffle operations (~5 cycles). This is the fastest reduction primitive.
2. Block-Level Reduction (Shared Memory): For thread blocks with multiple warps (e.g., 256 threads = 8 warps), each warp first reduces its 32 elements via shuffles, then the 8 warp results are combined via shared memory. The final value is written to global memory by one thread.
3. Grid-Level Reduction (Kernel Launch or Atomics): Multiple thread blocks each produce local sums. A second kernel launch (or atomic operations) combines the per-block results. Two-pass reduction (large kernel → small kernel) is standard for arrays with millions of elements.
Optimization Techniques
- Sequential Addressing (Avoid Divergence): Use stride = blockDim/2, blockDim/4, ... instead of stride = 1, 2, 4, ... to keep active threads in contiguous positions, avoiding warp divergence.
- First-Add During Load: Each thread loads and adds two (or more) elements during the initial global memory read, halving the number of threads needed and doubling memory throughput per thread.
- Unroll the Last Warp: When the number of active threads drops to 32, switch from shared-memory reduction to warp shuffles, eliminating synchronization overhead.
- Grid-Stride Loop: Each thread processes multiple elements in a loop before the tree reduction begins, maximizing work per thread and minimizing the number of thread blocks.
Performance
An optimized GPU reduction on an A100 achieves 80-90% of peak memory bandwidth (1.5+ TB/s) for large arrays — the operation is entirely memory-bandwidth-bound since the arithmetic (addition) is trivially cheap compared to the data movement.
Parallel Reduction is the simplest algorithm that exposes the full complexity of GPU optimization — just adding numbers reveals every performance pitfall: memory coalescing, warp divergence, shared memory bank conflicts, and kernel launch overhead.
Explore 500+ Semiconductor & AI Topics
From EUV lithography to CUDA optimization — search the full knowledge base or chat with our AI assistant.