It finds the fastest local-LLM setup for your exact Arm chip. Then it proves it.
Android · arm64 · free and open source · Arm Create : AI Optimization Challenge 2026 · Mobile AI Track
Android phones run wildly different Arm silicon underneath the same OS — different matrix instructions, different core layouts, different memory bandwidth. Almost every local-LLM app ships one generic build and hopes for the best, leaving most of the phone's speed unused.
PocketTune exists to answer, on the phone in your hand, what the spec sheet can't tell you.
On-device LLM apps compile once, generically — using none of the SIMD instructions the chip actually has.
The chip with more matrix instructions should win. Measured on three phones, it doesn't — the winner has the fewest features on paper.
Extra threads land on slow little cores and everyone waits for them. The right count differs per phone.
No public per-phone dataset says which config wins where — so every phone has to find out for itself.
These are the speed ranges across , each one measured against its own unoptimized baseline. The spread is the finding: the same optimization is worth a different amount on different silicon, so no single number describes your phone. See the per-device evidence ›
One panel per device we've benchmarked. Same model, same workload — only the build changes.
Read each ladder top to bottom. The speedup arrives with the compiler flags — permission to use the matrix instructions that particular silicon already has. Which instructions exist, and what they're worth, is a property of the chip, not of the app. That's the whole argument for measuring instead of assuming.
One build flag multiplies prompt processing on every phone we've run — it lets the compiler emit the SIMD instructions the chip already has. Most Android builds never pass it. How much it buys is the part that moves, and it moves unpredictably: 3.88×, 4.94×, 5.59× across three phones. It does not simply track the feature list — the biggest winner is a chip with no i8mm at all.
Arm's own acceleration library, tested four ways on an i8mm chip: no measurable gain for Q4_0, because plain compiled code already saturates the hardware. On other quants and other silicon it may well flip. It stays in the sweep for exactly that reason.
The winning thread count on the three phones — and no, it isn't “all of them”. The two big.LITTLE chips want 2; the tri-cluster Pixel wants 4, because it has exactly four fast cores. Ask it for 6 and prefill falls from 138 to 98 tok/s: the little cores become the bottleneck everyone waits for.
Every one contradicts what a reasonable developer would assume — and none of them generalizes across chips by default. That's the point: your phone's fastest configuration is an empirical question, so PocketTune answers it on your phone.
Which fast instructions does this chip have — dotprod? i8mm? How many fast versus slow cores? Read straight off the device.
Compression formats, instruction paths, thread counts — measured for speed, memory and energy per token, right on the phone.
The best configuration isn't a report. It becomes the app's live settings, chosen on evidence.
A private assistant running the tuned setup. Airplane mode is the demo — nothing leaves the phone.
Every line we wrote is TypeScript. Every fast path below it is measured, never assumed.
React Native, all TypeScript. Device reads the silicon, Models manages GGUF downloads, Tune runs the sweep and applies the winner, Chat runs it offline, Lab holds the evidence.
Builds the sweep plan from this phone's core layout, drives the benchmarks, scores every configuration (weighted 65% decode, 35% prefill), samples battery power for tokens per joule, and persists the applied config and history.
A prebuilt binding to the engine — no native code written in this project. It ships six arm64 kernel builds and picks one at startup by reading the chip's features: i8mm silicon gets the i8mm build, a dotprod-only chip gets the dotprod build.
Loads the GGUF model and runs the matmuls. One context lives at a time — a phone doesn't have RAM for two — and every engine call is serialized, so a mid-benchmark tab switch can never corrupt a measurement.
The ground truth: which matrix instructions exist (dotprod? i8mm? SVE2?) and where the big cores sit. Different on every phone — which is exactly why every layer above asks instead of assumes.
The whole stack exists to move one decision — which configuration to run — out of guesswork and into a measurement made on the phone in your hand.
Build-time levers are baked into the binaries and proved by the harness. Runtime levers are what the app tunes on your phone. Every one is measured — and two of the "obvious wins" turned out to be worth nothing.
-march=armv8.2-a+dotprod+i8mm gives the compiler permission to emit the chip's matrix-multiply instructions. Generic arm64 builds — what most apps ship — use none of them. This single flag is the largest gain in the entire project.
At model load, llama.cpp rearranges Q4_0 weight blocks into the tile order the i8mm instructions consume — so the SIMD units stream contiguous data instead of gathering it. Free at inference time; it costs a moment at load.
Arm's hand-written matmul routines — the thing everyone assumes brings the speed. Tested four ways on the i8mm chip, and again on two dotprod-only ones: once the compiler flags are right, plain compiled code already saturates the hardware for Q4_0. Three phones, three flat results. It stays in the sweep because other silicon and other quantizations may disagree.
Decode speed is bounded by memory traffic, not arithmetic — the CPU mostly waits for weights to arrive from RAM. Shrinking every weight from 16 bits to ~4 cuts that traffic roughly 4×, and is what makes a 1B-parameter model fit on a phone at all.
More threads is worse when the extra workers are little cores — the whole team waits for the slowest. And the winning count is different on every phone: the two big.LITTLE chips want 2 threads, while the tri-cluster Pixel wants 4, because it has exactly four fast cores (2× X1 + 2× A78) and the 5th thread spills onto an A55. The app derives its candidates from this phone's actual core map instead of defaulting to "all of them".
Flash attention computes attention in cache-sized tiles instead of materializing the whole score matrix; a q8_0 KV cache halves the memory the growing conversation occupies. Both help some chips and hurt others — which is why they're swept, not defaulted.
Where the kernel exposes the battery's power rails, every configuration is also scored for energy per token. On a phone, a config that's 5% faster but 30% hungrier is a bad trade — efficiency is a ranking axis, not an afterthought.
Measured numbers come from the three phones that have actually been through the harness — MediaTek and Google silicon, one with i8mm and two without — and trace to raw JSON in results/. They disagree, and the disagreement is the finding: the same compiler flag buys 3.88×, 4.94× and 5.59×, and the biggest winner is a chip with no i8mm. You cannot read the answer off a spec sheet. The kind of each optimization generalizes to any Arm phone; its value does not. That gap is the product.
Plug in any Android phone. The harness reads its silicon, sweeps the builds, and writes its own JSON — no phone is special-cased.
Every component — quantization, i8mm, compiler flags, the harness — explained from zero.