# Benchmark 8 lokálních LLM serverů pro Qwen 3.5 na Mac: nečekané výsledky v roce 2026
Na MacBook Pro s M2 Max byl proveden podrobný benchmark osmi lokálních LLM serverů pro spuštění Qwen 3.5 35B MoE. Při práci s jedním uživatelem činil rozdíl v rychlosti pouze 2 %, ale při dvou paralelních požadavcích jeden framework ukázal 2,17násobné zrychlení, zatímco jiný degradaci na 0,85x. Analýza odhalila kritické nuance continuous batching, fantomové ukazatele rychlosti a problémy s řízením paměti.
Proč spouštět LLM lokálně: tři technické důvody
Pro developery a inženýry lokální nasazení modelů řeší tři klíčové úkoly:
- Soukromí dat. Zpracování smluv, zadání a klientské korespondence bez předávání do cloudových API. Lokální model anonymizuje osobní údaje (jména, bankovní údaje) v reálném čase.
- Autonomní coding agenti. Nástroje jako OpenCode a Aider vyžadují OpenAI-kompatibilní endpoint. Nastavení
base_url: http://mac.local:8000/v1umožňuje spouštět agenty bez telemetrie a rate-limitů, čímž se šetří na tokenech cloudových služeb. - Absence síťové latence. 35B model v 4bitové kvantizaci na M2 Max generuje odpovědi rychleji než cloudové API díky absenci round-trip time. Na 64GB unified memory zabírá model ~20GB, zanechává zdroje pro KV cache.
Tyto faktory činí lokální server na Mac životaschopnou alternativou pro profesionální použití, zejména při práci s citlivými daty.
Proč je MLX lepší než llama.cpp na Apple Silicon
Volba MLX místo llama.cpp vychází z architektonických specifik Apple Silicon:
- Unified memory. Na M-series CPU i GPU sdílejí společný pool paměti. Model 35B o velikosti 20GB je uložen na jednom místě bez kopírování mezi RAM a VRAM (na rozdíl od NVIDIA).
- Výkon. MLX je stabilně o 10–30 % rychlejší než llama.cpp díky nativní kompilaci do Metal. M2 Max zajišťuje ~400 GB/s memory bandwidth, což umožňuje generovat 50–80 tokenů/s v reálném čase.
- Nativní continuous batching. V ekosystému MLX je podpora paralelního zpracování požadavků implementována „out of the box“, zatímco v llama.cpp vyžaduje dodatečné nastavení.
Důležité: Metal nepodporuje sdílení GPU kontextu mezi procesy. Všechny testy proběhly striktně na jednom serveru, což vylučuje konflikty zdrojů.
Srovnávané frameworky: výběr a kritéria
Do benchmarku bylo zařazeno šest serverů z ekosystému MLX (dva byly vyřazeny kvůli nekompatibilitě s Qwen 3.5 35B MoE):
- mlx-openai-server (Python 3.11) — podpora continuous batching, multi-model přes YAML, structured output.
- mlx-omni-server (Python 3.11+) — dvojitý API (OpenAI + Anthropic), TTS/STT.
- Rapid-MLX (Python 3.10+) — jednoduché nasazení, 1900+ testů, integrace s Cursor/Aider.
- vllm-mlx (Python 3.10+) — paged KV cache, multimodálnost, ale s kritickým bugem v SSE parseru.
- omlx (Python) — tiered KV cache (RAM + SSD), web dashboard, ale s tvrdým limitem 32768 tokenů.
- mlx-vlm (Python 3.10+) — podpora 40+ architektur, ale s patologickým zpomalením na dlouhých promptech.
Vyřazeny:
- higgs (Rust) — nepodporuje
qwen3_5_moe. - mlx-serve (Zig) — vyžaduje macOS 15 (Sequoia).
Klíčovým kritériem výběru byla kompatibilita s Qwen 3.5 35B MoE v nativním formátu MLX bez konverze.
Metodika benchmarku: jak byl měřen výkon
Testování proběhlo na MacBook Pro 16" M2 Max (64GB unified memory) s modelem mlx-community/Qwen3.5-35B-A3B-4bit. Benchmark harness v Pythonu zahrnoval:
- Scénáře zátěže:
* run_single — sekvenční zpracování 8 promptů (od 100 do 53000 tokenů).
* run_double_batch — dva paralelní požadavky přes asyncio bariéru:
gate = asyncio.Event()
task_a = create_task(chat_stream(..., start_gate=gate))
task_b = create_task(chat_stream(..., start_gate=gate))
await asyncio.sleep(0.05)
gate.set()
res_a, res_b = await gather(task_a, task_b)
- Metriky:
* wall_tps_p50 — medián tokenů/s podle wall-clock (hlavní ukazatel).
* ttft_p50 — čas do prvního tokenu (TTFT).
* speedup — efektivita batchingu (poměr wall_tps při dvou požadavcích k jednomu).
Pro vyloučení anomálií byla použita mediána místo průměru kvůli extrémnímu rozptylu délek promptů. Každý test byl opakován 5krát.
Výsledky: single-user vs. paralelní zpracování
Při práci s jedním uživatelem všechny servery vykázaly srovnatelný výkon (rozdíl do 2 %). Kritické rozdíly se projevily při dvou paralelních požadavcích:
- mlx-openai-server dosáhl 2,17× speedup — jediný framework s efektivním continuous batching.
- vllm-mlx vykazoval fantomové 14000 tokens/sec kvůli chybě v SSE parseru, která zkresluje gen_tps.
- Rapid-MLX nepodporuje streamovou generaci, proto TTFT = celkový čas odpovědi.
- mlx-omni-server vykázal degradaci na 0,85× speedup bez parametru
--workers 2. - omlx vracel HTTP 400 na promptech >32768 tokenů.
- mlx-vlm měl cyklické výkyvy rychlosti (790 → 3,5 tok/s) kvůli problémům s GC.
Zvláštní pozornost si zaslouží kvadratická složitost attention v některých implementacích — na promptech 52k tokenů prefill trval 31 minut místo očekávaných 2–3 minut.
Co je důležité: klíčové závěry pro developery
- Continuous batching je klíčové pro multiuživatelské scénáře. Pouze mlx-openai-server zajistil skutečné zrychlení při paralelních požadavcích. Ostatní je buď paralelně nezpracovávaly, nebo vyžadovaly manuální nastavení workerů.
- Ověřujte metriky na reálných datech. Fantomové ukazatele v vllm-mlx (14000 tok/s) vznikly chybou v zpracování SSE streamu, což činí gen_tps nespolehlivým.
- Unified memory je výhoda, ale ne lék na vše. Navzdory společnému poolu paměti zůstává tiered KV cache (jako v omlx) relevantní pro velmi dlouhé kontexty.
- Testujte na dlouhých promptech. Patologické zpomalení v mlx-vlm na promptech >30k tokenů může server diskvalifikovat pro úkoly s velkým kontextem.
- Verze Pythonu ovlivňuje stabilitu. Rapid-MLX s 1900+ testy vykázal nejlepší odolnost, ale prohrál v rychlosti batchingu.
— Editorial Team
Zatím žádné komentáře.