2× Faster Qwen3.8-27B on a 32 GB MacBook: TensorFold vs MLX MTP Speculative Decoding

by | Oct 12, 2026 | Macbook | 0 comments

Quick answer

On a MacBook Pro (M2 Pro, 32 GB), Qwen3.8-27B decodes 20.1 tokens/sec on code with TensorFold 1.0.8 + its DFlash2 drafter — up from ~11 tok/s in LM Studio. That’s 2.2× faster than the model’s own serial decoding and ~48% faster than the previous best (mlx-vlm + MTP). Drafted output is byte-identical to serial (lossless).

Qwen3.8-27B is a 27B-parameter hybrid model (64 layers, ~3:1 linear-to-full attention, with a vision tower). In MLX 4-bit the weights are ~15 GB, which fits in 32 GB of unified memory with room to spare. The catch: the model ships a Multi-Token Prediction (MTP) head that can act as a native speculative-decoding drafter — a small head that drafts several cheap candidate tokens, which the big model then verifies in parallel. Done right it is lossless: the output is byte-for-byte what serial decoding would produce, only faster.

The problem: LM Studio leaves speed on the table

LM Studio’s bundled MLX engine ignores the MTP head (upstream issue lmstudio-ai/mlx-engine#371), so it runs the model serially and tops out at ~11 tok/s. On paper an M2 Pro has ~200 GB/s of memory bandwidth, and a 15 GB 4-bit dense model reading its weights once per token has a hard serial ceiling of roughly 13 tok/s. At ~11 tok/s LM Studio is already ~80% of that ceiling, so no settings tweak can meaningfully help. The only real lever is speculative decoding.

Two speculative-decoding paths

mlx-vlm + MTP TensorFold + DFlash2
Engine mlx-vlm 0.7.6 (MLX) TensorFold 1.0.8 (Zig native)
Drafter mlx-community/Qwen3.8-27B-MTP-4bit (~239 MB, the model’s own MTP head) z-lab/Qwen3.8-27B-DFlash2 (~3.6 GB)
Vision Works Text-only

mlx-vlm + MTP uses the model’s own MTP head as the drafter (auto-detected as --draft-kind=mtp). It is a drop-in, keeps image input working, and is easy to run. TensorFold + DFlash2 is a younger, from-scratch Zig engine that serves the 27B with a dedicated DFlash2 drafter — faster, but text-only and (in 1.0.7) not qualified for M2 chips.

The TensorFold saga: from 0 tokens to the fastest option

My first TensorFold attempt (1.0.7) failed completely: the server started, then every request errored with RoundGpuFailure and produced 0 tokens. The root cause was memory — 1.0.7 kept the projection weights in memory twice (~32+ GB), which blew past the 32 GB machine and caused an out-of-memory failure.

I reported it as TensorFold issue #643. Another user dug in and found the exact cause and fix, and the maintainer confirmed and shipped both fixes in 1.0.8:

  • 93a218dda — keep the projection weights once on Metal (the memory fix)
  • b5056a511 — a failed GPU round now ends only its own requests (and logs the Metal error)

After brew upgrade tensorfold, the same model loaded in ~19 GB instead of 32+, and generated tokens cleanly.

The benchmarks

All runs: --max-tokens 256 --temperature 0 (greedy), on a quiet machine, plugged in, median of 3 runs.

Prompt A — code-heavy

Write a detailed 400-word tutorial on quicksort in Python, then a short scene describing a rainstorm over a city.

Config Decode tok/s Speedup vs own serial
LM Studio (baseline) ~11 —
mlx-vlm serial 11.28 —
mlx-vlm + MTP drafter 13.54 1.20×
mlx-vlm server + MTP 13.4 —
TensorFold 1.0.8 + DFlash2 20.1 2.21×
TensorFold "draft": false 9.09 —

Prompt B — prose-heavy

Describe in vivid prose a rainstorm rolling over a sleeping city at night.

Config Decode tok/s Draft acceptance
mlx-vlm + MTP 11.30 53.7%
TensorFold + DFlash2 11.6 28.5%

Key findings

  • TensorFold 1.0.8 + DFlash2 wins for text: 20.1 tok/s on code — +48% over mlx-vlm+MTP (13.54) and 2.2× over its own serial (9.09).
  • The gains are content-dependent. On code, TensorFold’s DFlash2 drafter accepts 56% of drafted tokens (~4.8 tokens/round). On fresh prose, acceptance drops to 28.5% and both engines roughly tie at ~11.5 tok/s.
  • mlx-vlm + MTP is the safe, general pick: +20% on code, ~0% on prose, keeps vision working.
  • Lossless: drafted output was byte-identical to "draft": false for both engines on both prompts. TensorFold was even deterministic across runs (same sha256 every time).

Memory (the thing that nearly killed the whole idea)

Config Peak footprint
mlx-vlm serial 16.4 GB
mlx-vlm + MTP 17.1 GB
mlx-vlm + MTP + image 19.4 GB
TensorFold 1.0.7 (broken) 32+ GB (OOM)
TensorFold 1.0.8 19.2 GB

That 32 GB → 19 GB difference is the whole story of whether a 32 GB Mac can run the model at all.

Vision

mlx-vlm + MTP still handles images (correctly described a screenshot in testing; image prefill ~55 s). TensorFold’s 27B row has no vision tower, so if you need image input, mlx-vlm is the one.

How to reproduce

mlx-vlm + MTP (keeps vision)

uv tool install mlx-vlm
mlx_vlm.generate --model "$HOME/.lmstudio/models/lmstudio-community/Qwen3.8-27B-MLX-4bit" 
  --draft-model mlx-community/Qwen3.8-27B-MTP-4bit 
  --max-tokens 256 --temperature 0 --verbose --prompt "Your prompt here"

TensorFold 1.0.8 + DFlash2 (fastest text)

brew install ashhart/tensorfold/tensorfold   # or: brew upgrade tensorfold
tensorfold pull TensorFold/Qwen3.8-27B-MLX-4bit
tensorfold pull z-lab/Qwen3.8-27B-DFlash2
tensorfold serve TensorFold/Qwen3.8-27B-MLX-4bit 
  --drafter z-lab/Qwen3.8-27B-DFlash2 --context 4096 --temperature 0 --no-thinking --port 8081

Then POST to http://127.0.0.1:8081/v1/chat/completions like any OpenAI-compatible API.

Bottom line

Use case Best pick Why
Fastest text generation TensorFold 1.0.8 + DFlash2 20 tok/s code, 2.2× over serial
Need image input mlx-vlm + MTP +20% on code, vision works
Zero-hassle GUI LM Studio ~11 tok/s, no MTP support yet

If you’re on an M-series Mac with 32 GB and want the fastest local text inference for Qwen3.8-27B today, upgrade to TensorFold 1.0.8 and add the DFlash2 drafter. And if you hit a bug, file it — the maintainer fixed #643 within a day.

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *

avatart

About

I’m a System Administrator and a developer with 10+ years of experience.