Rung 1 — CPU Tiled Matmul Writeup¶
Aligned phase: Phase 0 (systems/math foundations) Ship by: end of M2 Effort: ~2 weeks, ~15 hrs total Signal: low-mid. This is a warm-up rung. Its purpose is to prove you can (a) benchmark honestly, (b) reason about caches, and (c) write a technical blog post.
The artifact spec¶
Write a C or C++ program that computes C = A @ B for square FP32 matrices of size 1024×1024, 2048×2048, and 4096×4096, and progressively optimizes it through these stages. Each stage MUST be benchmarked and included in the writeup.
Stage |
Optimization |
Expected speedup vs naive |
|---|---|---|
0 |
Naive triple-loop |
1.0× baseline (~1-2 GFLOPS) |
1 |
Loop reorder (i-k-j) |
3-5× (cache-friendly B access) |
2 |
Register blocking (4x4 microkernel) |
2-3× more |
3 |
Cache blocking (block size = L1/L2 tuned) |
2-3× more |
4 |
SIMD (AVX2 or NEON intrinsics, 8-wide) |
4-8× more |
5 |
OpenMP multithreading |
~N-core× more |
Final |
All combined |
~50-200× vs naive; within 2-3× of MKL/OpenBLAS |
The last row is the money shot. On a modern laptop the naive version does ~1 GFLOPS; MKL does ~200 GFLOPS on 8 cores. Getting to 30-60% of MKL is the achievable target.
Include a roofline plot. X-axis = arithmetic intensity (FLOPs/byte), Y-axis = achieved GFLOPS, horizontal line = peak FLOPS, sloped line = peak bandwidth × AI. Each version of your kernel is a dot on the plot. This plot is what makes the post look serious.
Repo structure¶
matmul-writeup/
├── README.md # Blog-quality writeup, embedded numbers + roofline PNG
├── src/
│ ├── v0_naive.c
│ ├── v1_reorder.c
│ ├── v2_register.c
│ ├── v3_cache.c
│ ├── v4_simd.c
│ └── v5_openmp.c
├── bench/
│ ├── run_all.sh # Pins clocks, sets CPU governor to performance, runs each 10× warmup + 100× measured
│ └── roofline.py # Regenerates the plot from bench outputs
├── results/
│ ├── raw.csv
│ └── roofline.png
└── ATTRIBUTIONS.md # Cite Simon Boehm, Stefan Salewski, MIT 6.172
README.md is the blog post. Use headings, tables, code snippets, one hero plot at top.
Where to post¶
GitHub:
github.com/<you>/matmul-writeup. Make it public. Star it yourself so it shows on profile.Blog: cross-post the README as a blog post on your own site (or dev.to / Substack if you don’t have one yet). Title: “CPU Matmul: from 1 GFLOPS to 60 GFLOPS in six versions.” URLs with GFLOPS in them get clicks.
HN / r/programming: do not post here. This rung is too basic for HN — you’ll get roasted for redoing something that’s been done. Post to your Twitter/X circle instead.
GPU MODE Discord
#hip-rocmor#general: share once. Don’t spam.LinkedIn: skip. Save LinkedIn for rung 5+.
What signals it sends¶
“I can benchmark without embarrassing myself” (fixed clocks, warmups, measured throughput).
“I understand the memory hierarchy” (cache blocking, roofline).
“I can write about a technical topic in ≤2000 words with real numbers.”
This is a table stakes artifact. Every serious inference engineer has done this or the equivalent. The purpose is to not look like a fake later.
Past examples to study¶
Simon Boehm’s CUDA SGEMM worklog — https://siboehm.com/articles/22/CUDA-MMM. GPU version, but the same structure. Read the whole thing before starting. This is your target quality.
MIT 6.172 Performance Engineering — matmul lectures. YouTube search “MIT 6.172 matmul.” Charles Leiserson explains cache blocking end-to-end.
BLIS papers — https://github.com/flame/blis — the modern reference. Overkill for rung 1 but skim the design doc.
Stefan Salewski Nim/C matmul post — one Google search away. Similar to yours in scope.
Common mistakes¶
Wrong compiler flags. Use
-O3 -march=native -ffast-mathfor all versions including the naive baseline. Otherwise your “speedup” is fake.Not pinning clocks. On Linux
sudo cpupower frequency-set -g performance. On Mac, disable Turbo Boost and note the clock in the writeup.Timing hot vs cold. First run has cold caches. Do 10× warmup then 100× timed; report median and P99.
Not verifying correctness. After every version,
assert(max_abs_diff(C_new, C_ref) < 1e-3). Speedups on wrong outputs are worthless.Comparing to BLAS incorrectly. Link against MKL or OpenBLAS, run the same benchmark harness, put it on the same plot. If your best version is 3× slower than MKL, say so honestly. Don’t hide it.
Writing too much. 1500-2500 words. Anything longer is padding. One plot at top, tables in the middle, small conclusion.
Success criteria¶
Repo builds with
makeon Linux and Macbench/run_all.shregenerates all numbers from scratchRoofline PNG in the repo
Blog post published somewhere with a permalink
At least one person you don’t know reads it and comments or stars
You can explain any line of code in a phone study 6 months later
If all six check, ship rung 2.