CUDA Pooling Operations Optimization Guide

December 25, 2025 8 min By RightNow AI Team

Introduction

Pooling operations reduce spatial dimensions in CNNs by computing max or average over local regions. While conceptually simple, efficient implementation requires careful attention to memory access patterns and thread assignment.

This guide covers standard pooling, global pooling for classification heads, and the less common but important adaptive pooling.

Common Performance Issues

  • Strided access patterns hurt memory coalescing
  • Thread divergence in boundary handling
  • Inefficient global pooling implementation

Optimization Techniques

1. Coalesced Output Writes

Assign threads to output positions for coalesced writes.

2. Warp Reduction for Global Pooling

Use warp shuffles for efficient spatial reduction.

3. NHWC Layout

Channels-last layout improves pooling memory access.

Implementation Comparison

Before (Naive Implementation)

Basic pooling with channels-last layout.

__global__ void maxpool2d_naive(float* input, float* output,
                                 int H, int W, int C, int pool_size) {
    int c = blockIdx.x * blockDim.x + threadIdx.x;
    int out_h = blockIdx.y;
    int out_w = blockIdx.z;

if (c >= C) return;

float max_val = -INFINITY;
    for (int kh = 0; kh < pool_size; kh++) {
        for (int kw = 0; kw < pool_size; kw++) {
            int h = out_h * pool_size + kh;
            int w = out_w * pool_size + kw;
            float val = input[(h * W + w) * C + c];
            max_val = fmaxf(max_val, val);
        }
    }

output[(out_h * (W/pool_size) + out_w) * C + c] = max_val;
}

After (Optimized Implementation)

Vectorized pooling processes 4 channels simultaneously.

__global__ void maxpool2d_vectorized(float4* input, float4* output,
                                      int H, int W, int C4, int pool_size) {
    // Process 4 channels at once with float4
    int c4 = blockIdx.x * blockDim.x + threadIdx.x;
    int out_h = blockIdx.y;
    int out_w = blockIdx.z;

if (c4 >= C4) return;

float4 max_val = make_float4(-INFINITY, -INFINITY, -INFINITY, -INFINITY);

for (int kh = 0; kh < pool_size; kh++) {
        for (int kw = 0; kw < pool_size; kw++) {
            int h = out_h * pool_size + kh;
            int w = out_w * pool_size + kw;
            float4 val = input[(h * W + w) * C4 + c4];
            max_val.x = fmaxf(max_val.x, val.x);
            max_val.y = fmaxf(max_val.y, val.y);
            max_val.z = fmaxf(max_val.z, val.z);
            max_val.w = fmaxf(max_val.w, val.w);
        }
    }

output[(out_h * (W/pool_size) + out_w) * C4 + c4] = max_val;
}

Performance Results

Metric Naive Optimized Improvement
Throughput (images/sec) 12000 38000 3.2x

Frequently Asked Questions

Max pool vs average pool performance?

Max pooling is slightly faster as it avoids the division. Average pooling requires counting valid elements for boundary handling.