← TinyGPT · docs · devlog · roadmap · speedup
source: docs/learn/essential-vs-optimization.md · view on GitHub ↗

Mathematically essential vs engineering optimization

The whole project splits cleanly into two layers, and keeping them separate is the difference between understanding TinyGPT and memorizing it.

The litmus test: “If I removed this, would the model compute a different function, or the same function more slowly?” Different ⇒ essential. Same ⇒ optimization.

The two columns

Mathematically essential (defines the function)What it computes
Token + position embeddingstokens → vectors; inject order
Self-attention: softmax(QKᵀ/√d ⊙ causalmask) · Vmix information across positions
Multi-headrun attention in H subspaces, concat
LayerNorm + residualsstabilize + let gradients skip
MLP + GELUper-position non-linear transform
Output projection → logitshidden → vocab scores
Cross-entropy lossscores → a scalar to minimize
Backprop (chain rule through all the above)loss → per-parameter gradients
Adam/AdamW updategradients → weight step
Engineering optimization (preserves the function)What it speeds/shrinksMust preserve
Matmul tiling + 4×4 register blocking (study_guide §8)arithmetic intensity → compute-boundbit-parity matmul
vec4<f32> packed loads (§9)128-bit memory transactionssame result (the bug that wasn’t)
FlashAttention-2 (online-softmax, §7)O(T²)→O(T) memory; recompute on backwardidentical attention math
KV cachedecode O(T²)→O(T) computesame logits
Quantization (4-bit/8-bit)weights smallerapproximately same weights (lossy!)
WASM / WebGPU backendsrun on CPU-SIMD / GPUparity with python_ref
Memory64 (-sMEMORY64)address heaps >4 GBsame kernels
Gradient checkpointingrecompute activations → less RAMidentical gradients

Two subtleties worth internalizing:

Loss drift — the number that polices the boundary

How do you prove an optimization preserved the math? Loss drift.

Loss drift = the percent difference between an optimized backend’s training loss and the Python reference’s loss at the same step N, same seed, same data, same hyperparameters. drift = |loss_backend − loss_ref| / loss_ref.

It is the single number that says “this optimization is still computing the right function”:

This is also why parity testing beats standalone kernel benchmarks: a standalone benchmark checks the kernel in isolation (ideal shapes, fresh cache) and answers “is it fast + self-consistent?” Drift checks the kernel inside the real pipeline and answers “does it still compute the model’s function?” Only the second question is the one that ships. (See study_guide §9.)

Why this lens matters

References