Explain GPU Memory And Matrix Transpose Optimization
ZooxSoftware Engineer · OnsiteLast updated
Tests understanding of GPU memory hierarchy, memory coalescing, shared memory bank conflicts, and optimization techniques through the canonical matrix transpose problem.
Problem Overview
Matrix transpose is a classic memory-bound GPU kernel where optimizing memory access patterns is critical to approaching peak device bandwidth. A naive parallel implementation either reads coalesced and writes uncoalesced, or vice versa, causing significant memory bus underutilization.
Your task is to analyze the naive GPU matrix transpose, identify its hardware-level bottlenecks, and design an optimized tiled transpose algorithm.
Requirements
-
Memory Access Analysis:
- Explain how thread warps (32 threads) access global memory in the naive implementation below:
__global__ void naiveTranspose(const float* in, float* out, int width, int height) { int x = blockIdx.x * blockDim.x + threadIdx.x; int y = blockIdx.y * blockDim.y + threadIdx.y; if (x < width && y < height) { out[x * height + y] = in[y * width + x]; } } - Identify whether the reads, writes, or both are coalesced, and explain the performance impact on modern GPU memory controllers.
- Explain how thread warps (32 threads) access global memory in the naive implementation below:
-
Tiled Implementation with Shared Memory:
- Present an optimized kernel using shared memory tiling (e.g., using a
TILE_DIM x TILE_DIMblock). - Outline how both global reads and global writes can be made fully coalesced.
- Write pseudo-CUDA code or provide a precise step-by-step implementation for the kernel.
- Present an optimized kernel using shared memory tiling (e.g., using a
-
Shared Memory Bank Conflicts:
- Explain what shared memory bank conflicts are, how a 32-bank architecture responds to a naive 2D shared memory tile allocation like
__shared__ float tile[32][32], and how tile padding (e.g.,tile[32][33]) resolves this conflict.
- Explain what shared memory bank conflicts are, how a 32-bank architecture responds to a naive 2D shared memory tile allocation like
Constraints & Assumptions
- Matrix dimension
widthandheightare multiples of 32 (e.g., 2048 x 2048 or 4096 x 4096). - Standard warp size is 32 threads, operating in SIMT lockstep.
- Shared memory consists of 32 independent banks, each 4 bytes wide.
Hints
Hint 1
In the naive kernel, consecutive threads in a warp have adjacent x-indices; trace which output indices they map to when indexing out[x * height + y].
Hint 2
Shared memory allows you to decouple the global read pattern from the global write pattern by staging intermediate data.
Hint 3
If consecutive threads in a warp write or read along the column of a 32-wide 2D array, they hit the same bank modulo 32 unless you add an extra column offset.
Follow-up questions
- How does thread coarsening (e.g., each thread processing multiple rows per tile) improve instruction-level parallelism and arithmetic intensity?
- How would your approach change if matrix dimensions were not even multiples of the tile dimension?
- In an autonomous driving sensor pipeline (e.g., LiDAR range image transposition or camera tensor reshaping), would you prefer a custom transpose kernel or a library call like cuBLAS / cuTENSOR, and under what conditions?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.