MLX vs llama.cpp — an Apple Silicon Performance Comparison, Measured

The eternal r/LocalLLaMA question, answered with a benchmark instead of a take — same Mac, same models, both frameworks' own tools, raw JSON published. Neither wins outright. What decides it is the shape of your workload.

"Should I run MLX or llama.cpp on my Mac?" gets asked weekly, and the answers are usually vibes. So we measured it: one idle M2 Pro Mac mini (32 GB), three models, each framework benchmarked with its own canonical tool — llama-bench for llama.cpp, mlx_lm generate for MLX — plus full server-path runs to confirm the raw numbers survive contact with a real OpenAI-compatible server. Medians of repeated runs, and the complete raw JSON is published.

Disclosure: we build rapid-mlx, an MLX-native server, so we have a horse in this race — which is why the numbers where llama.cpp wins are printed just as large.

The raw numbers

llama.cpp: llama-bench -p 512 -n 128 (Metal, build 10330, 5 reps). MLX: mlx_lm generate, ~490-token prompt, 128 tokens out, 3 reps, mlx-lm 0.31.3. Same quantization class per pair (native MXFP4 for gpt-oss; Q4_K_M ≈ 4.8 bpw vs MLX 4-bit ≈ 4.5 bpw for the others).

Generation (decode) — tokens/second:

Model MLX llama.cpp ratio
gpt-oss-20b (MoE, native MXFP4) 49.5 52.0 0.95×
Qwen3.6-35B-A3B (MoE, 4-bit) 61.7 39.1 1.58×
Gemma4-12B-it (dense, 4-bit) 22.1 19.7 1.12×

Prompt processing (prefill) — tokens/second:

Model MLX llama.cpp ratio
gpt-oss-20b 250 558 0.45×
Qwen3.6-35B-A3B 277 460 0.60×
Gemma4-12B-it 96 172 0.56×

That's the whole story in two tables: MLX decodes faster — hugely so on sparse MoE models — and llama.cpp prefills 1.7–2.2× faster, every time.

(A wrinkle inside the decode table: raw llama-bench actually edges raw MLX on gpt-oss. That lead didn't survive the server path — see below — but it's real at the kernel level and we're not hiding it.)

Do the raw numbers survive a real server?

Raw benchmarks flatter frameworks; users talk to servers. So we re-ran everything through OpenAI-compatible endpoints — llama-server for llama.cpp, rapid-mlx for MLX — streaming, temperature 0, medians of 3 (rapid-mlx column re-measured 2026-08-12 on 0.12.11, same machine and protocol; llama-server numbers are the 2026-08-11 measurements at build 10330):

Long-form decode (t/s) rapid-mlx (MLX) llama-server
gpt-oss-20b 46.7 42.6
Qwen3.6-35B-A3B 58.5 38.3
Gemma4-12B-it 21.3 18.3

The MoE gap (1.5×) carries straight through. The dense model, a tie at the raw-framework level and a tie on the server path in our original 0.12.9 run (18.7 vs 18.3), moved to a ~15% rapid-mlx edge on 0.12.11. And cold time-to-first-token flips the sign, exactly as the prefill table predicts: on a ~1,000-token cold prompt, llama-server answered in 2.3 s (Qwen) / 6.7 s (Gemma) against rapid-mlx's 3.4 s / 9.3 s. Both cache prompt prefixes, so follow-up turns in a conversation are sub-second on either — the cold-start penalty is paid once per fresh context.

Concurrent streams

(Added 2026-08-12.) We also ran 4 and 8 simultaneous salted requests (~1K-token prompt, 256-token budget each, medians of 3 batches; llama-server --parallel N -c N×4096, restart between lanes). Aggregate decode across all streams, 4-way → 8-way:

Model rapid-mlx (MLX) llama-server
Qwen3.6-35B-A3B 75.7 → 82.9 53.2 → 48.8
gpt-oss-20b 61.4 → 62.9 60.4 → 63.6
Gemma4-12B-it 24.4 → 22.6 28.3 → 30.9

Same shape as everything else in this post, sharpened: on the sparse MoE 35B, MLX serving is the only stack that speeds up with more streams and ends 1.7× ahead; on gpt-oss it's a dead heat; on the dense 12B llama.cpp's batching wins outright. llama.cpp's prefill edge also means it wins whole-batch wall-clock on gpt-oss (43.2 vs 34.7 t/s incl. prefilling all eight prompts) while MLX wins it on the MoE (41.8 vs 36.2). Full per-batch numbers are in the raw JSON.

So which one should you run?

It depends on the shape of your workload, and now you can look it up:

TTFT dominate; you prefill once and then generate for minutes. MLX wins this shape, and wins it big if your model is MoE (which the current best-on-Mac models — Qwen3.6-35B-A3B, gpt-oss — are).

RAG over fresh chunks) — prefill dominates. llama.cpp wins this shape by roughly 2×.

in our runs); close enough to choose on ecosystem instead.

One more Apple-specific note: MLX is Apple's own framework, built around unified memory, and MoE-heavy model design is where open-weights is heading — the two models with the widest MLX margins here are the two most recent. llama.cpp's portability (Linux, Windows, CUDA, Vulkan) remains its unanswerable advantage; on a Mac, that advantage is moot.

Caveats, so you don't have to find them yourself

absolute numbers won't. We'd genuinely like to see M4 replications.

identical at "4-bit," in GGUF's favor, and this is a speed test only.

0.31.2 here; rapid-mlx server numbers re-measured on 0.12.11). Numbers have a shelf life; the protocol doesn't.

unified checkpoint yet) — noted in the JSON.

Frequently asked questions

Is MLX faster than llama.cpp on Apple Silicon?

At generation, mostly — in our measurements MLX ranged from a near-tie (llama.cpp edged gpt-oss 52.0 vs 49.5 t/s at the raw kernel level, though the served comparison flipped back to MLX) up to 1.58× on a mixture-of-experts 35B. At prompt processing, no: llama.cpp prefilled 1.7–2.2× faster on every model we tested. Which framework is "faster" for you depends on whether your workload is generation-heavy (chat, agents) or ingestion-heavy (long-document one-shots).

Why is MLX so much faster on MoE models?

Sparse mixture-of-experts models like Qwen3.6-35B-A3B store 35B parameters but activate ~3B per token, so decode is dominated by memory-bandwidth- efficient gather-and-compute over a changing subset of weights — a pattern MLX's unified-memory design handles very well. The gap we measured (61.7 vs 39.1 t/s raw, 58.5 vs 38.3 t/s served) reproduced across both raw and server paths.

Does llama.cpp's prefill advantage matter in practice?

For the first long prompt of a session, yes — a ~1,000-token cold prompt reached first token about 1.4× sooner via llama-server in our runs. For everything after that, both stacks cache the prompt prefix and follow-up turns start in well under a second, so conversations feel similar; batch document processing feels different.

Can I use both?

Easily — they read different weight formats (GGUF vs MLX/safetensors), so they coexist on one machine. A practical split: an MLX server like rapid-mlx for your daily chat and coding agents, llama.cpp for bulk document ingestion jobs.


Run this yourself. rapid-mlx is an open-source, OpenAI-compatible inference server for Apple Silicon. One command installs it, then rapid-mlx serve <alias> serves any model on localhost:8000/v1.
curl -fsSL https://rapidmlx.com/install.sh | bash

New models and speedups, in your inbox

A short note whenever rapid-mlx gets faster or adds models worth running on your Mac. No spam — unsubscribe anytime.