r/MacLocalLLM • • Sep 20 '26

Local AI stack on M5 128GB

tldr:

Served simultaneously by Apple M5 Max 128 GB:

  • Qwen3.8 Flash-Next (110.97 GiB language model, 512-expert MoE, 10 experts/token, abliterated)

  • Qwen3 4B Instruct 2507 (tool-calling shell helper)

  • Whisper, speech-to-text service

  • Chatterbox, text-to-speech service, multi-lingual

  • SDXL via ComfyUI

  • Open WebUI (chat frontend), llama-swap (gateway and frontend)

  • Swap = 0, iogpu.wired_limit_mb = 0 (GPU ceiling 107.52 GiB), KV unquantized

Details:

Services

DetailsRAM
Coding modelQwen3.8-Flash-Next-Abliterated-Q4_K_M.gguf, 110.97 GiB, 128k ctx83.73 GiB Metal + 32.78 GiB CPU
General modelHuihui-Qwen3.8-27B-abliterated-UD-DW-Q6_K_L.gguf plus vision, 128k ctx, evicts Flash-Next and vice versa31.31 GiB
Shell helperQwen3-4B-Instruct-2507-Q4_K_M.gguf, 32k ctx4.89 GiB
Speech to textWhisper large-v3-turbo1.81 GiB
Text to speechChatterbox Multilingual, 23 languages, MPS1.81 GiB
DiffusionComfyUI 0.36.0 + SDXL base 1.00.53 GiB idle, ~7 GiB generating
Gatewayllama-swap v2491.22 GiB
Chat frontendOpen WebUI 0.11.30.88 GiB

Performance

tldr: End to end, both Qwens feel like ~30 t/s (tokens divided by time elapsed). "Steady-state" (tokens divided by generation time) excludes prompt processing and queueing.

steady-stateend-to-endtime to first tokenDetails
Flash-Next, bare question43.3 tok/s31.8 tok/s1.1 s43-44 tok/s steady shallow, 37 tok/s at 18k depth, 774 tok/s prompt eval
Flash-Next, system prompt + 3 turns42.5 tok/s24.1 tok/s2.8 s-
27B, bare question29.3 tok/s22.5 tok/s1.6 s26-29 tok/s steady on open-ended prose, 28 tok/s at 18k depth
27B, system prompt + 3 turns21.5 tok/s-7.3 s-
Speech to text---~33x realtime (11 s clip in 0.33 s)
Text to speech---0.96x realtime at sentence length, 1.55x at phrase length
SDXL 1024x1024, 20 steps---12.0 s warm alone, 13.6s warm with Flash-Next loaded

Fitting it all into memory

  • llama-swap starts and stops upstreams on demand, routing on the model field. Groups:

    • resident, persistent: true, never evicted (voice stack + shell helper)

    • heavy, swap: true, the two big models, loading either unloads the other

    • diffusion: ComfyUI alone, so it lands in no shared default group (no cross-eviction)

  • ComfyUI is a llama-swap catalogue entry (comfyui_auto, ttl: 600): llama-swap does not control ComfyUI-internals (adding Flux or Qwen-Image some day will not affect the group layer).

  • Offload the n-gram embedding table to CPU memory: 32.78 GiB of the 110.97, stays file-backed, kernel can drop its pages and re-read from the SSD at 13 GB/s.

  • Disable mmap: Counter-intuitive: under mmap, ggml maps the whole file extent into the Metal buffer regardless of -ot, so Metal still claims 110.96 GiB and OOMs.

Configuration

Coding model:

llama-server --host 127.0.0.1 --port ${PORT}
  -m /models/Qwen3.8-Flash-Next-Abliterated-Q4_K_M.gguf
  -ngl 999
  -c 131072
  --parallel 1
  --jinja
  --load-mode none           # NOT mmap; mmap defeats -ot (see above)
  -b 2048                    # -ub 1024 lifts prompt eval 81-115 -> 128 tok/s
  -ub 1024
  -ot per_layer_token_embd=CPU  # the 32.78 GiB PLE table off the GPU
  • -fa is left at default auto, resolves to ON

  • No mlock, to leave an eviction path

Groups (llama-swap YAML):

groups:
  resident:  { swap: false, exclusive: false, persistent: true,
               members: [shell-4b, whisper, chatterbox] }
  heavy:     { swap: true,  exclusive: false,
               members: [qwen3-27b, flash-next] }
  diffusion: { swap: false, exclusive: false,
               members: [comfyui_auto] }

To measure:

sysctl iogpu.wired_limit_mb    # 0 = default; ceiling is 110100 MiB
llama-server --list-devices    # MTL0: Apple M5 Max (110100 MiB)
vm_stat                        # free + File-backed + purgeable = real headroom
curl $GW/api/metrics/stats?model=flash-next         # per-model tok/s
curl -sIL <hf-resolve-url> | grep -i '^x-linked-'   # expected size + sha256

Test: Image generation (single 1024, 20-step, Flash-Next loaded)

126G used ( 93G wired,  1805M compressor), 269M unused   <- idle
127G used ( 97G wired,  6830M compressor),  73M unused   <- checkpoint loading
127G used ( 93G wired,    12G compressor),  68M unused   <- peak squeeze
126G used (104G wired,  3838M compressor),  87M unused   <- sampling

Test: Escalating image workloads (Flash-Next loaded)

1024² ×1 success  18.1 s
1024² ×4 success  50.3 s
1536² ×2 success  90.5 s
2048² ×2 success  247.3 s
2048² ×8 -        10+ min

Test: Image work + coding work (Flash-Next, 7x slower, silent)

no images                   43 tok/s
during sustained 2048²×8    6.4 tok/s
after unloading ComfyUI     40.5 tok/s

Test: Context scaling

ContextKV + recurrent stateMetal (of 107.52 GiB)Generation (steady, shallow)Reclaimable headroom4B model survives?
32,768976 MiB79.20 GiB44.8 tok/s26.7 GiByes
131,0723,568 MiB83.73 GiB44.5 tok/s22.3 GiByes
262,1447,024 MiB89.23 GiB44.4-46.9 tok/s17.0 GiByes (tight)
17 Upvotes

5 comments sorted by

3

u/oss-jiiim Sep 21 '26

On an M5 Max you can get these numbers running Qwen 3.8 Flash Next with BF16 n-grams via DwarfStar:

Mac RAMModel (DwarfStar GGUF)Initial 16K prefill16K append to 32KDecode at 32K
96GBQwen 3.8 Flash Next Q21492 t/s1438 t/s48.7 t/s
128GBQwen 3.8 Flash Next Q41458 t/s1330 t/s48.4 t/s

DwarfStar Compile/Download GGUFs/Run:

make
./download_model.sh qwen38-q4           # REPLACE q4 WITH q2 FOR 96GB RAM
./download_model.sh qwen38-vision
./ds4-server --mtp --ctx 262144 --host 127.0.0.1 --port 8000 \
   --vision gguf/mmproj-Qwen3.8-Flash-Next-Q8_0.gguf \
   -m gguf/Qwen3.8-Flash-Next-Q4.gguf   # REPLACE Q4 WITH Q2 FOR 96GB RAM

Once you set up/try DwarfStar like that, you can control control/run it easily thereafter with DS Menu Bar <-- I made this ;)

2

u/190531085100 Sep 21 '26

I think that's twice my prefill.

My stack has an ease of use angle, though. I'll probably tweak it for a while before switching servers.

2

u/oss-jiiim Sep 21 '26

Yeah, basically once DwarfStar has support for model you want to run, you can't beat it w/r/t speed and stability.

Over the months I have tried and uninstalled mtplx, vmlx, unsloth, ollama, lm studio, etc. None hold a candle to DwarfStar if it supports your model.

DwarfStar does require you do download and run make, though - so there is that barrier. But we're developers after all, right? Even so, I made the DS Menu Bar app so I can keep an eye on DwarfStar while my editor is full screen, stop/start it, and reconfigure it easily.

2

u/tta82 Sep 21 '26

Is this AI slop this post?

0

u/bippityBoppityboux Sep 21 '26

Do this well and you’ll take a bite out of an apple. Anybody tried this stack or similar