Speed, memory, and fidelity for LLM serving on unified-memory desktops. All requests hit an OpenAI-compatible endpoint at concurrency 1 / 8 / 16, BF16 weights, n=100 per level.
benchmark repo weekly journals paper (soon)
SiliconBench audits the LLM serving engines that run on unified-memory desktop hardware. Nine stacks are benchmarked on Apple Silicon against the same weights and prompts, with a CUDA-native reference track on an NVIDIA DGX Spark for the three engines the two ecosystems share. A maintainer agent re-runs the benchmark, commits the raw results, and this page rebuilds from them automatically.
Speed alone is a misleading ranking on shared machines: the engine pool is the same memory your browser and IDE use, and a stack can be fastest while claiming most of it or while returning wrong output. The tables therefore keep three lenses side by side. Speed is measured on two workloads (a short-prompt chat split and an agent split whose multi-turn prompts reach several thousand input tokens), memory as the peak footprint during serving, and fidelity as weighted F1 on a classification task against an NVIDIA reference on identical weights. Try sorting by tok/s at c=1 and then at c=16: the point of the concurrency sweep is that single-stream rankings do not survive load.
Single-node serving only. Stacks are listed alphabetically by default; click a column header to sort (failed runs always sink to the bottom); no default ranking is implied; the paper's central finding is that speed-only orderings mislead. ✕ = crashed (<5/100 requests), n/100 = partial run, – = not measured. Trend sparklines show each stack's own shape across c=1/8/16 (per-row normalized; TTFT on a log scale); magnitudes are in the numbers. Hover a stack name for its run provenance; the full record is under per-framework provenance at the bottom.
Runs 2026-07-05 to 2026-07-08 (splits from different runs): 6 of 9 stacks complete every request at every level; 3 crash at least once.
| Stack | tok/s c=1 | tok/s c=8 | tok/s c=16 | trend | TTFT p50 c=16 | TTFT trend | peak mem GB |
|---|---|---|---|---|---|---|---|
| hf_transformers | 26.4 | 43.8 | 33.7 | 1.09 s | 57.3 | ||
| llama.cpp | 113.5 | 236.3 | 252.0 | 2.22 s | 22.3 | ||
| mistral.rs | 84.0 | 3.866/100 | ✕ | – | 61.4 | ||
| mlx_lm | 76.1 | 70.1 | 62.7 | 2.28 s | 58.7 | ||
| ollama | 130.7 | 226.0 | 448.8 | 385 ms | 47.3 | ||
| omlx | 95.2 | 135.1 | 141.3 | 2.67 s | 21.3 | ||
| sglang | 100.1 | 80.0 | 81.0 | 607 ms | 58.3 | ||
| vllm-metal | 57.8 | 184.1 | 193.8 | 254 ms | 33.8 | ||
| vllm-mlx | 110.9 | 75.223/100 | ✕ | – | 20.2 |
| Stack | tok/s c=1 | tok/s c=8 | tok/s c=16 | trend | TTFT p50 c=16 | TTFT trend | peak mem GB |
|---|---|---|---|---|---|---|---|
| hf_transformers | ✕ | ✕ | ✕ | – | – | – | – |
| llama.cpp | 41.6 | 52.9 | 51.5 | 15.42 s | 34.5 | ||
| mistral.rs | ✕ | ✕ | ✕ | – | – | – | – |
| mlx_lm | 30.270/100 | 15.817/100 | ✕ | – | 58.5 | ||
| ollama | 96.7 | 126.3 | 184.3 | 2.43 s | 47.8 | ||
| omlx | 54.3 | 91.5 | 95.1 | 5.55 s | 45.4 | ||
| sglang | 38.7 | 30.4 | 30.1 | 2.02 s | 58.2 | ||
| vllm-metal | 32.1 | 106.8 | 101.7 | 646 ms | 34.1 | ||
| vllm-mlx | 40.9 | 22.89/100 | ✕ | – | 34.5 |
| Stack | 0-shot F1 | 5-shot F1 |
|---|---|---|
| vllm-nvidia (ref) | 0.4094 | 0.7364 |
| hf_transformers | 0.3996 | 0.7325 |
| llama.cpp | 0.4031 | 0.7366 |
| mistral.rs | – | – |
| mlx_lm | 0.3953 | 0.7315 |
| ollama | 0.4088 | 0.4463 |
| omlx | 0.3939 | 0.7341 |
| sglang | 0.3955 | 0.7337 |
| vllm-metal | 0.4021 | 0.7343 |
| vllm-mlx | 0.1652 | 0.4805 |
Runs 2026-07-05 to 2026-07-09 (splits from different runs): 5 of 9 stacks complete every request at every level; 1 degrade to partial or skip; 1 crash at least once; 2 never start.
| Stack | tok/s c=1 | tok/s c=8 | tok/s c=16 | trend | TTFT p50 c=16 | TTFT trend | peak mem GB |
|---|---|---|---|---|---|---|---|
| hf_transformers | 28.9 | 29.3 | 29.076/100 | 39.73 s | 12.7 | ||
| llama.cpp | 99.0 | 235.4 | 234.2 | 4.05 s | 20.7 | ||
| mistral.rs | ✕ | ✕ | ✕ | – | – | – | – |
| mlx_lm | 68.6 | 68.5 | 68.6 | 11.91 s | 17.5 | ||
| ollama | ✕ | ✕ | ✕ | – | – | – | – |
| omlx | 108.6 | 150.7 | 153.8 | 4.51 s | 19.8 | ||
| sglang | ✕ | ✕ | ✕ | – | – | – | – |
| vllm-metal | 67.5 | 131.7 | 159.9 | 701 ms | 44.4 | ||
| vllm-mlx | 82.6 | 203.6 | 272.3 | 320 ms | 20.5 |
| Stack | tok/s c=1 | tok/s c=8 | tok/s c=16 | trend | TTFT p50 c=16 | TTFT trend | peak mem GB |
|---|---|---|---|---|---|---|---|
| hf_transformers | 24.4 | 24.5 | 24.5 | 134.25 s | 12.6 | ||
| llama.cpp | 50.8 | 85.5 | 92.2 | 9.55 s | 18.0 | ||
| mistral.rs | ✕ | ✕ | ✕ | – | – | – | – |
| mlx_lm | 38.1 | 38.0 | 38.0 | 25.76 s | 14.4 | ||
| ollama | ✕ | ✕ | ✕ | – | – | – | – |
| omlx | 50.7 | 102.6 | 103.5 | 5.19 s | 21.7 | ||
| sglang | ✕ | ✕ | ✕ | – | – | – | – |
| vllm-metal | 24.9 | 32.3 | 31.4 | 4.26 s | 42.0 | ||
| vllm-mlx | 66.2 | 159.7 | 164.2 | 578 ms | 37.4 |
| Stack | 0-shot F1 | 5-shot F1 |
|---|---|---|
| vllm-nvidia (ref) | 0.6953 | 0.5949 |
| llama.cpp | 0.7016 | 0.5965 |
| mlx_lm | 0.6940 | 0.5907 |
| omlx | 0.6959 | 0.5894 |
| vllm-metal | 0.6865 | 0.6054 |
Runs 2026-07-05 to 2026-07-08 (splits from different runs): 5 of 9 stacks complete every request at every level; 1 crash at least once; 3 never start.
| Stack | tok/s c=1 | tok/s c=8 | tok/s c=16 | trend | TTFT p50 c=16 | TTFT trend | peak mem GB |
|---|---|---|---|---|---|---|---|
| hf_transformers | 10.5 | 10.7 | 10.6 | 105.16 s | 32.5 | ||
| llama.cpp | 20.8 | 35.0 | 35.2 | 26.53 s | 31.2 | ||
| mistral.rs | ✕ | ✕ | ✕ | – | – | – | – |
| mlx_lm | 14.3 | 14.3 | 14.3 | 50.52 s | 33.8 | ||
| ollama | ✕ | ✕ | ✕ | – | – | – | – |
| omlx | 27.8 | 56.8 | 57.6 | 9.75 s | 31.2 | ||
| sglang | ✕ | ✕ | ✕ | – | – | – | – |
| vllm-metal | 18.4 | 59.2 | 87.8 | 568 ms | 42.1 | ||
| vllm-mlx | ✕ | ✕ | ✕ | – | – | – | – |
| Stack | tok/s c=1 | tok/s c=8 | tok/s c=16 | trend | TTFT p50 c=16 | TTFT trend | peak mem GB |
|---|---|---|---|---|---|---|---|
| hf_transformers | ✕ | ✕ | ✕ | – | – | – | – |
| llama.cpp | 13.0 | 21.2 | 21.0 | 56.63 s | 36.6 | ||
| mistral.rs | ✕ | ✕ | ✕ | – | – | – | – |
| mlx_lm | 5.3 | 5.4 | 5.3 | 114.44 s | 32.2 | ||
| ollama | ✕ | ✕ | ✕ | – | – | – | – |
| omlx | 14.7 | 48.7 | 50.3 | 13.01 s | 38.9 | ||
| sglang | ✕ | ✕ | ✕ | – | – | – | – |
| vllm-metal | 8.4 | 11.8 | 10.9 | 9.77 s | 40.7 | ||
| vllm-mlx | ✕ | ✕ | ✕ | – | – | – | – |
| Stack | 0-shot F1 | 5-shot F1 |
|---|---|---|
| vllm-nvidia (ref) | 0.8511 | 0.9128 |
| llama.cpp | 0.8497 | 0.9107 |
| mlx_lm | 0.7497 | 0.8856 |
| omlx | 0.5524 | 0.0453 |
| vllm-metal | 0.8201 | 0.8889 |
Run 2026-07-05: 3 of 3 stacks complete every request at every level.
| Stack | tok/s c=1 | tok/s c=8 | tok/s c=16 | trend | TTFT p50 c=16 | TTFT trend | peak mem GB |
|---|---|---|---|---|---|---|---|
| llama.cpp | 119.4 | 157.7 | 235.3 | 178 ms | – | ||
| sglang | 106.8 | 592.3 | 734.1 | 42 ms | – | ||
| vllm | 103.3 | 633.3 | 796.0 | 52 ms | – |
| Stack | tok/s c=1 | tok/s c=8 | tok/s c=16 | trend | TTFT p50 c=16 | TTFT trend | peak mem GB |
|---|---|---|---|---|---|---|---|
| llama.cpp | 55.3 | 70.2 | 79.7 | 1.90 s | – | ||
| sglang | 82.6 | 323.5 | 384.1 | 84 ms | – | ||
| vllm | 80.2 | 294.1 | 342.7 | 134 ms | – |
Run 2026-07-05: 3 of 3 stacks complete every request at every level.
| Stack | tok/s c=1 | tok/s c=8 | tok/s c=16 | trend | TTFT p50 c=16 | TTFT trend | peak mem GB |
|---|---|---|---|---|---|---|---|
| llama.cpp | 106.5 | 162.1 | 376.1 | 169 ms | – | ||
| sglang | 95.0 | 580.2 | 935.3 | 50 ms | – | ||
| vllm | 94.2 | 529.8 | 680.6 | 113 ms | – |
| Stack | tok/s c=1 | tok/s c=8 | tok/s c=16 | trend | TTFT p50 c=16 | TTFT trend | peak mem GB |
|---|---|---|---|---|---|---|---|
| llama.cpp | 77.5 | 108.5 | 172.7 | 409 ms | – | ||
| sglang | 79.4 | 396.4 | 684.1 | 66 ms | – | ||
| vllm | 73.4 | 223.9 | 248.5 | 551 ms | – |
Run 2026-07-05: 3 of 3 stacks complete every request at every level.
| Stack | tok/s c=1 | tok/s c=8 | tok/s c=16 | trend | TTFT p50 c=16 | TTFT trend | peak mem GB |
|---|---|---|---|---|---|---|---|
| llama.cpp | 20.1 | 29.8 | 72.8 | 1.19 s | – | ||
| sglang | 17.2 | 129.4 | 211.7 | 167 ms | – | ||
| vllm | 17.2 | 136.6 | 238.5 | 161 ms | – |
| Stack | tok/s c=1 | tok/s c=8 | tok/s c=16 | trend | TTFT p50 c=16 | TTFT trend | peak mem GB |
|---|---|---|---|---|---|---|---|
| llama.cpp | 18.1 | 24.6 | 51.7 | 2.43 s | – | ||
| sglang | 16.0 | 125.8 | 201.9 | 189 ms | – | ||
| vllm | 16.1 | 131.9 | 226.4 | 211 ms | – |
TL;DR.
Stacks with enforced budgets trace flat paths: throughput grows while memory stays put (vllm-metal holds 33 to 39 GB while scaling 3.3x). The sharpest finding cuts against the audit table: mistral.rs and sglang declare explicit budgets, yet both dive to about 60 GB on a 64 GB machine and degrade to partial completion or crash on the agent split. A declared budget is a configuration knob; discipline has to be enforced end to end through the allocator. ollama sits at 98 percent of Metal's 48 GB advisory working-set hint from the first request, sized to the hint rather than to demand.
Upstream vllm on GB10 scales 7.7x on chat and 4.3x on agent with median TTFT of 52 and 134 ms at c=16; sglang behaves alike. The attribution is engine-local: sglang scales 6.9x on CUDA but declines below single-stream on its MLX backend, and llama.cpp plateaus on both platforms, so its ceiling is the engine design, not the hardware. The silicon itself is competitive: single-stream speed matches across platforms, and llama.cpp on the M-series finishes ahead of its own CUDA build at chat c=16 (252 vs 193 tok/s).
A maintainer agent re-runs the benchmark weekly: it pulls each engine from upstream, runs both splits, diagnoses failures, applies bounded fixes inside a write allowlist, and commits a structured journal. In one week a single MLX library bump broke three stacks through three distinct failure modes; two were repaired within the same cycle. The journals are the provenance record behind the live tables above.
Paper & code. The paper is under review; a preprint link will appear here. The harness, per-run results, and weekly journals are public in the benchmark repository.