Skip to content

Qwen3.8-27B (dense 27.8B hybrid Gated-DeltaNet VLM)

Qwen3.8-27B reports model_type: qwen3_5 despite the name — a dense 27.8B multimodal model whose 64 decoder layers are 48 Gated-DeltaNet (linear attention) + 16 full attention, one full layer in every four. Hidden 5120, intermediate 17408, vocabulary 248,320, plus a 27-block vision tower. mlx-lm 0.31.3 and mlx-vlm 0.6.3 already carry the architecture, so nothing needed porting; conversion takes about 18 seconds.

Two builds are published, and they target different machines.

build size peak (1.3K prompt) decode Opencode agentic
tq4-g64 15.15 GiB 22.85 GB 11.4 tok/s pass (6m12s)
tq3-mini-g64 11.55 GiB 13.30 GiB 12.2 tok/s pass (6m32s)¹
tq3-g64, 8-bit extras (unpublished) 12.88 GiB 20.60 GB 9.2 tok/s pass (6m32s)

¹ the agentic run was done on the 8-bit-extras tq3; the published tq3-mini differs only in the extras tier, at an identical 8.0052-vs-8.0551 perplexity.

Both fix a planted off-by-one end to end: given the task's pytest command, each build runs it, reads the file, makes the minimal edit, re-runs to green and explains with file:line. Vision works on both — shapes, colours, positions and grounded bounding boxes.

Take tq4 if it fits; take tq3-mini if it does not. At 4-bit, tq4 is faster as well as better than the 8-bit-extras tq3 for 2.3 GiB more — the same result the speed flip predicts, since the codebook path only earns its keep at 2-bit and 3-bit's 10-indices-per-uint32 packing costs more to unpack than 4-bit's clean 8.

The exception is the 16 GB tier, where nothing else reaches. tq3-mini-g64 is verified on a real 16 GB M4 Mac mini — not projected down from a bigger machine — at iogpu.wired_limit_mb=14336 and --prefill-step-size 256:

prompt peak, 16 GB mini peak, 64 GB M4 Max* prefill decode
220 tok 12.95 GiB 13.22 GiB 20.7 tok/s 3.9 tok/s
818 tok 12.96 GiB 13.05 GiB 19.0 tok/s 3.9 tok/s
2,014 tok 13.14 GiB 13.30 GiB 21.0 tok/s 3.6 tok/s
5,017 tok 13.61 GiB 13.81 GiB 20.6 tok/s 3.7 tok/s

* The M4 Max sweep realized 246 / 844 / 2,040 / 5,043 prompt tokens — a constant 26 more than the mini's, worth 1.62 MiB of KV at 64 KiB/token. Read the comparison as close, not as an exact A/B.

Vision passes there too — OCR correct at 350×100 (12.44 GiB) and 700×200 (12.68 GiB). Peaks reproduced identically across two independent sweeps, and they run 0.09–0.27 GiB below the comparable prompts on the M4 Max — 55–170× more than that 26-token gap can account for — so the larger machine is the pessimistic estimate. Reproduce with benchmarks/bench_mini_qwen38.py; the full record is in bench_mini_qwen38_results.json.

Neither 4-bit build reaches that tier: affine 4-bit peaks at 16.16 GiB, more than a 16 GB machine has in total. Decode there is 3.7 tok/s — fine for document Q&A, summarization and vision calls, too slow for interactive chat.

The hybrid stack makes KV very cheap — only 16 of 64 layers cache anything, so 64 KiB/token, or 2.0 GiB at 32K context.

pip install "turboquant-mlx-full[vlm]"

python -m turboquant_mlx.convert_vlm \
  --hf-path Qwen/Qwen3.8-27B \
  --mlx-path ./Qwen3.8-27B-tq4-g64 \
  --bits 4 --group-size 64 --quantize-extras --extras-bits 8

python -m turboquant_mlx.generate_vlm \
  --model ./Qwen3.8-27B-tq4-g64 --prompt "..." --max-tokens 256

Measured on mlx-lm 0.31.3 with both mlx-vlm 0.6.3 and 0.6.13 — identical peaks and identical vision results on each, so the [vlm] floor of 0.6.12 reproduces these numbers.

--prefill-step-size is the memory knob for long prompts, and it is a large one. mlx-vlm only chunks prefill above the step size, so a 1342-token prompt runs unchunked at the 2048 default:

--prefill-step-size peak prefill
default (2048 — unchunked here) 21.28 GB 162 tok/s
256 16.77 GB 77 tok/s

−4.5 GB for 2.1× slower prefill. 256 also keeps every TurboQuant layer on the fused Metal kernel, which materializes nothing. This is what makes the 24 GB target work.

lm_head stays off the polar path

With a 248,320-token vocabulary lm_head is 248320×5120 = 1.271B parameters, the largest matrix in the model. PolarQuantizedLinear has a fused Metal kernel only up to _QMM_MAX_TOKENS (256) tokens; past that it dequantizes the weight through full-size intermediates. Measured on exactly that shape at 3-bit/g64:

tokens transient peak
256 +0.116 GiB
257 +13.141 GiB
2048 +13.953 GiB

The same shape as an 8-bit affine layer costs +1.895 GiB at 2048 tokens — that is the logits array itself, with no weight-dequant term at all. So qwen3_5 joins muse_glimmer in VLM_SKIP_PATTERNS, and lm_head is left to the --quantize-extras affine path. End to end on a 1342-token prompt:

build size peak
lm_head affine 15.15 GiB 22.85 GB
lm_head polar 14.52 GiB 33.34 GB

Polar buys 0.63 GB on disk and costs 10.5 GB at runtime — and that 33 GB peak is the difference between fitting a 36 GB Mac and not.

This is easy to miss because mlx-vlm's chunked prefill discards the chunk forward's return value, so MLX never evaluates the matmul at all. But chunking only engages above prefill_step_size (default 2048); a prompt of 257–2048 tokens takes the single-shot branch in generate/ar.py, which slices logits[:, -1, :] and therefore does evaluate lm_head across the whole sequence. That is an ordinary chat turn, not an edge case.

Against MLX's own affine 4-bit

mlx-community/Qwen3.8-27B-4bit is the same architecture at the same 4-bit / group-size-64 setting, with no MTP weights on either side — so this is a like-for-like comparison, run through the same harnesses and the same server.

The two TurboQuant columns differ only in what the non-polar remainder (embeddings, lm_head, vision tower) is stored at — --extras-bits 8 versus 4. They are separate builds with separate numbers, so they get separate columns.

affine 4-bit tq4-g64 extras 8 tq4-g64 extras 4
on disk 14.95 GiB 15.15 GiB 13.81 GiB
bits/weight (polar tier) 4.500 4.251 4.251
PPL, 16K tok 7.2685 7.2496 7.3043
PPL, 32K tok 8.3593 8.3321 8.4061
decode 29.7 tok/s 11.3 tok/s 11.5 tok/s
peak, same harness 16.16 GiB 17.80 GiB 16.47 GiB
vision battery 3/4 4/4 —
Opencode agentic pass (6m42s) pass (6m12s) —

Only the extras 8 build is published, and the vision/agentic runs were done on it; the extras 4 column is the size-matched control.

Take the affine build if you want 4-bit Qwen3.8-27B. It is 2.6× faster to decode at a lower peak, and the perplexity difference is 0.3% — real and stable in ordering across both corpus sizes, but far too small to trade that speed for.

The one place TurboQuant is structurally ahead is storage. At identical bit width and group size, on the same 17408×5120 projection:

weight scales biases bits/weight
affine 4-bit 42.50 MiB 2.66 2.66 4.500
polar 4-bit 42.50 MiB 2.66 — 4.251

The Hadamard rotation symmetrizes each group, so there is no zero-point to store — 5.5% leaner for free. That margin is a rounding error at 4-bit and the reason the codebook only earns its keep at 2-bit and below (see the speed flip).

Two cautions on reading the table. The vision split is not a capability gap: affine's single miss is one string (VOLTAGE 47 → VOLTAGE), and a five-string follow-up probe put it at 4/5, so treat 3/4-vs-4/4 as a tie. And the agentic wall-clock is not a speed metric — affine decodes 2.6× faster yet finished slower, because it spent more tool turns.

Reproduce both halves:

# 16K-token column (--chunks 32) and 32K-token column (--chunks 64);
# both records are checked in under benchmarks/
python -m turboquant_mlx.benchmarks.eval_vlm_perplexity \
    ./Qwen3.8-27B-tq4-g64 ./Qwen3.8-27B-tq4-g64-x4 \
    mlx-community/Qwen3.8-27B-4bit --chunks 32
python -m turboquant_mlx.benchmarks.eval_vlm_perplexity \
    ./Qwen3.8-27B-tq4-g64 ./Qwen3.8-27B-tq4-g64-x4 \
    mlx-community/Qwen3.8-27B-4bit --chunks 64
python -m turboquant_mlx.benchmarks.eval_vlm_vision \
    ./Qwen3.8-27B-tq4-g64 mlx-community/Qwen3.8-27B-4bit

Will it fit?

turboquant-plan --model <path> at 16K context, per machine:

Mac verdict
16 GB ❌ Will not run. Peak 18.44 GB against 15.46 GB usable — over by 2.98 GB. A dense model has no expert tier to stream, so the 16 GB recipe does not apply
24 GB ⚠️ Fits after raising the Metal wired cap, with --prefill-step-size 256 — that knob measures 16.77 GB peak on a 1342-token prompt, against 21.28 GB at the default
36 GB ✅ Resident, 12.34 GB headroom
64 GB ✅ Resident, 39.40 GB headroom

Speed is the caveat. At ~11 tok/s decode and ~110–160 tok/s prefill an agentic turn takes about six minutes, against 33 s for Laguna-XS.2 at 3-bit. It passes, but this is a model to serve for quality rather than latency.