Lab 02 — Memory Hierarchy & Roofline Microbenchmarks (C)
Phase: 01 — GPU Architecture | Difficulty: ⭐⭐⭐☆☆ | Time: 3–5 hours Language: C11 + make | Hardware: any CPU (the methodology transfers to GPU)
Concept primer:
../WARMUP.mdCh. 6–7 (memory hierarchy, roofline),../HITCHHIKERS-GUIDE.md§4–5.
Run
make
./membench # latency curve + bandwidth + roofline placement
0. The mission
Stop trusting tables — measure a memory hierarchy yourself, with the same two techniques every GPU microbenchmarking paper uses (e.g., Jia et al.'s Volta dissection):
- Pointer-chasing measures latency: each load's address depends on the previous load's value, so the CPU cannot overlap or prefetch — you see the raw round-trip per level.
- Streaming measures bandwidth: independent sequential accesses let the hardware pipeline everything — you see the peak transfer rate.
Then you place two kernels on your machine's roofline and verify the model predicts which one is memory-bound. This is the exact methodology you'd use to characterize an unknown accelerator a vendor hands you (WARMUP Ch. 10).
1. What the program does
- Latency sweep: builds a randomly-permuted cycle of pointers in buffers from 4 KB to 256 MB and chases it 10M times. Plotting ns/load vs buffer size shows plateaus at each cache level, with cliffs between them.
- Bandwidth: streams a large array (read-sum, then memcpy-style write) and reports GB/s.
- Roofline placement: times
vector_add(AI ≈ 0.083 FLOP/byte) and a blockedmatmul(AI grows with block size), converts to GFLOP/s, and prints where each lands relative tomin(peak, AI × measured_bandwidth).
Example output (Apple M-series; your numbers will differ — that's the point):
== latency (pointer chase) ==
4 KB 1.2 ns/load <- L1
128 KB 3.1 ns/load <- L2
8 MB 9.8 ns/load <- SLC/L3
256 MB 98.4 ns/load <- DRAM
== bandwidth (stream) ==
read : 61.2 GB/s write : 38.9 GB/s
== roofline ==
vector_add: AI=0.083 predicted ceiling 5.1 GFLOP/s measured 4.7 -> memory-bound (92% of roof)
matmul b=64: AI≈10.7 measured 41.3 GFLOP/s -> compute-bound region
2. Fill in ROOFLINE.md
The worksheet asks you to:
- Identify each plateau and match it to your CPU's published cache sizes.
- Compute your CPU's ridge point from measured bandwidth + estimated peak (cores × clock × SIMD width × 2 FLOPs).
- Compute A100 and H100 ridge points from their spec sheets (WARMUP Ch. 7 has the answers — derive before checking).
- Place batch-1 LLM decode (AI ≈ 2) on the H100 roofline and write the three-sentence implication (this is Phase 07's thesis statement).
3. Why the methodology transfers to GPUs
| This lab (CPU) | The GPU equivalent |
|---|---|
| pointer-chase ns/load cliffs | shared mem ~30 cyc → L2 ~200 → HBM ~500 (Jia et al. measured exactly this way, in CUDA) |
| stream GB/s | HBM bandwidth tests (bandwidthTest, nvbandwidth) |
| roofline placement | Nsight Compute's roofline chart (Phase 02 Lab 02 reads it) |
| prefetcher distorting naive latency tests | GPU coalescer distorting naive bandwidth tests (Phase 02) |
4. Common pitfalls
- Sequential pointer chase — the prefetcher hides DRAM latency and you'll "measure" L1 everywhere. The permutation must be random. (Try it: replace the shuffle with identity and watch the curve flatten — instructive!)
- Compiler deleting your loop — the chase result must feed a
volatilesink or the optimizer removes everything. Same trick JMH uses (Blackhole), same trick you'll need in CUDA benchmarks. - Timing one pass — too noisy; the harness does warmup + multiple reps and reports the minimum (least-disturbed) run.
- Forgetting
-O2— at-O0you're benchmarking the compiler's laziness, not the memory system.
5. Extensions
- Add a strided read benchmark (stride 1, 2, 4, … 128 elements) and find the cliff at your cache-line size (64 B): past it, every load is a new line and bandwidth collapses by the stride factor. This is exactly the GPU coalescing argument of Phase 02 — uncoalesced access wastes the same way.
- Run under
taskset -c 0vs a core on another socket/cluster (Linux) and observe NUMA / cluster effects. - Use
perf stat -e cache-misses,cache-references ./membenchand correlate the counters with your plateaus.
6. What this lab proves about you
You can characterize any memory system — CPU today, an OEM partner's accelerator next quarter — with two from-scratch benchmarks and place workloads on its roofline before a single line of vendor marketing reaches your team.