The knobs that actually move the needle in production
| Knob | What to do | Why it works |
|---|---|---|
| Datatype | Prefer FP16 / BF16 weights; keep FP32 accumulation only when stability demands | Hits the matrix unit / Tensor Core path |
| Hidden size | Round to 64 or 128 | Fills 128×128 block tile perfectly; avoids padding-fallback |
| Memory placement | Weights → private; activations → shared only if CPU must read | Keeps hot data in on-chip SRAM |
| Warm-up pass | Run one dummy inference after loading weights | Caches the library's best algorithm choice |
| Batch size | Push as high as memory allows for inference | GEMV → GEMM, bandwidth-bound → compute-bound |
| Algorithm caching | Persist cuBLASLt / autotune choices across runs | Skip 1–10s of cold-start algo search |
Common face-plants & quick fixes
| Symptom | Probable cause | Fix |
|---|---|---|
| GEMM < 40% F32 utilization | Hidden size not a multiple of 16 | Pad M and/or K to 64 / 128 |
| Throughput halves overnight | Forgot to enable reduced-precision flag after a library upgrade | Re-enable, rebuild, document the flag |
| Big gaps between command buffers | CPU tokenizer or I/O stalling the queue | Async pre-tokenize; keep enough work queued |
| Library swaps to GEMV path silently | Tiny m or k (e.g. head_dim = 32 for single-token decode) | Fuse heads or rewrite KV-cache to keep batch ≥ 8 |
| Inference latency varies 5× | Library re-running heuristic per shape | Cache algorithm choice; sort requests by shape |
Bottom line
GEMM is 90% of the math in a transformer. Feed the library shapes it loves (padded, batched, reduced-precision, hidden-size on the right tile boundary), glance at utilization counters when something slows, let BLAS do the heavy lifting. Hand-rolled kernels are for learning (this quest!) and for shapes the library genuinely doesn't cover well — not as a default.