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.

26% → 5%median error, 43 fitted runs
12 → 37of 43 runs within ±15%
34% → 10%median error, 9 held-out runs
122% → 24%worst miss, fitted runs

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 overhead
tokens/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.

PlatformBandwidth efficiencyOverhead, denseOverhead, MoERuns fitted
NVIDIA GeForce / RTX PRO (CUDA)83%1.0 ms2.5 ms12
DGX Spark / GB10 (CUDA)77%0.0 ms3.4 ms7
AMD Strix Halo (Vulkan / ROCm)80%0.1 ms5.1 ms10
Apple M Pro / Max (Metal / MLX)90%1.2 ms5.3 ms8
Apple M Ultra (Metal / MLX)84%1.8 ms6.5 ms6

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.

Estimated vs measured tokens per second, old and new formulaLog-log scatter of 52 benchmark runs. Points on the dashed diagonal are perfect estimates. The old formula's grey rings sit well above the line for fast MoE runs; the new formula's dots cluster on it.3355101020205050100100200200500500Old: DGX Spark, Qwen2.5-Coder 7B Q8_0: 20 est vs 29 measuredOld: DGX Spark, Gemma 3 4B Q4_0: 65 est vs 81 measuredOld: DGX Spark, gpt-oss-120b MXFP4: 60 est vs 59 measuredOld: DGX Spark, gpt-oss-20b MXFP4: 86 est vs 83 measuredOld: DGX Spark, Qwen3-Coder 30B-A3B Q8_0: 47 est vs 61 measuredOld: DGX Spark, GLM-4.7-Flash Q8_0: 51 est vs 49 measuredOld: DGX Spark, Qwen3.5-122B-A10B Q5_K_S: 24 est vs 23 measuredOld: Strix Halo, Llama-2 7B Q4_0: 40 est vs 53 measuredOld: Strix Halo, Qwen3 8B Q4_K_M: 31 est vs 41 measuredOld: Strix Halo, Mistral Small 24B Q4_K_M: 11 est vs 14 measuredOld: Strix Halo, Llama 3.3 70B Q4_K_M: 4 est vs 5 measuredOld: Strix Halo, Qwen3 30B-A3B Q4_K_M: 77 est vs 86 measuredOld: Strix Halo, Qwen3 30B-A3B UD-Q4_K_XL: 77 est vs 75 measuredOld: Strix Halo, Qwen3-Coder-Next 80B-A3B Q4_K_M: 84 est vs 58 measuredOld: Strix Halo, Llama 4 Scout UD-Q4_K_XL: 15 est vs 20 measuredOld: Strix Halo, gpt-oss-120b MXFP4: 57 est vs 48 measuredOld: Strix Halo, Qwen3 235B-A22B IQ3_XS: 17 est vs 18 measuredOld: M5 Max 40c, Llama-2 7B Q4_0: 97 est vs 120 measuredOld: M5 Max 40c, Llama-2 7B Q8_0: 51 est vs 72 measuredOld: M5 Pro, Llama-2 7B Q4_0: 48 est vs 68 measuredOld: M5 Pro, Llama-2 7B Q8_0: 26 est vs 39 measuredOld: M5 Max 40c, Qwen3.6-27B Q8_0: 13 est vs 18 measuredOld: M5 Max 40c, Qwen3.6-35B-A3B Q8_0: 116 est vs 93 measuredOld: M5 Max 40c, gpt-oss-120b MXFP4: 136 est vs 88 measuredOld: M4 Max 40c, gpt-oss-20b MXFP4: 171 est vs 118 measuredOld: M5 Ultra, Llama-2 7B Q4_0: 193 est vs 179 measuredOld: M5 Ultra, Llama-2 7B Q8_0: 103 est vs 119 measuredOld: M3 Ultra 80c, Llama 3.3 70B 4-bit: 12 est vs 17 measuredOld: M3 Ultra 80c, gpt-oss-20b MXFP4: 257 est vs 116 measuredOld: M2 Ultra 76c, gpt-oss-120b MXFP4: 177 est vs 80 measuredOld: M2 Ultra 76c, gpt-oss-20b MXFP4: 251 est vs 116 measuredOld: RTX PRO 6000, Llama-2 7B Q4_0: 282 est vs 274 measuredOld: RTX PRO 6000, Llama 3.3 70B Q4_K_M: 25 est vs 34 measuredOld: RTX PRO 6000, gpt-oss-120b MXFP4: 397 est vs 196 measuredOld: RTX PRO 6000, gpt-oss-20b MXFP4: 562 est vs 287 measuredOld: RTX 5090, Llama-2 7B Q4_0: 282 est vs 290 measuredOld: RTX 5090, Qwen3.6-27B Q4_K_M: 64 est vs 77 measuredOld: RTX 5090, Qwen3.6-35B-A3B UD-Q4_K_M: 591 est vs 271 measuredOld: RTX 5090, gpt-oss-20b MXFP4: 562 est vs 282 measuredOld: RTX 4090, Llama-2 7B Q4_0: 158 est vs 186 measuredOld: RTX 4090, Qwen3 32B Q4_K_M: 30 est vs 42 measuredOld: RTX 4090, gpt-oss-20b MXFP4: 316 est vs 222 measuredOld: RTX 4090, Qwen3 30B-A3B Q4_K_M: 302 est vs 198 measuredOld: Strix Halo, Qwen3.6 35B-A3B Q4_K_M: 84 est vs 63 measuredOld: M5 Ultra, gpt-oss-120b MXFP4: 266 est vs 99 measuredOld: M5 Max 40c, gpt-oss-120b MXFP4: 136 est vs 93 measuredOld: RTX PRO 6000, gpt-oss-120b MXFP4: 397 est vs 221 measuredOld: RTX 5090, Qwen3 30B-A3B Q4_K_M: 537 est vs 352 measuredOld: RTX 5090, gpt-oss-20b MXFP4: 562 est vs 419 measuredOld: Framework Desktop (Strix Halo), Llama 3.1 70B Q4_K_M: 4 est vs 5 measuredOld: Dell Pro Max GB10, Llama 3.1 70B Q4_K_M: 4 est vs 5 measuredOld: M3 Ultra 512GB, Llama 3.1 70B Q4_K_M: 12 est vs 14 measuredNew: DGX Spark, Qwen2.5-Coder 7B Q8_0: 26 est vs 29 measuredNew: DGX Spark, Gemma 3 4B Q4_0: 83 est vs 81 measuredNew: DGX Spark, gpt-oss-120b MXFP4: 61 est vs 59 measuredNew: DGX Spark, gpt-oss-20b MXFP4: 80 est vs 83 measuredNew: DGX Spark, Qwen3-Coder 30B-A3B Q8_0: 50 est vs 61 measuredNew: DGX Spark, GLM-4.7-Flash Q8_0: 54 est vs 49 measuredNew: DGX Spark, Qwen3.5-122B-A10B Q5_K_S: 28 est vs 23 measuredNew: Strix Halo, Llama-2 7B Q4_0: 53 est vs 53 measuredNew: Strix Halo, Qwen3 8B Q4_K_M: 41 est vs 41 measuredNew: Strix Halo, Mistral Small 24B Q4_K_M: 14 est vs 14 measuredNew: Strix Halo, Llama 3.3 70B Q4_K_M: 5 est vs 5 measuredNew: Strix Halo, Qwen3 30B-A3B Q4_K_M: 67 est vs 86 measuredNew: Strix Halo, Qwen3 30B-A3B UD-Q4_K_XL: 67 est vs 75 measuredNew: Strix Halo, Qwen3-Coder-Next 80B-A3B Q4_K_M: 72 est vs 58 measuredNew: Strix Halo, Llama 4 Scout UD-Q4_K_XL: 18 est vs 20 measuredNew: Strix Halo, gpt-oss-120b MXFP4: 55 est vs 48 measuredNew: Strix Halo, Qwen3 235B-A22B IQ3_XS: 20 est vs 18 measuredNew: M5 Max 40c, Llama-2 7B Q4_0: 123 est vs 120 measuredNew: M5 Max 40c, Llama-2 7B Q8_0: 71 est vs 72 measuredNew: M5 Pro, Llama-2 7B Q4_0: 67 est vs 68 measuredNew: M5 Pro, Llama-2 7B Q8_0: 37 est vs 39 measuredNew: M5 Max 40c, Qwen3.6-27B Q8_0: 19 est vs 18 measuredNew: M5 Max 40c, Qwen3.6-35B-A3B Q8_0: 90 est vs 93 measuredNew: M5 Max 40c, gpt-oss-120b MXFP4: 98 est vs 88 measuredNew: M4 Max 40c, gpt-oss-20b MXFP4: 109 est vs 118 measuredNew: M5 Ultra, Llama-2 7B Q4_0: 182 est vs 179 measuredNew: M5 Ultra, Llama-2 7B Q8_0: 114 est vs 119 measuredNew: M3 Ultra 80c, Llama 3.3 70B 4-bit: 17 est vs 17 measuredNew: M3 Ultra 80c, gpt-oss-20b MXFP4: 108 est vs 116 measuredNew: M2 Ultra 76c, gpt-oss-120b MXFP4: 95 est vs 80 measuredNew: M2 Ultra 76c, gpt-oss-20b MXFP4: 107 est vs 116 measuredNew: RTX PRO 6000, Llama-2 7B Q4_0: 280 est vs 274 measuredNew: RTX PRO 6000, Llama 3.3 70B Q4_K_M: 34 est vs 34 measuredNew: RTX PRO 6000, gpt-oss-120b MXFP4: 231 est vs 196 measuredNew: RTX PRO 6000, gpt-oss-20b MXFP4: 264 est vs 287 measuredNew: RTX 5090, Llama-2 7B Q4_0: 280 est vs 290 measuredNew: RTX 5090, Qwen3.6-27B Q4_K_M: 81 est vs 77 measuredNew: RTX 5090, Qwen3.6-35B-A3B UD-Q4_K_M: 269 est vs 271 measuredNew: RTX 5090, gpt-oss-20b MXFP4: 264 est vs 282 measuredNew: RTX 4090, Llama-2 7B Q4_0: 180 est vs 186 measuredNew: RTX 4090, Qwen3 32B Q4_K_M: 40 est vs 42 measuredNew: RTX 4090, gpt-oss-20b MXFP4: 209 est vs 222 measuredNew: RTX 4090, Qwen3 30B-A3B Q4_K_M: 204 est vs 198 measuredNew: Strix Halo, Qwen3.6 35B-A3B Q4_K_M: 72 est vs 63 measuredNew: M5 Ultra, gpt-oss-120b MXFP4: 109 est vs 99 measuredNew: M5 Max 40c, gpt-oss-120b MXFP4: 98 est vs 93 measuredNew: RTX PRO 6000, gpt-oss-120b MXFP4: 231 est vs 221 measuredNew: RTX 5090, Qwen3 30B-A3B Q4_K_M: 260 est vs 352 measuredNew: RTX 5090, gpt-oss-20b MXFP4: 264 est vs 419 measuredNew: Framework Desktop (Strix Halo), Llama 3.1 70B Q4_K_M: 5 est vs 5 measuredNew: Dell Pro Max GB10, Llama 3.1 70B Q4_K_M: 5 est vs 5 measuredNew: M3 Ultra 512GB, Llama 3.1 70B Q4_K_M: 16 est vs 14 measuredOld formula (0.6 × bandwidth)New formula, fitted runsNew formula, held-out runsWithin ±15% of measuredMeasured tokens/sec (log scale)Our estimate, tokens/sec (log scale)
Each vertical line connects the old estimate (ring) to the new one (dot) for the same run. Hover a point for details.

What changed on the site

Here's how the numbers on a few machine pages moved, at 4-bit. Big dense models went up by about a third. Small-active MoE models on fast hardware came down a lot.

Where we're still off

All 52 benchmark runs

Every row links to where the number came from. Errors within ±15% are green. Rows marked held out weren't used to fit the formula. Click a column header to sort.

Source
1DGX Spark
273 GB/s
Qwen2.5-Coder 7B Q8_0
llama.cpp tg32
Dense29.420.2−31%26.0−12%source
2DGX Spark
273 GB/s
Gemma 3 4B Q4_0
llama.cpp tg32
Dense81.064.9−20%83.4+3%source
3DGX Spark
273 GB/s
gpt-oss-120b MXFP4
llama.cpp tg32
MoE58.760.5+3%61.4+5%source
4DGX Spark
273 GB/s
gpt-oss-20b MXFP4
llama.cpp tg32
MoE83.485.6+3%80.0−4%source
5DGX Spark
273 GB/s
Qwen3-Coder 30B-A3B Q8_0
llama.cpp tg32
MoE61.146.7−23%49.8−18%source
6DGX Spark
273 GB/s
GLM-4.7-Flash Q8_0
llama.cpp tg32
MoE48.751.4+6%53.9+11%source
7DGX Spark
273 GB/s
Qwen3.5-122B-A10B Q5_K_S
llama.cpp tg128
MoE23.023.8+4%27.7+20%source
8Strix Halo
256 GB/s
Llama-2 7B Q4_0
llama.cpp Vulkan tg128
Dense52.740.2−24%53.4+1%source
9Strix Halo
256 GB/s
Qwen3 8B Q4_K_M
llama.cpp Vulkan tg128
Dense41.430.6−26%40.7−2%source
10Strix Halo
256 GB/s
Mistral Small 24B Q4_K_M
llama.cpp Vulkan tg128
Dense14.310.7−25%14.30%source
11Strix Halo
256 GB/s
Llama 3.3 70B Q4_K_M
llama.cpp Vulkan tg128
Dense4.83.6−25%4.80%source
12Strix Halo
256 GB/s
Qwen3 30B-A3B Q4_K_M
llama.cpp Vulkan tg128
MoE85.576.8−10%67.3−21%source
13Strix Halo
256 GB/s
Qwen3 30B-A3B UD-Q4_K_XL
llama.cpp Vulkan tg128
MoE75.376.8+2%67.3−11%source
14Strix Halo
256 GB/s
Qwen3-Coder-Next 80B-A3B Q4_K_M
llama.cpp Vulkan tg128
MoE57.584.5+47%71.5+24%source
15Strix Halo
256 GB/s
Llama 4 Scout UD-Q4_K_XL
llama.cpp Vulkan tg128
MoE20.214.9−26%18.0−11%source
16Strix Halo
256 GB/s
gpt-oss-120b MXFP4
llama.cpp ROCm tg128
MoE47.556.7+19%54.6+15%source
17Strix Halo
256 GB/s
Qwen3 235B-A22B IQ3_XS
llama.cpp Vulkan tg128
MoE18.116.9−6%20.2+12%source
18M5 Max 40c
614 GB/s
Llama-2 7B Q4_0
llama.cpp tg128
Dense119.996.5−19%123.4+3%source
19M5 Max 40c
614 GB/s
Llama-2 7B Q8_0
llama.cpp tg128
Dense72.451.4−29%70.6−2%source
20M5 Pro
307 GB/s
Llama-2 7B Q4_0
llama.cpp tg128
Dense67.648.3−29%66.6−1%source
21M5 Pro
307 GB/s
Llama-2 7B Q8_0
llama.cpp tg128
Dense38.925.7−34%36.9−5%source
22M5 Max 40c
614 GB/s
Qwen3.6-27B Q8_0
llama.cpp decode@128
Dense18.012.9−28%18.9+5%source
23M5 Max 40c
614 GB/s
Qwen3.6-35B-A3B Q8_0
llama.cpp decode@128
MoE93.0115.6+24%90.3−3%source
24M5 Max 40c
614 GB/s
gpt-oss-120b MXFP4
llama.cpp 4K ctx
MoE87.9136.0+55%98.0+12%source
25M4 Max 40c
546 GB/s
gpt-oss-20b MXFP4
llama.cpp tg128
MoE117.8171.3+45%108.8−8%source
26M5 Ultra
1,228 GB/s
Llama-2 7B Q4_0
llama.cpp tg128
Dense179.1193.1+8%181.8+2%source
27M5 Ultra
1,228 GB/s
Llama-2 7B Q8_0
llama.cpp tg128
Dense119.0102.9−14%114.4−4%source
28M3 Ultra 80c
819 GB/s
Llama 3.3 70B 4-bit
MLX (oMLX) 1K ctx
Dense17.012.4−27%16.8−1%source
29M3 Ultra 80c
819 GB/s
gpt-oss-20b MXFP4
llama.cpp tg128
MoE115.5256.9+122%107.8−7%source
30M2 Ultra 76c
800 GB/s
gpt-oss-120b MXFP4
llama.cpp tg128
MoE79.7177.2+122%95.0+19%source
31M2 Ultra 76c
800 GB/s
gpt-oss-20b MXFP4
llama.cpp tg128
MoE116.1251.0+116%107.0−8%source
32RTX PRO 6000
1,792 GB/s
Llama-2 7B Q4_0
llama.cpp tg128
Dense274.2281.7+3%280.4+2%source
33RTX PRO 6000
1,792 GB/s
Llama 3.3 70B Q4_K_M
llama.cpp tg128
Dense34.025.3−26%33.8−1%source
34RTX PRO 6000
1,792 GB/s
gpt-oss-120b MXFP4
llama.cpp tg128
MoE196.3396.8+102%231.4+18%source
35RTX PRO 6000
1,792 GB/s
gpt-oss-20b MXFP4
llama.cpp tg128
MoE286.9562.2+96%264.1−8%source
36RTX 5090
1,792 GB/s
Llama-2 7B Q4_0
llama.cpp tg128
Dense290.0281.7−3%280.4−3%source
37RTX 5090
1,792 GB/s
Qwen3.6-27B Q4_K_M
llama.cpp tg128
Dense77.164.0−17%81.3+5%source
38RTX 5090
1,792 GB/s
Qwen3.6-35B-A3B UD-Q4_K_M
llama.cpp tg128
MoE271.0591.2+118%268.6−1%source
39RTX 5090
1,792 GB/s
gpt-oss-20b MXFP4
llama.cpp tg128
MoE282.5562.2+99%264.1−6%source
40RTX 4090
1,008 GB/s
Llama-2 7B Q4_0
llama.cpp tg128
Dense186.2158.5−15%179.8−3%source
41RTX 4090
1,008 GB/s
Qwen3 32B Q4_K_M
llama.cpp tg128
Dense42.030.4−28%40.4−4%source
42RTX 4090
1,008 GB/s
gpt-oss-20b MXFP4
llama.cpp tg128
MoE222.0316.2+42%208.9−6%source
43RTX 4090
1,008 GB/s
Qwen3 30B-A3B Q4_K_M
llama.cpp tg128 (fusion off)
MoE198.4302.3+52%204.4+3%source
44Strix Halo
256 GB/s
Qwen3.6 35B-A3B Q4_K_Mheld out
Ollama/llama.cpp
MoE63.084.5+34%71.5+14%source
45M5 Ultra
1,200 GB/s
gpt-oss-120b MXFP4held out
MLX short prompt (concurrency caveat)
MoE99.0265.7+168%108.8+10%source
46M5 Max 40c
614 GB/s
gpt-oss-120b MXFP4held out
MLX (oMLX) 1K ctx
MoE92.8136.0+47%98.0+6%source
47RTX PRO 6000
1,792 GB/s
gpt-oss-120b MXFP4held out
llama.cpp 4K ctx (newer build)
MoE221.3396.8+79%231.4+5%source
48RTX 5090
1,792 GB/s
Qwen3 30B-A3B Q4_K_Mheld out
llama.cpp + GGML_CUDA_GRAPH_OPT=1
MoE352.1537.4+53%260.1−26%source
49RTX 5090
1,792 GB/s
gpt-oss-20b MXFP4held out
llama.cpp + GGML_CUDA_GRAPH_OPT=1
MoE419.1562.2+34%264.1−37%source
50Framework Desktop (Strix Halo)
256 GB/s
Llama 3.1 70B Q4_K_Mheld out
Ollama (Geerling)
Dense5.03.6−27%4.8−3%source
51Dell Pro Max GB10
273 GB/s
Llama 3.1 70B Q4_K_Mheld out
Ollama (Geerling)
Dense4.73.9−18%4.9+5%source
52M3 Ultra 512GB
819 GB/s
Llama 3.1 70B Q4_K_Mheld out
Ollama (Geerling)
Dense14.111.6−18%15.7+12%source

"Active GB per token" is active parameters × bits per weight ÷ 8. For dense GGUF files we back-solved the bits per weight from the file size. MoE rows use the same bits-per-weight values the rest of the site uses (Q4_K_M 4.85, Q8_0 8.5, MXFP4 4.25). M2 and M3 Ultra runs were used for the Ultra class at their own bandwidth. Spotted a benchmark we should add, or a mistake? Email hello@bestlocalairig.com. Full method: How we estimate.

See the numbers for your machine

Every machine page now uses the new formula. Pick a rig to see which open models fit and how fast they should run, or check the models page for memory needs.

We send one short email a week with price drops, restocks and new open models. If we re-run this check with new benchmarks, that's where it'll go first.

Restock & price-drop alerts

One short email a week: what got cheaper, what's back in stock, and which new open models your machine can run. Unsubscribe anytime.