6.2.14 · D1GPU Architecture

Foundations — GPU memory bandwidth optimization

3,152 words14 min readBack to topic

This page assumes you have seen nothing. Before we can talk about coalescing, banks, tiling, or the roofline, we must earn every letter the parent note uses. Read top to bottom — each symbol is built on the one above it.


0. What is a byte, an address, and a "consecutive" access?

Everything in the parent note is about where data lives. So we start there.

Figure — GPU memory bandwidth optimization

Figure s01 — the address street. Memory is drawn as one long row of numbered byte-boxes. Each box is one byte; its number is its address. The blue block covers bytes A through A+3 — that is one 4-byte float. The orange block right beside it covers A+4 through A+7 — the next float. The green arrow underneath marks that these two floats are neighbours on the street: no gap between them. This picture is the definition of "consecutive" that the whole page leans on.


1. Threads, warps, and why they move in lockstep

For more on how warps are chosen and scheduled, see GPU-Warp-Scheduling.


2. The memory transaction — the truck's fixed cargo size

Figure — GPU memory bandwidth optimization

Figure s02 — one full truck vs many empty ones. Two warps are shown, each with 32 threads asking for one float apiece. In the top (green) row the 32 floats sit side-by-side and fit inside a single 128-byte transaction — one full truck. In the bottom (red) row the same 32 floats are scattered far apart, so the hardware must send many separate trucks, each carrying only one useful float and lots of empty air. Same amount of wanted data, wildly different number of trips.

Now let us name the pieces the formula needs.

Best case, worked from zero. 32 threads reading consecutive floats, so , lowest address , highest address :

Worst case (stride 32). Thread reads , so highest is :


3. Efficiency — how full each truck is

Consecutive case: . Stride-32 case: .


4. Where itself comes from (raw bandwidth)

You will not usually compute — the datasheet gives it (e.g. an A100 ≈ 2 TB/s). The formula matters only so the symbol isn't a mystery.

The parent also multiplies several efficiencies together: Each is a fraction ≤ 1 from a different loss. (fewer trips because data was already nearby) is the business of Cache-Hierarchy; (keeping the road never idle) ties into Memory-Latency-Hiding.


5. Shared memory and banks — the fast local shelf

For how you actually program it, see CUDA-Shared-Memory.

Before the bank formula, we need one small piece of arithmetic notation.

Figure — GPU memory bandwidth optimization

Figure s03 — banks as a clock face. The 32 banks are drawn around a circle like the numbers on a clock. Slot-indices are assigned going clockwise: 0, 1, 2, … 31, and then slot-index 32 wraps straight back onto bank 0. The orange dot highlights byte-address 70: first floor-divide by the slot size, , giving slot-index 17; then , so it lands on bank 17. The clock shape is the modulo made visible: counting past 31 loops around; here 17 is still on the first lap, so bank = slot-index.


6. Counting work: FLOPs and arithmetic intensity

Figure — GPU memory bandwidth optimization

Figure s04 — the roofline. The horizontal axis is arithmetic intensity (math per byte); the vertical axis is the speed you can actually attain. The black line has two parts: a slanted ramp on the left (where the memory road limits you) and a flat roof on the right (where raw compute limits you). They meet at the dotted vertical line, the machine balance . The red dot (naive, ) sits far left and low — starved. The orange dot (tiled, ) has climbed the ramp but is still left of the corner. The green dot () sits up on the flat roof — compute-bound. The whole message: raising walks you rightward up the ramp until you hit the roof.

The parent's tiling trick (parameter = tile edge length) raises from (no tiling) toward and past . Why tiling reuses data is a story of good Parallel-Algorithm-Design — you compute a value while its neighbours are still hot in shared memory.


7. How it all connects

The diagram below is a prerequisite map: read an arrow "" as "you must understand before makes sense." Each box is one concept we just built, in the same order as the sections above; follow the arrows and you re-walk the whole page. Everything funnels into the parent topic at the bottom.

byte and address

consecutive access

thread

warp of 32

transaction size T tx

coalescing efficiency eta

bandwidth B

delivered throughput M

shared memory

banks and conflicts

FLOP

arithmetic intensity I

machine balance I star

GPU memory bandwidth optimization


Equipment checklist

Cover the right side and answer aloud. If any stalls, re-read that section.

What is an address, in one plain sentence?
The numbered position of a byte-box in memory, like a house number on a street.
What are the TWO different "words" on this page?
The machine data-word (4-byte float in DRAM) and the shared-memory bank-slot (4-byte bank cell) — both 4 bytes here but separate ideas.
Why does warp size 32 matter for memory?
All 32 threads issue their requests at the same instant, so the hardware can merge those simultaneous requests into wide transactions.
What does the symbol stand for?
The number of bytes a single thread requests in one access (e.g. 4 for one float).
Define "address span" as a formula.
highest address touched − lowest address touched + .
What is the transaction size , and its typical value?
The smallest fixed chunk (≈128 bytes) the controller ever fetches; even a 4-byte request drags a whole -byte load.
Why do we need the ceiling function when counting transactions?
You cannot send a partial truck — any leftover bytes force one more full transaction.
Why can a perfectly consecutive 128-byte access still cost two transactions?
If it starts off a -byte boundary it straddles two aligned segments, splitting into two trucks.
Define efficiency in words.
Useful bytes the threads actually used, divided by total bytes the transactions moved (0 = all waste, 1 = perfectly full).
Write the delivered-throughput formula.
.
Why is there a factor of 2 in the bandwidth formula?
DDR memory transfers on both the rising and falling edge of each clock tick, doubling data per heartbeat.
What is in the bank formula and how do you get a bank from address ?
is the bank-slot size (4 bytes); compute to get the slot-index, then take that .
Compute the bank of byte-address 70 with .
, then → bank 17.
When is there NO bank conflict even with all 32 threads active, and what's the catch?
When they all READ the same address (hardware broadcasts) or all hit distinct banks — but writes to the same address do NOT merge.
Distinguish from .
is the fixed 128-byte hardware DRAM transaction; is the software-chosen edge length of a matrix tile (e.g. 32).
Define and arithmetic intensity .
is peak compute in FLOP/s; is FLOPs performed divided by bytes moved from DRAM.
What separates bandwidth-bound from compute-bound?
Comparing to machine balance : below it you are bandwidth-bound, above it compute-bound.