Field report: Qwen3.8-2.4T-A95B in TP4 on four M3 Ultras (early numbers) #4231
Replies: 4 comments
Update (same boot): 126k prefill, then 8-bit headsPrefill ladder finished after the first post. Unique prompts, 8 greedy tokens,
Gentle roll-off from ~209 tok/s at 34k. Fabric stayed quiet. 262k still unrun. Then we swapped 8-bit
The cheap head fix is not enough. Short greedy still works; sampled and long greedy still collapse to the same junk token ( |
Update: batch
First B=1 call paid a 330 s cold kernel. After that, aggregate decode scaled 7.9 → 14.0 → 24.3 tok/s at B=1/2/4 (per-stream ~7.9 / 7.0 / 6.1). Unique ~2k prefills at B=4 were 322 tok/s aggregate vs ~210 single-stream. Peak stayed 367 GB. Quality unchanged. The useful “batch work” shape is still a frozen prefix plus short greedy questions, not a bigger microbatch. |
Theory: why decode falls into
|
Update: hosted Qwen3.8-Max does not emit the junk stringSame three prompts that die on the MLX 4-bit pack, run against hosted
Zero occurrences of Max is not the open A GPU run of open FP8/BF16 on those same prompts would still split convert-vs-runtime. If anyone has that, we would like to know whether greedy |
Uh oh!
There was an error while loading. Please reload this page.
Qwen3.8-2.4T-A95B in TP4 on four M3 Ultras
Early field report, 13 August 2026. One certified JACCL boot. A 64k/128k
prefill ladder is still running as this is written. An 8-bit
lm_head+embed_tokenspatch is staged from official BF16 and not yet loaded. We willfollow up with those numbers rather than wait to post.
Stack: mlx 0.32.0, mlx-lm 0.31.3, macOS 26.6.1 (25G76), kernel 25.6.0,
4× Mac Studio M3 Ultra 512 GB unified memory, Thunderbolt 5 full mesh, JACCL/RDMA,
MLX_METAL_FAST_SYNCH=0.This is a field report, not a recommendation to do the same.
We wanted to know whether the open-weight Qwen3.8 2.4T model can run at all on
the largest consumer Apple silicon we can cable together. It can. What follows is
how we built the checkpoint, what we measured, and an honest reading of what that
is worth. We do not have perplexity, MMLU, or any other capability eval:
only stability, throughput, and a handful of behavioural probes.
The open checkpoint is not Qwen3.8-Max. Max is the hosted product (vision,
optional non-thinking, 1M context, official tools).
Qwen/Qwen3.8-2.4T-A95Bistext-only and thinking-only: the chat template errors if you pass
enable_thinking=false, and every generation prompt opens<think>.The lesson, first
Hosting a 2.4T model on four M3 Ultras is possible. Using it as Qwen3.8 is a
different claim, and we did not establish it.
On this hardware the body must be about 4-bit. That is arithmetic, not
taste:
If 4-bit wrecks the job this model exists for (long think, sampled decode),
then this class of weight does not have a useful home on four Studios. That is
the consumer-hardware ceiling, not a convert bug we can wish away. An 8-bit
body does not fit. An 8-bit head on a 4-bit body does; that swap is
staged and not yet measured.
We still think there may be a narrow air-gapped use (below). We do not think
this is how anyone who can use a data centre or Qwen Cloud should run 2.4T.
How we built it
Stock
mlx_lm.convertcannot ingest this checkpoint on a 512 GB node:Model()allocates the dense tree, and Hugging Face
quant_method=fp8(block-128) is nothandled. We streamed official
Qwen/Qwen3.8-2.4T-A95B-FP8:mx.from_fp8×weight_scale_inv(128×128 blocks).[512, out, in].mtp.*.model_type=qwen3_5_moeso mlx-lm 0.31.3'sqwen3_5_moe.pyloads it(
MODEL_REMAPPINGhas noqwen3_5_moe_text).Convert wall time 803 s. Output: 277 shards, 1.20 TiB, 2.420×10¹²
parameters. Per-tensor 8-bit was applied only to MoE router gates
(
mlp.gate,shared_expert_gate), copyingqwen3_5.quant_predicate.lm_headandembed_tokensare 4-bit (vocab 248,320). mlx-lm's mixedquant recipes keep
lm_headat higher bits; this convert did not.mlx_lm.sharebroadcast the replica over JACCL in 273 s (~4.5 GB/s). RDMAprotection domains are not reclaimed on process exit; after a multi-hundred-GB
share we rebooted the fleet before TP4.
mlx.distributed_configis once perboot. We did not bump mlx / mlx-lm.
Architecture as loaded: 92 layers, hybrid 23 full attention / 69 Gated
DeltaNet, GQA 64 Q / 4 KV (
head_dim256), 512 experts top-10 + 1 shared,hidden 8192. Native context 262,144 (1,010,000 is an extension, untested).
Not MLA. GDN
ArraysCacheis not trimmable (can_trim_prompt_cacheis false)but is extendable.
What we measured
One certified JACCL boot.
prefill_step_size=512(mlx-lm already chunks theprompt; the CLI on 0.31.3 does not expose the flag: pass it in Python).
Official card sampler is
temperature=1.0, top_p=0.95, top_k=20.reasoning_effort=lowunless noted. All quality probes below are greedyexcept the sampler sweep.
Load and fabric
all_sumof onesFour full loads and many generates on the same boot; no JACCL
tbt_post_recvSEGV, noLOC_PROT_ERRmid-run, no Metal 60 s watchdog, nomulti-chunk prefill deadlock. That last item is the contrast with Kimi-K3 on
the same fabric.
Prefill (unique prompts, cache-busting)
Linear through ~34k. If it holds: ~21 min to fill the native 262k window,
once. A unique ~63k-word prompt is in flight as this is written.
After the 34k prefill, the next eight greedy tokens were still coherent
English. Long decode is where quality died, not long prefill.
Prefix cache (measured)
Frozen ~31.6k-token document (planted site record + unique filler). First
user turn cached; each question is a second user turn so the prefix is a
token-level prefix. GDN cannot trim, so each hit clones the snapshot
(
mx.arraycopy) and extends only the suffix.</think>, answerQ38-7741can_trim=FalseQ38-7741The air-gap shape is real on the speed side: one 150 s fill, then ~8 s
for a short greedy question instead of another 160 s. Cache is small
(0.89 GB at 32k; linear that is a few GB at 128k). Quality still dies as
soon as the think is longer than a peaked lookup.
Greedy short tasks (not evals)
</think>81 × 47, 256 max3807, 70 tok @ 8.0 tok/s384 + 7691153, then a repeating junk token to length1000 / 76, 70 tokQ38-7741, 57 tokNothen one junk token17 × 23on an earlier load was the same pattern: greedy low-effort391and a close, 65 tok @ 8.5 tok/s.
Official sampler (same trivial math)
temp=1.0, top_p=0.95, top_k=20temp=0.6/0.8temp=1.0,presence_penalty=0.5temp=1.0,presence_penalty=1.0<tool_call>9Needles in a ~32k haystack
Planted sentence
The secret token is WATTLE-5519.at start, middle, or endof unique filler. Greedy, 256 new tokens.
Prefill stayed 208–209 tok/s. The think named the right region (start /
between paragraphs / near the end) on all three. None closed, none emitted
the token: the same junk token took over. Long prefill attends. Decode
still dies.
The repeating junk token on this pack is
资源篮. It shows up in sampleddecode, long greedy, and any think that runs past a few dozen tokens.
We have no perplexity, no held-out loss, no coding/agent bench. We know
this 4-bit pack can emit a correct short greedy answer when the mode is
peaked, and that every sampled setting we tried, plus every longer think,
destroyed the tail. That is not a capability profile.
Reading the quality hole
The MoE body at 4-bit is why the model fits and why TP4 prefills at ~210 tok/s.
The lm_head is a 248k-way classifier quantized the same way. Argmax only
needs the mode; short arithmetic is peaked, so greedy can still pick
391or3807. Sampling (top_k=20,top_p=0.95,temp≥0.6) needs a calibratedneighbourhood. We believe 4-bit group-64 noise is enough to mix junk tokens
into that neighbourhood; one bad sample then locks the decode. Long greedy is
the same story with more near-ties.
mlx-lm mixed recipes already special-case
lm_headto higher bits. A cheapfollow-up is in progress: 8-bit
lm_headandembed_tokens, body still4-bit. Official FP8 stores those two tensors as BF16 already
(
248320 × 8192). Staging them to affine 8-bit group-64 took 2.0 s and4.3 GB. They have not been swapped into the live replica yet (the current
load still holds the 4-bit shard). Until that run exists, “4-bit Qwen3.8 on
Apple” should not be described as the model.
What this can achieve, honestly
Stable TP4 hosting of a 2.4T 4-bit replica on four M3 Ultras, ~8–10 tok/s
decode, ~210 tok/s chunked prefill through 34k, linear so far, and a working
prefix-cache extend (894 MB at 32k, miss 161 s → hit 8.4 s). That is a
real MLX/JACCL result. Kimi-K3 on the same mesh was not a usable runtime.
It is not a chat model, not Max, and not a capability claim. 8 tok/s plus
forced think is overnight / queue latency. Independent jobs each pay full
prefill unless the prefix is frozen. Concurrent multi-stream is untested.
The remaining use case we take seriously is air-gapped text that must not
leave the building, with a large frozen prefix (corpus, case file, tree)
and short greedy questions, decode capped. Cost-per-token versus an API
does not apply if there is no API you are allowed to use. The speed half of
that story is now measured. The quality half is only peaked lookups
(
Q38-7741,3807,391). Harder questions against the same prefix start areal think and then loop.
If the data can go to Qwen Cloud or a GPU cluster, it should. FP8/BF16, a
full-precision head, MTP, and Max’s product features live there. Four Mac
Studios are a serious amount of money to generate eight tokens a second of
unevaluated 4-bit output.
We would not retire 512 GB Studios. They are excellent at 30B–300B, one node,
current mlx-lm / omlx. We would treat trillion-class serving on four
consumer machines as a closed experiment unless the 8-bit head run changes
the quality picture.
That is the experiment. A year ago a 1T model was frontier. Capability has
moved: for most work a much smaller model in a competent harness is as good or
better, and it is the smart default. The largest weight is no longer necessary
for the majority of tasks. We already measured a version of this on the same
four machines: one Studio on a ~300B-class model with a fast local runtime
beat four Studios on a 1.6T checkpoint in a reference graph, on both speed and
the jobs we cared about. Topology helps a dispatch-bound runtime; kernels and
the right-sized model help more.
The question that remains is when a multi-trillion model is necessary
enough that waiting minutes per answer is warranted. That buyer is not
shopping for Thunderbolt cables. They are paying for data centre equipment
worth millions (or renting it) because they need FP8/BF16, a full-precision
head, MTP, and a harness that matches the card. The fact that four second-hand
M3 Ultras can host 2.4T at reading speed is remarkable. It is also the
limit of what is viable on consumer metal. Past that limit, the work and the
capital belong in a hall with real interconnect, not a desk with TB5.
Still running, and what we will post next
On this boot, next:
17 × 23, one long greedy, oneprefix-cache hit
We are not claiming those results in this post. Happy to take questions in
the meantime: convert layout, GDN cache,
prefill_step_size, or the qualityhole.
Reproduction sketch
generate/stream_generate(..., prefill_step_size=512). Chat template:reasoning_effortin{xhigh, medium, low}(defaultxhigh); do not passenable_thinking=false. Wired limit =max_recommended_working_set_size.Prefix reuse: prefill the common turn into
make_prompt_cache, clone, passonly the suffix as
promptwithprompt_cache=clone. Do not replay thefull prompt into the same cache (generate_step does not skip a cached prefix).
Happy to share the streaming converter and the one-load bench harness if useful.
What we are not claiming
have finished; 63k is in flight).
fits.
Possible, on this metal, is not the same as worth it.
All reactions