Cheat Sheet — Model Accuracy & AI Performance
The numbers, formulas, and decision rules to have at your fingertips — the interview-whiteboard quick reference. Definitions are in GLOSSARY.md.
Quantization formulas
Affine (asymmetric): Q(x) = clamp(round(x/s) + z, q_min, q_max)
x̂ = s · (Q(x) − z)
Scale: s = (x_max − x_min) / (q_max − q_min)
Zero-point: z = q_min − round(x_min / s)
Symmetric (z=0): s = max(|x|) / q_max
Quant error bound: |Q(x) − x| ≤ s/2 → smaller s = smaller error
| dtype | range | notes |
|---|---|---|
| INT8 signed | −128 … 127 | symmetric default |
| INT8 unsigned | 0 … 255 | asymmetric; don't cast to int8 before dequant — overflow! |
| INT4 | −8 … 7 (or 0 … 15) | weight-only (GPTQ/AWQ); needs error comp / salient protection |
| NF4 | 16 normal-spaced levels | QLoRA base dtype |
PTQ vs QAT: try PTQ first (fast, little data). Reach for QAT when PTQ's accuracy drop is too big (often INT4, or sensitive layers). Mixed precision (sensitive layers high, rest low) is usually the best size/accuracy point.
Memory math: params × bytes/param. 7B model → FP16 ≈ 14 GB, INT8 ≈ 7 GB, INT4 ≈ 3.5 GB. (Plus the KV cache, which grows with sequence length — quantize/bound it on edge.)
Floating-point formats
| format | exp bits | mantissa bits | use |
|---|---|---|---|
| FP32 | 8 | 23 | baseline / accumulation |
| FP16 | 5 | 10 | range-limited, precise-ish |
| BF16 | 8 | 7 | FP32's range, less precision — training-stable |
Rule: accumulate in higher precision than you store. Reductions (softmax, sum) in FP32 even when activations are FP16/BF16.
Roofline (the master diagram)
Arithmetic Intensity (AI) = FLOPs / bytes moved
Attainable perf = min( peak_FLOPs/s , peak_BW × AI )
AI low → MEMORY-bound → fix: fusion, quantization, layout, FlashAttention
AI high → COMPUTE-bound → fix: better matmul, more cores, higher clocks
Most LLM inference is memory-bound (weight loading at batch 1; the N×N attention matrix) — which is why FlashAttention and quantization win. Don't optimize FLOPs on a memory-bound kernel.
Profiling discipline
1. WARM UP (discard first iters — compile/alloc/cache)
2. measure MANY iters, take the MEDIAN (not mean — outliers)
3. know your clock: CPU time ≠ device time ≠ wall time; self ≠ total
4. find the regime (roofline) BEFORE choosing a fix
Top recurring perf bugs: CPU↔device sync (.item()/.cpu() in a hot loop), hidden dtype
casts, non-contiguous copies from reshapes, kernel-launch overhead (many tiny ops),
unfused elementwise chains.
Inference optimizations (when each wins)
| Technique | Wins on | Idea |
|---|---|---|
| FlashAttention | long seq, memory-bound | tile + online softmax; never materialize N×N. Exact. |
| KV cache | autoregressive decode | reuse past K/V; O(1)/token attention (memory grows w/ len) |
| Speculative decoding | latency, batch 1 | small draft proposes, big model verifies in parallel. Exact. |
| Continuous batching | server throughput | evict finished + admit new each step; keep HW saturated |
| Quantization | memory-bound / fit | move fewer bytes; fit the model in memory |
Edge (batch 1, power-limited): FlashAttention ✅, quantization ✅✅, KV-cache quant ✅, spec-decode (if draft fits memory) ☑, continuous batching ➖ (server-side).
FX graph dialects (the silent bug)
| capture | node ops you see | example target |
|---|---|---|
symbolic_trace(module) | call_module | "l1", "act" |
| Dynamo (default) | call_function (torch fns) | torch.relu, torch._C._nn.linear |
make_fx / aot | call_function (aten) | aten.linear.default |
A pass that matches the wrong dialect silently does nothing (no fusion, 0 NPU nodes). Match by function identity AND aten string to be robust. Fuse only when the producer has 1 user.
NPU mental model (Qualcomm)
LOVES : static shapes · dense regular dataflow · fixed-point (INT8/INT4) · supported ops · on-chip
HATES : dynamic shapes · data-dependent control flow · gather/scatter (MoE) · unsupported ops · DRAM round-trips
- Coverage = % compute on NPU. Treat as a vital sign; one unsupported hot op → CPU fallback → latency death.
- Fixed-point accumulator can overflow — size scales carefully.
- Budget is joules, not just ms (power ↔ thermal throttle ↔ latency).
- Sim validates accuracy; trust the device for performance; reconcile when they diverge.
Consistency: every optimization must be verified
quantize → eval accuracy delta (P09)
compile/fuse/export → numerical-equivalence check vs eager (atol/rtol)
spec-decode → bit-exact to base model
deploy to NPU → eval ON DEVICE (numerics differ from sim)
Fast-but-wrong is a shipped bug. No optimization is "done" until its accuracy is proven.
Evaluation rigor
report: score + variance/CI + baseline delta + per-example churn (not a lone number)
reproducible: fixed seed/data/config
is-it-real?: bootstrap CI / McNemar (paired); effect must exceed the noise band
Pareto: accuracy vs latency is a FRONTIER; ship at the KNEE for the device's budget
regression CI: block merges that drop accuracy below threshold
The lab verification convention (this track)
# tests import `lab` (your code) if present, else `solution`:
pytest test_*.py # against your lab.py (NotImplementedError until you fill it)
# to verify the reference solution, shadow lab.py with solution.py, then pytest.
Heavy/proprietary deps (C++ toolchain, TensorFlow, TVM, Qualcomm AI Hub SDK) make a few labs skip gracefully rather than fail — read the code and run them where the toolchain exists.