6.2.9 · D4GPU Architecture

Exercises — Bank conflicts in shared memory

1,691 words8 min readBack to topic
Figure — Bank conflicts in shared memory

Level 1 — Recognition

L1.1 — Read the bank off a word index

A thread reads shared[70] where shared is a float array. Which bank does it hit?

Recall Solution

WHAT: float = one 4-byte word, so word index directly (no extra division; we are already counting in words, not bytes). WHY: bank . Answer: bank 6.

L1.2 — Byte address to bank

A thread accesses byte address in shared memory (4-byte banks). Which bank?

Recall Solution

WHY divide by 4 first: the address is in bytes, so convert to a word index by . WHY mod 32: round-robin over 32 banks. Answer: bank 8.


Level 2 — Application

L2.1 — Conflict degree from stride

A warp runs float v = shared[tid * 8];. What is the conflict degree, and how many cycles does the access take?

Recall Solution

WHAT: stride in words is . WHY gcd: two threads collide when their word indices differ by a multiple of 32; the number colliding per bank is . Answer: 8-way conflict → the access is serialized into 8 cycles.

L2.2 — Odd stride surprise

float v = shared[tid * 7];. Conflict degree?

Recall Solution

Because 7 shares no factor with 32 (which is ), the map hits all 32 banks — a permutation. 1 cycle.

L2.3 — A double array

__shared__ double d[64]; double v = d[tid];. Reasoning in 4-byte banks, is this conflict-free?

Recall Solution

WHY not "divide by 8": banks are physically 4 bytes wide — there is no 8-byte bank. A double occupies two consecutive banks (low half + high half). Thread 's two 4-byte halves sit at word indices and . Across the warp the low halves land in banks and high halves in ; hardware issues the two halves in two phases so that within a phase every bank serves exactly one thread. Answer: conflict-free for straightforward d[tid].


Level 3 — Analysis

L3.1 — Array-of-structures, .x field

struct P { float x, y, z; };      // 12 bytes = 3 words
__shared__ P p[32];
float px = p[tid].x;

Compute the stride and conflict degree for reading .x.

Recall Solution

Struct size words, so consecutive .x fields are 3 words apart. Thread hits bank , a permutation of all 32 banks (T0→0, T1→3, …, T11→33 mod 32 = 1, …). 1 cycle.

L3.2 — The AoS that does bite

struct Q { float a, b, c, d; };   // 16 bytes = 4 words
__shared__ Q q[32];
float qa = q[tid].a;

Conflict degree on .a?

Recall Solution

Struct size words ⇒ . A "nice" power-of-two struct silently creates a 4-way conflict on every field. 4 cycles.

L3.3 — Column walk of a square tile

__shared__ float m[32][32];       // row-major, word(r,c)=32r+c
float v = m[tid][col];            // fixed col, tid = row

Show every thread hits the same bank.

Recall Solution

Thread reads word . Stride . Independent of all 32 threads land in bank ⇒ 32-way conflict, 32 cycles. See figure.

Figure — Bank conflicts in shared memory

Level 4 — Synthesis

L4.1 — Pad the tile to kill the conflict

Fix L3.3 by declaring __shared__ float m[32][33];. Show the column walk becomes conflict-free.

Recall Solution

WHAT changed: row stride is now 33 words (one padding column). Element sits at word . Thread reads word . WHY 33 works: , so As , sweeps all 32 distinct banksconflict-free, 1 cycle. The padding cost is one wasted column per row (32 floats = 128 bytes).

Figure — Bank conflicts in shared memory

L4.2 — Choose padding for a 16-wide tile

You have __shared__ float t[32][16]; and walk columns (t[tid][col], stride 16). What single padding value (making the row width ) is the smallest that makes the column walk conflict-free?

Recall Solution

Row width becomes words = the new stride . We need , i.e. must be odd. is even, so the smallest making it odd is (width 17). Check: ✓. Answer: , width 17.


Level 5 — Mastery

L5.1 — General stride cycle count

A warp uses stride words. Write the number of cycles as a function of , then evaluate for and .

Recall Solution

General: cycles (each colliding group serialized).

  • : cycles.
  • : cycles. Why: shares one factor of 2 with ; shares three.

L5.2 — Transpose kernel diagnosis

In 6.3.01-Matrix-transpose-optimization a kernel does tile[threadIdx.y][threadIdx.x] = in[...] (row write) and later out[...] = tile[threadIdx.x][threadIdx.y] (transposed read) with tile[32][32]. Which access conflicts, by how much, and what is the one-word fix?

Recall Solution
  • Write tile[ty][tx]: within a warp tx varies over the column → stride 1 → conflict-free.
  • Read tile[tx][ty]: within a warp tx varies over the row index → stride 32 → 32-way conflict, 32 cycles.
  • Fix: declare tile[32][33]. Row stride 33 ≡ 1 (mod 32) ⇒ read becomes conflict-free (L4.1). Total wasted shared memory: bytes. This is why the transpose optimization exists — it links straight to 6.2.10-Occupancy-and-resource-limits because that extra column eats into your shared-memory budget.

L5.3 — Broadcast is NOT a conflict

float v = shared[0]; — every thread reads the same word 0. How many cycles?

Recall Solution

Same address ≠ conflict. A bank conflict needs threads at different addresses in the same bank. When all 32 threads request the identical word, the hardware broadcasts it in a single fetch. Answer: 1 cycle. (Contrast with shared[tid*32], where the addresses differ but all map to bank 0 → 32-way conflict.)

Recall Two-line self test

Stride 8 conflict degree? ::: → 8-way Padding that fixes a stride-32 column walk of float[32][N]? ::: any with odd; smallest is

Related: 6.2.08-Memory-coalescing · 6.2.07-Shared-memory-architecture · 6.2.11-Warp-divergence