Data · October 23, 2026
We checked our local-LLM speed estimates against 52 real benchmarks
Every machine page on this site shows a tokens-per-second number for each model. Those are estimates, not our own benchmarks, so we went and found 52 published runs to see how wrong we were. Short answer: pretty wrong on MoE models. Here's what we found, what we changed, and where we're still off.
Why simple bandwidth math goes wrong
The usual rule of thumb for local LLMs goes like this. To generate one token, the machine has to read every active weight from memory once, so tokens per second is roughly memory bandwidth divided by model size. That's what we used: 0.6 × bandwidth ÷ active weights, with the 0.6 standing in for "you never get the full spec-sheet bandwidth."
For big dense models that's in the right ballpark. It was actually a bit pessimistic. Llama 3.3 70B on Strix Halo measures 4.8 tok/s, and we said 3.6. Modern llama.cpp builds get closer to 80–90% of peak bandwidth on large reads, not 60%.
The real problem is mixture-of-experts (MoE) models. A model like gpt-oss-120b only touches about 5B parameters per token, so the formula sees a tiny read and predicts huge speeds. On an RTX PRO 6000 it predicted 397 tok/s for gpt-oss-120b. The measured number is 196. On an M3 Ultra, gpt-oss-20b came out at 257 predicted against 116 measured. That's more than 2× too high.
What the formula misses is that every token also has a fixed cost that doesn't scale with model size: launching GPU kernels, attention, and for MoE models, routing each token to its experts. When the weight read only takes a millisecond or two, that fixed cost is a big share of the time. Then a faster GPU barely helps, because it only speeds up the part that was already small.
The new formula, in plain words
time per token = active weights ÷ (efficiency × bandwidth) + fixed overheadtokens/sec = 1 ÷ time per token
So there are two knobs per platform. Efficiency is how much of the spec-sheet bandwidth you actually get on big reads. Overhead is the fixed cost per token, with one value for dense models and a larger one for MoE. We fitted both to the benchmarks below. For each platform we picked the values that minimise the error in log terms, so a 20% miss on a slow run counts the same as a 20% miss on a fast one.
| Platform | Bandwidth efficiency | Overhead, dense | Overhead, MoE | Runs fitted |
|---|---|---|---|---|
| NVIDIA GeForce / RTX PRO (CUDA) | 83% | 1.0 ms | 2.5 ms | 12 |
| DGX Spark / GB10 (CUDA) | 77% | 0.0 ms | 3.4 ms | 7 |
| AMD Strix Halo (Vulkan / ROCm) | 80% | 0.1 ms | 5.1 ms | 10 |
| Apple M Pro / Max (Metal / MLX) | 90% | 1.2 ms | 5.3 ms | 8 |
| Apple M Ultra (Metal / MLX) | 84% | 1.8 ms | 6.5 ms | 6 |
A few things stand out. NVIDIA cards have the smallest MoE overhead (2.5 ms), which is why they keep the lead on small-active MoE models. Apple and Strix Halo pay roughly twice that, around 5–6 ms per token. The M Ultra chips also lose a bit of efficiency compared with a single-die M Max. That fits the common report that two fused dies don't quite give you double the speed.
Before and after
On the 43 runs we fitted to, the median error dropped from 26% to 5%. 37 of 43 now land within ±15%, up from 12. The worst miss went from 122% to 24%.
Fitting to the data and then grading yourself on the same data flatters the result. So we also kept 9 runs out of the fit and only checked them afterwards. On those, the median error went from 34% to 10%, and 7 of 9 are within ±15% (the old formula got 0). That held-out set includes Jeff Geerling's Ollama runs of a 70B model on a Framework Desktop, a Dell GB10 and an M3 Ultra, plus the Strix Halo result that started all this: Qwen3.6 35B-A3B measured at 63 tok/s, where we used to say 84.5 and now say 71.5.