Reliable GLM-5.2-oQ4e-mtp on one M3 Ultra 512GB (v0.5.0): config recipe, SSD prefix cache 40×, MTP is a win, and the real long-context ceiling #2190
Replies: 3 comments
-
|
Great writeup, thanks. A few updates: the FAST_SYNCH=1 hang was an omlx bug, fixed in 69ea88d (#2183), so it no longer needs to be off with that commit. d5fcb22 is already on main. The other findings here also landed: port binds before pinned preload with /health answering 503 while loading, and the wired-limit recommendation is clamped below physical RAM. One extra data point for the recipe: stranded wired memory from a hard kill also poisons Metal sync machine-wide until reboot. |
Beta Was this translation helpful? Give feedback.
-
|
Verified on our 5× M3 Ultra 512GB fleet — thanks for the fast turnaround. Cherry-picked We'd also been carrying a local workaround that forced a non-thread-local MLX stream ( Confirmed with
Also re-confirmed the #2184 behaviour: graceful |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.

Uh oh!
There was an error while loading. Please reload this page.
-
Working recipe + findings: reliable GLM-5.2-oQ4e-mtp on a single M3 Ultra 512GB (v0.5.0)
Sharing a config recipe and a few measurements from getting
Jundot/GLM-5.2-oQ4e-mtp(423.8 GiB)running reliably on one Mac Studio M3 Ultra 512GB, macOS 26.5.2, omlx v0.5.0 (source build,
custom kernels, mlx 0.32.0). Three things had to be right before it was stable, then the SSD prefix
cache and MTP both paid off well. Posting in case it saves someone the two days it cost us.
The config that made it reliable
MLX_METAL_FAST_SYNCH=0. With it set to1, every GLM generation hung forever on the firstforward pass — GPU pinned at 100% residency but only ~4W (a spin-wait, not compute), no token, no
error, and the executor thread wedged so all later requests queued forever.
=0fixed itcompletely with no measurable perf cost at our scale. (Filed as GLM-5.2 generation hangs forever (silent GPU spin-wait) with MLX_METAL_FAST_SYNCH=1 on M3 Ultra — FAST_SYNCH=0 fixes #2183 — bare
mlx_lm.generatewith the same model/kernels never hung; the trigger needs the server's stream usage + FAST_SYNCH.)
iogpu.wired_limit_mb=499712(488 GiB), not 510 GiB. The enforcer log suggests raising toward522240; on a 512 GiB box with this 424 GiB model that pushed the OS into jetsam, which killed theserver mid-load, which (with launchd KeepAlive) became a compounding crash-loop. 488 GiB leaves the
424 GiB model + ~60 GiB KV with the OS safe. (Filed as Hard-killed process holding a large wired model leaks the wired memory at kernel level (reboot-only recovery); KeepAlive turns it into a jetsam crash-loop #2184.)
Graceful stops only + generous
ExitTimeOut. A hard-killed process (SIGKILL/jetsam) holding~424 GiB wired leaks the whole allocation at kernel level — it survives process death and only a
reboot reclaims it. Graceful
bootout(SIGTERM) released everything (wired 424 GiB → 5 GiB inseconds). So never
kickstart -kwith a big model loaded. (Also Hard-killed process holding a large wired model leaks the wired memory at kernel level (reboot-only recovery); KeepAlive turns it into a jetsam crash-loop #2184.)Plus the expected: source-built kernels (
OMLX_WITH_CUSTOM_KERNEL=1, mlx 0.32.0 + nanobind 2.13.0),--memory-guard-gb 488, small--hot-cache-max-sizefor a single-pinned-model node.SSD prefix cache — the payoff (this is the recipe worth copying)
--paged-ssd-cache-dir <dir> --paged-ssd-cache-max-size 600GB, plus cherry-pickingd5fcb22a(#2180) onto the v0.5.0 tag. Needle test at 131k tokens:
~40× faster on any resumed/iterated long-context session. The #2180 cherry-pick is load-bearing:
without it, an adaptive-prefill/memory-guard event discards the reconstructed SSD prefix and cold-
prefills the whole prompt — exactly the case that fires on a node sitting near the memory ceiling.
Would suggest it belongs in the next tag.
MTP is a net win on GLM-5.2 post-#2160 (counter to the fine-grained-MoE caution)
The common guidance is that MTP is a net loss on fine-grained (8/256) MoE because verify batches load
near-disjoint expert unions (#2077 etc.). With Lightning MTP (#2113) + the #2160 verify-routing fix
on the oQ4e quant, we measured the opposite — MTP on vs off (llama-benchy, greedy):
So on this model+build, keeping MTP on is correct at every context length we tested. Worth noting for
anyone who disabled it based on the general MoE caution.
Retrieval quality + the real long-context ceiling
Needle-in-haystack at 131k: 5/5 needles at 10/30/50/70/90% depth, plus a full-recall sweep, all
correct, and every re-query hit the SSD prefix cache (~20–40 s vs the 23 min cold). No sign of the
Pushing higher, the binding limit turned out to be prefill peak memory, not retrieval quality and
not steady-state KV. At 196k the one query that ran returned the needle correctly (quality holds),
but subsequent queries were rejected by the prefill memory guard:
prefill would require ~494.81 GB peak. 246k was rejected outright (~511.54 GB peak). So on a single 512GB node with this 424 GBmodel, the reliable context ceiling is ~131k — prefill transient memory (~495 GB at 196k) hits the
wall long before the steady-state KV would (only ~20 GiB at 196k). Worth flagging because the
max_context_windowyou can set (we tried 262144) is well above what the node can actually prefill— it fails safe with a 400, but the practical ceiling is a good deal lower than the configurable one.
(Likely tunable somewhat via smaller prefill chunk size / guard settings — not yet explored.)
Benchmarks vs the v0.5.0 release claims (M3 Ultra, this quant)
Happy to share full llama-benchy JSON / py-spy stacks / logs if useful for any of the linked issues.
Beta Was this translation helpful? Give feedback.
All reactions