Core AI model zoo

int8 LM head + measuring decode speed honestly

TL;DR: for a big-vocab decoder the fp16 lm_head is ~half the per-token memory read — untie it (if tied) and quantize to int8 (per-block-32 **symmetric / absmax, never clipping) for a large decode win at no quality cost. Then measure the win on the bundle you actually ship, in the actual app, as a same-conditions back-to-back A/B — controlled benchmarks, VLM image buffers, thermal, and the app’s own per-token UI work each move the number by tens of percent.**

The int8 head lever

A 100k–250k-row lm_head is read on every decoded token, and on a sub-2B transformer that matrix is roughly half of the per-token weight traffic. Quantizing it int8 ≈ halves that half.

The same model gives different decode numbers — don’t quote across them

Worked example: MiniCPM-V-4.6 (qwen3.5-0.8B class), iPhone 17 Pro, int8 head.

The app can be the bottleneck, not the model

A SwiftUI chat that re-decodes the whole token list with the tokenizer on every token (tokenizer.decode(fullSequence)) and pushes a view update is O(n²) and serializes against the model. It dragged both the measured and the experienced rate well below the model’s true decode speed, and got worse the longer the output (a 343-token reasoning trace measured ~10 tok/s low).

Op note: a wedged GPU looks like a code regression

After a day of repeated 1 GB+ bundle loads + GPU-heavy work, an iPhone can wedge: a model load hangs at cold-compile and devicectl reports the console connection invalidated. Reboot the device — it clears the Metal/GPU state and the load returns to normal. Before blaming code, confirm the load path is unchanged (here the only edits were UI-layer; the engine/backend were untouched) — heavy on-device sessions need an occasional reboot.