Lab 01 — Transformer FLOPs, Memory & Budget Calculator
Build the napkin. By the end you can answer "I give you 1,000 H100s for 30 days — what do you train?" with a number, a memory check, a serving check, and a dollar figure.
The problem
Feinberg opens his Princeton talk with exactly that question, and hands you one tool:
C = 6ND. But the equation alone is not enough to answer it. You also need to know whether the
answer fits in memory, whether you have that many tokens, whether the resulting model is
servable, and what it costs. That whole chain is one calculator, and this lab is it.
Every downstream phase calls back into this arithmetic: Phase 01 fits scaling laws over C,
Phase 02 optimizes 6ND + 2N·D_inf, Phase 03 needs active-vs-total parameters, Phase 05
divides these numbers by hardware constants to get a roofline.
What you build
| Group | Functions | The idea |
|---|---|---|
| The primitive | matmul_flops, padded_matmul_flops | 2mkn; and what tile padding costs you |
| Parameters | params_per_layer, total_params, moe_parameter_split | GQA/MQA, gated MLPs, the embedding trap, active vs total |
| FLOPs | training_flops, inference_flops, training_flops_exact, unembedding_flops, attention_flop_fraction | 6ND, 2N, the exact per-shape count, and where 6ND breaks |
| Memory | activation_bytes, training_memory, kv_cache_bytes, max_concurrent_requests, decode_arithmetic_intensity, ridge_point | Adam's 16 bytes/param, ZeRO stages, the serving wall |
| Budgets | budget_to_flops, flops_to_days, chinchilla_split, lifetime_flops, cost_report, budget_report | chips × days ↔ FLOPs ↔ (N, D) ↔ dollars ↔ watts |
Key concepts
| Concept | Why it is in this lab |
|---|---|
2mkn | A linear layer costs 2 × params per token. The root of everything. |
6ND | Forward 2N + backward 4N. The currency conversion of the field. |
18BTDF + 24BTDNH | The slide identity — 6ND written out in shapes. A test asserts it. |
| Active vs total parameters | MoE: active for FLOPs, total for memory. Off by 13× if you swap them. |
Non-embedding N | A 256k vocab is 89% of a small model. Scaling ladders must exclude it. |
| 16 bytes/param | Mixed-precision Adam, before a single activation. |
| ZeRO stages | Optimizer states are 75% of training memory — shard those first. |
| KV cache | 2·L·n_kv·d_h·T·B·b. The serving wall; n_kv is the lever. |
| Ridge point | peak FLOP/s ÷ HBM bandwidth. Below it you are memory-bound. |
| Lifetime FLOPs | 6N·D_train + 2N·D_inf. Chinchilla's blind spot. |
Files
| File | What it is |
|---|---|
| lab.py | Your implementation. Signatures, docstrings and validation contracts are given; the arithmetic is yours. |
| solution.py | Reference. python solution.py prints a twelve-part worked example. |
| test_lab.py | 75 tests: happy path, validation, boundaries, invariants, determinism. |
| requirements.txt | pytest only. Pure stdlib otherwise. |
Run
pytest test_lab.py -v # your lab.py — red until you implement
LAB_MODULE=solution pytest test_lab.py -v # the reference — must be green (75 passed)
python solution.py # the worked example
Where to start
matmul_flopsand gettest_matmul_flops_is_2x_weight_countpassing. That single identity is the root of6ND; if it is not obvious to you yet, re-read WARMUP Chapter 2.params_per_layer, thentest_gqa_shrinks_only_kv_projections. It forces you to notice that GQA touchesW_k/W_vand leavesW_q/W_oalone.training_flops_exact, thentest_exact_step_reproduces_the_slide_identity. This is the money test — it checks your per-shape accounting against Feinberg's slide.- Everything else is bookkeeping.
The trap is units. Bytes vs GB, FLOPs vs FLOP/s, per-token vs per-step vs per-run, active vs total. Every signature names its unit; read them.
Success criteria
-
LAB_MODULE=solution pytest test_lab.py -v→ 75 passed. -
Your
lab.pyreaches 75 passed. -
python solution.pyruns and you can explain every one of its twelve sections. -
Without the calculator, you can derive
6NDon paper in under two minutes. -
You can state the four places
6NDbreaks and the error in each. -
You have run
budget_reporton a cluster you might plausibly be given, and written a paragraph on what you would train — including where you would deliberately deviate from Chinchilla and why.
How this maps to the real stack
| This lab | The real thing | Where the miniature lies |
|---|---|---|
training_flops_exact | torch.utils.flop_counter.FlopCounterMode; JAX cost_analysis(); NVIDIA Nsight | Real counters trace the actual graph, so they catch fused ops, custom kernels, and recomputation. Ours assumes a textbook architecture. |
training_memory | torch.cuda.max_memory_allocated(); DeepSpeed's memory estimator; torch.distributed.fsdp | Ignores allocator fragmentation, NCCL buffers, CUDA context (~1 GB), and per-framework overheads. Real usage runs 10–30% above this. |
kv_cache_bytes | vLLM's PagedAttention block manager; TensorRT-LLM's KVCacheManager | Real engines page the cache in fixed blocks, so the true footprint rounds up to block granularity and can share prefixes across requests. |
chinchilla_split | Nobody ships a library for this — every lab has an internal version | Uses a fixed tokens_per_param ratio. Real practice fits the ratio from your own ladder (Phase 01) because it is recipe-dependent. |
ridge_point / decode_arithmetic_intensity | The roofline analysis in Nsight Compute; llm-analysis | Assumes perfect bandwidth utilization. Real kernels achieve 60–90% of peak HBM bandwidth. |
HARDWARE table | Vendor spec sheets | Peak numbers are marketing maxima at ideal clocks. Sustained throughput under thermal load is 5–15% lower. |
What is not a lie: the FLOP arithmetic. 2mkn, 6ND, 18BTDF + 24BTDNH, and the
KV-cache formula are exact. That is why they are what interviewers ask about.
Extensions
For your own hardware, or for a portfolio piece:
- Validate against a real model. Load a small HF checkpoint, count its parameters with
sum(p.numel() for p in model.parameters()), and checktotal_paramsmatches. Then runFlopCounterModeon a forward pass and checkinference_flops. Any mismatch is a lesson. - Add pipeline/tensor-parallel memory. Extend
training_memorywithtp_degreeandpp_degree, and model the activation memory that pipeline stages must hold in flight. Phase 04 will need it. - Add an MoE memory model. Expert-parallel sharding puts different experts on different
chips — extend
moe_parameter_splitto report per-device memory givenep_degree. - Fit the tokens-per-param ratio. Instead of hardcoding 20, take a list of
(N, D, loss)points and solve for the ratio that minimizes loss at fixedC. That is Phase 01, and doing it here first makes Phase 01 trivial. - Plot the roofline. Sweep batch size and context length, and plot arithmetic intensity against the ridge point to find exactly where decode becomes compute-bound.
Interview / resume bullets
- "Derived and implemented the full FLOP and memory accounting for transformer pre-training —
C = 6NDwith exact per-shape decomposition (18BTDF + 24BTDNH), MoE active-vs-total parameter handling, mixed-precision Adam memory with ZeRO stages 0–3, and KV-cache sizing under GQA/MQA — and used it to size training runs against real accelerator budgets." - "Built a compute-budget planner converting chips × days → FLOPs → Chinchilla-optimal
(N, D)→ dollars and watts, with automated feasibility checks for cluster memory, corpus size and single-chip serving; quantified the lifetime-cost crossover where an overtrained smaller model beats the compute-optimal one." - Interview-ready: "backward is exactly 2× forward because each forward matmul becomes two
backward matmuls,
dX = dY·WᵀanddW = Xᵀ·dY, each the same size as the forward one."