Vibe Engines
YouTube

Vibe Engines is free, interactive AI engineering & system design — runs in your browser. Pick a learning path

Handbooks  /  On-Device AI
AI Engineering~12 min readIntermediate
Deep Dive

Small models, everywhere: the on-device AI handbook.

The frontier gets the headlines, but a quieter revolution shipped in the other direction: models small enough to run on your laptop, your phone, your factory floor — and good enough that, for most everyday tasks, users can't tell. Here's why small got good, the physics that governs it, the stack that runs it, and when local beats the cloud.

01

Why small models suddenly got good

In 2022, "small" meant "toy." Today a modern 8B model beats the 175B giants of that era on most practical tasks. Two forces did it. Distillation: frontier models now generate and grade the training data, so a small model inherits capability instead of rediscovering it from raw internet text — every frontier release quietly raises the ceiling for the small models trained in its shadow. And data quality: the phi-family experiments proved that textbook-grade tokens beat web-scale sludge, parameter for parameter.

→ The mental model

Model capability isn't a function of size alone — it's size × data quality × teacher quality. Two of those three kept improving even where size stayed fixed. Small models ride the frontier's coattails, about 12–18 months behind at a hundredth the cost.

As a rule of thumb, not a standard, sizes sort roughly like this: sub-2B models for classification, extraction and autocomplete on phones; 3–8B for chat, summarization and RAG on laptops; 8–14B approaching last year's frontier for reasoning-lite work on a single consumer GPU.

02

The physics: it's bandwidth, not FLOPs

Local inference speed obeys one equation. Generating a token means streaming essentially all active weights through the processor once. So:

→ The napkin law

tokens/sec ≈ memory bandwidth ÷ model size in bytes. A laptop with 100 GB/s bandwidth runs a 4 GB model at ~25 tok/s and an 8 GB model at ~12. Compute barely enters the picture — decode is a memory workout.

Everything about the on-device world follows from this law. Quantization works because INT4 weights are 4× fewer bytes than FP16 — near-4× more tokens/sec, same bandwidth. Apple Silicon punches high because unified memory offers 100–800 GB/s to a big pool. NPUs matter for prefill and energy, but decode still queues at the bandwidth window. Size your expectations with the GPU VRAM calculator — the same math, one slider away.

Both halves are one multiplication. A model's size is params × bits/8, and quantization shrinks it enough to fit and speeds decode by the same factor:

7B model on an 8GB phone: FP16 = 14GB (won't fit, 7 tok/s) · INT4 = 3.5GB (fits, 28 tok/s) — 4× smaller, 4× faster

Same bandwidth, a quarter of the bytes, so four times the tokens per second — and it finally fits in RAM. The runnable version below computes fit and speed for any model, precision, and device.

03

Two phases, two bottlenecks: prefill, decode and heat

A request runs in two phases that stress different hardware. Prefill processes the whole prompt in parallel, so it is limited by compute, and its cost shows up as time to first token. Decode then emits one token at a time, so it is limited by the memory bandwidth described above, and its cost shows up as tokens per second. That split explains why an NPU can make a long prompt feel instant while chat speed barely changes, and why trimming a prompt helps latency more than a faster chip does.

Phones add a third constraint: heat. Sustained decode keeps the chip busy for as long as the answer runs, and the OS responds by throttling clocks. Apple's ProcessInfo.thermalState documentation says only that at higher thermal states "your app should reduce usage of system resources." So treat the speed from a short benchmark as a ceiling. Measure a five-minute run, and shorten outputs or fall back to the cloud when the thermal state climbs.

04

The memory you actually get: KV cache and per-app limits

Weights are not the only thing that has to fit. Every token in the context adds a key and a value vector for each layer, and that KV cache grows with context length. For a model with L layers, Hkv key-value heads and head size d, FP16 costs 2 × L × Hkv × d × 2 bytes per token. Take Llama 3.2 3B, whose published config has 28 layers, 8 key-value heads and a head size of 128:

2 × 28 × 8 × 128 × 2 bytes = 114,688 bytes per token (~112 KiB)
8,192 tokens of context  →  ~0.94 GB of cache, on top of ~1.8 GB of INT4 weights (3B × 0.5 byte, rounded up for quantization overhead)

Grouped-query attention (8 KV heads instead of 24) already cut this by 3×. The KV Cache Calculator runs the same arithmetic for any config.

Then comes the cap you don't control. A phone app doesn't get "the device's RAM"; it gets a per-app limit, and the OS may terminate it above that. On iOS, Apple's com.apple.developer.kernel.increased-memory-limit entitlement tells the system that core features "may perform better by exceeding the default app memory limit on supported devices", and Apple adds that your app must still behave correctly if the extra memory isn't available. os_proc_available_memory() returns the bytes your app may still allocate, which Apple documents as advisory and warns "don't cache it." Budget from that number at load time, not from the spec sheet.

05

The stack that actually runs things

LayerThe toolWhat it does
Enginellama.cppThe C++ inference engine behind most local AI; runs GGUF-quantized models on CPU, GPU, and Metal.
FormatGGUF (Q4/Q5/Q8…)Pre-quantized model files — the de facto distribution format for local weights.
ServerOllamaDeveloper-friendly wrapper: pull a model like a Docker image, get an OpenAI-compatible local API.
AppleMLXApple's array framework — the fastest path on M-series unified memory.
BrowserWebGPU runtimesIn-browser inference: zero install, model ships with the web page.
PhoneCore ML / AICoreVendor NPU runtimes running distilled sub-4B models inside iOS and Android.

The pattern across the stack: OpenAI-compatible local APIs everywhere, so the same application code targets a cloud frontier model in production and a local 8B in development — or routes between them at runtime.

06

Named models, quantization and runtimes

Quantization is where small models earn their fit. AWQ (Activation-aware Weight Quantization, MLSys 2024 Best Paper) starts from the observation that "protecting only 1% salient weights can greatly reduce quantization error," and that those channels are found from the activation distribution, not the weights. It scales them up before rounding, and needs no backpropagation or reconstruction. Quantization-aware training (QAT) goes further by simulating low precision during training. Google's Gemma 3 QAT release (April 2025) cut the 27B model's weight memory from 54 GB (BF16) to 14.1 GB (int4), which fits a single RTX 3090, and reduced the perplexity drop from quantizing by 54%. The same post lists the 4B model at 2.6 GB and the 1B at 0.5 GB, weights only.

ModelWhat it shows
Gemma 3n (Google, June 2025)E2B and E4B: raw parameter counts of 5B and 8B, but running in as little as 2 GB and 3 GB of memory. Per-Layer Embeddings keep much of the embedding work out of fast accelerator memory, and MatFormer nests the smaller model inside the larger one. It takes image, audio, video and text input.
Apple on-device model (July 2025 tech report)About 3B parameters, with 2-bit quantization-aware training and KV-cache sharing, exposed to developers through the Swift Foundation Models framework with guided generation and tool calling.

For runtimes, ExecuTorch 1.0 (October 2025) deploys PyTorch models to phones, embedded boards and laptops without converting formats. It promoted XNNPACK, Core ML, Qualcomm's Hexagon NPU path and Arm Ethos-U to production-ready, and Meta says it powers on-device features in Instagram, WhatsApp, Messenger and Facebook. Next to llama.cpp, Ollama and MLX from the stack table, that's the "export from PyTorch" option: pick it when you want the model you trained and quantized in PyTorch to be the model that ships.

07

When on-device wins — and when it doesn't

The decision, honestly

Local wins on

  • Privacy — data physically never leaves
  • Latency — no network round-trip, instant first token
  • Offline — planes, fields, factories, air-gaps
  • Unit cost — zero marginal cost per request at scale
  • Residency — regulation that forbids the cloud

Cloud wins on

  • Frontier reasoning — hard multi-step work
  • Long contexts — local KV caches eat scarce RAM
  • Freshest capability — new models land in the API first
  • Fleet simplicity — one deployment, not a million devices
  • Burst compute — batch jobs beyond any laptop

The mistake is framing it as either/or. The pattern that ships in 2026 is hybrid: a local model handles the instant, private, frequent 80% — autocomplete, classification, summarizing the meeting you just had — and a router-style fallback escalates the hard 20% to the cloud, with user consent where privacy is the point.

08

Hybrid patterns that actually ship

Four production hybrids
Local-firstSmall model answers immediately; escalate to cloud on low confidence or explicit user request. The assistant pattern.
Split by taskEmbeddings, classification and extraction local (cheap, private); generation and reasoning cloud. The RAG pattern — retrieval never leaves the device.
Draft & verifyThe local model drafts, the cloud model verifies or polishes — speculative decoding's logic, applied across the network boundary.
Cloud dev, local shipPrototype against a frontier API, then distill/fine-tune a small model on the traces once the task is pinned down — frontier quality, local economics.

That last pattern deserves emphasis: a LoRA/QLoRA fine-tune of an 8B model on a few thousand task-specific examples routinely beats a prompted frontier model on that one task — at zero marginal cost, forever. Narrow beats general when you own the narrow.

09

The craft: choosing and proving a small model

Day-to-day, on-device work is an evals discipline. Leaderboard rank tells you little — small models vary wildly by task, and quantization moves quality in task-specific ways. The working loop: shortlist three candidate models in your size budget, build a golden set of 50–200 real examples from your task, score each model × quantization level (Q4 vs Q8 can matter more than model choice), and watch the tails — small models fail weirder than big ones, so read the worst 10 outputs, not the average.

→ The rule that saves weeks

Fix the memory budget first (device RAM − OS − your app), then shop for the best model+quant that fits with room for the KV cache. Teams that pick the model first and discover the budget later ship six weeks late, twice.

RUN IT YOURSELF

Will it fit, and how fast will it run?

On-device inference is one napkin law: a model's size is params × bits/8, and decode speed is memory bandwidth ÷ that size. This checks a 7B model on an 8 GB phone at three precisions — FP16 needs 14 GB and won't fit, INT8 just fits, INT4 fits comfortably at 3.5 GB — and computes tokens per second for each. Quantizing FP16 → INT4 quarters the bytes, so it fits and runs about four times faster on the same bandwidth. Change the model size, precision, RAM, or bandwidth.

CPython · WebAssembly
Frequently asked

Quick answers

Why did small models get good?

Distillation (frontier models generate the training data) plus textbook-quality data curation. Small models inherit capability ~12–18 months behind the frontier at a hundredth the cost.

What limits local inference speed?

Memory bandwidth: tokens/sec ≈ bandwidth ÷ model bytes. Quantization speeds decode almost linearly because it shrinks the bytes.

When should AI run on-device?

Privacy, instant latency, offline, zero marginal cost, or data-residency rules. The cloud keeps frontier reasoning, long contexts and the newest capabilities.

What is the standard local stack?

llama.cpp + GGUF underneath, Ollama as the friendly server, MLX on Apple Silicon, WebGPU in browsers, Core ML / AICore on phones.

What limits on-device AI besides model size?

Three things beyond weights. Prefill is compute-bound and sets time to first token, while decode is memory-bandwidth-bound and sets tokens per second. The KV cache grows with context, about 112 KiB per token for Llama 3.2 3B in FP16, roughly 0.94 GB at 8,192 tokens. And phones enforce a per-app memory limit and throttle clocks when they heat up, so budget from the memory the OS reports available and benchmark a sustained run.

Small Models & On-Device AI · AI Engineering · Vibe Engines · 2026
Finished this one? 0 / 208 Handbooks done

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More Handbooks