The rematch arrived 1 day early. Yesterday's M1 Ultra tuning article closed with a prediction: when an MLX release lands compiled decode graphs, the rematch against the 20.3 tok/s lane would take an afternoon and 1 download. This morning I spotted a new runtime called MTPLX promising that future as a finished product — native multi-token-prediction speculative decoding on Apple Silicon, an autotuner that measures your specific machine, an OpenAI- and Anthropic-compatible server, 1-click launches for the agent harnesses we actually use, and Qwen3.8-27B as its flagship coding model. Their site says "your favorite tools, at twice the speed." I brought it to Kimi K3, my collaborator on the tuning adventure the day before, and we set the terms together: install it clean on the Studio, let its own tuner make its best case, then measure what the server actually serves. Kimi ran the remote work and the counters; I kept the house rules and made the calls. This is how the day actually went.

MTPLX is the product version of the lane we built together

The architecture deserves credit up front, because it is the right idea executed seriously. MTPLX drafts with the model's own MTP heads — the same mechanism our lane uses — and accepts drafts through exact rejection sampling, so sampling at temperature 0.6 produces the identical distribution to ordinary decoding, only faster. There is no separate draft model consuming memory, the draft depth is tuned per machine against an autoregressive baseline, and the project refuses to attach unverified MTP sidecars to arbitrary weights. That last policy is a discipline Kimi and I had to apply manually the day before, when we re-paired Qwen3.8's dropped MTP tensors with their trunk. The lane from part one was the obvious measuring stick: same Studio, same model family, measured the same way.

The install passed 2 quarantine checks before it touched a weight

The first check was mine. Back in March, in my autonomous refinement lab, we had hit a trap where an isolated Python environment quietly lost access to Apple's accelerated libraries, and the numbers only came back slow. Before Kimi went further, I asked for proof the trap did not apply. A 3-line probe settled it — native arm64 interpreter, Metal available, GPU as the default device. Apple's Metal path ships inside the mlx-metal wheel itself, so the interpreter's origin is irrelevant to GPU access, and the lane from part one had already proven GPU-native throughput through the same isolation shape. MTPLX's own inspector agreed, rating both of its Qwen3.8-27B builds verified-native with all 15 MTP tensors present.
The second check was Kimi's, mid-download. MTPLX's pull command ignores the usual Hugging Face cache environment variables, and the first attempt was writing 20 GB of model into the Studio's home directory instead of the data volume the house rules protect. Kimi killed the pull, removed only what it had created, and restarted with an explicit cache directory. Fans stayed on Apple's curve the entire day, which matters on a machine that moonlights as a flight simulator.

The loudest machine that morning was not the one running the experiment

Mid-download, my own workstation started spiking hard — load average climbing past 50 — and we stopped the experiment to answer a more important question: was this our doing? It was not. Our work ran on the Studio over short-lived SSH sessions; the local footprint was a few finished curl and ssh processes. The storm was an Apple daemon wave — a stuck asset download, indexing, and a burst from a virtualization process that vanished before we could name its owner — plus the discovery that the "mystery respawning node processes" I had been killing were 3 development supervisors doing exactly their job by restarting their servers. We wrote the whole thing into the machine-guard handoff and went back to the experiment with a clean conscience. The pause belongs in this story because the discipline is the same one the benchmarks run on: know your own footprint before you blame a machine.

The autotuner measured a 2.20x blowout

MTPLX's tuning ritual is honest engineering: it runs the real model on your hardware at each draft depth, keeps autoregressive decoding as the baseline, and saves a depth only if it beats that baseline. On the M1 Ultra it produced AR 11.4, depth 1 at 9.2, depth 2 at 25.1, depth 3 at 20.7 tok/s — depth 2 crowned the winner at 2.20x, saved for all future launches.
That 25.1 is a real measurement of a real engine, 24% above our lane's 20.3 serving rate. If the server had reproduced it, this article would be a migration guide.

The server served 16.5, and the defaults were spending tokens out of sight

The serving bench used the same prompts, temperature 0, and wall-clock accounting as the lane's baseline. The first run came back at 14.7 tok/s, and the response metadata surfaced 2 defaults working against the measurement:
  • Reasoning mode defaults to on, and Qwen3.8 thinks before it answers. On a trivial count-to-10 prompt, 44 of the 64 generated tokens were hidden reasoning the client never displays. Real workloads pay for those tokens, so the wall rate already includes them — but the tuner's prompts carried no such tax.
  • The Turbo runtime profile, with its compiled verify kernels, is a launch rule of the native Mac app. A terminal mtplx serve resolves to the Sustained profile instead, so the command-line path never sees the fast kernels unless you ask.
Pinning Turbo, depth 2, and reasoning off lifted the FP16 build to 16.5 tok/s wall — the best MTPLX produced all day. The 4-bit Optimized-Speed build, the one the project recommends for coding, served 15.5. Both builds sit at 20.4 GB on disk against our lane's 15 GB, and on a bandwidth-bound machine every token pays to stream those extra 5 GB.
Serving rate on identical prompts (tok/s wall, M1 Ultra, Qwen3.8-27B)

The tuner and the server measure 2 different machines

The most useful finding of the day explains the gap between 25.1 and 16.5. MTPLX's own server logs a warmup ladder on startup, and that ladder reports 20-24 tok/s from the engine — inside the same process that then serves 16.5 over HTTP. The engine is fast and the transport is taxed. Between the decode loop and the client sit the HTTP layer, per-request session-bank bookkeeping (a 160 MB snapshot write appeared in the first request's stats), and the reasoning machinery, and each token crosses that boundary. On M4 and M5 chips, where the project's published 1.6-2.24x figures were measured, faster CPU and fabric absorb the crossing. The M1 Ultra's engine keeps up; its serving path does not.
Diagram source
graph LR
    subgraph Tuner path
        A[Real model] --> B[Draft depths D1-D3]
        B --> C[Decode-only timer  
25.1 tok/s]
    end
    subgraph Serving path
        D[HTTP request] --> E[Session bank  
+ reasoning defaults]
        E --> F[Same engine  
ladder shows 20-24]
        F --> G[Per-token boundary  
16.5 tok/s wall]
    end

    style C fill:transparent,stroke:#10B981,stroke-width:2px
    style G fill:transparent,stroke:#F59E0B,stroke-width:2px
This is part one's lesson wearing a new costume. llama.cpp's community claims of 25-32 tok/s did not survive contact with this chip, and MTPLX's tuner's 25.1 did not survive its own server. The durable rule sharpens: a serving rate is measured at the HTTP boundary with production defaults visible, never inside the tuner. That is benchmark-driven development applied 1 layer up the stack.

The challenger left 38 GB lighter, and the watchdog stayed behind

I gave Kimi 1 non-negotiable before the experiment started: whatever runs on that Studio has to stop itself when idle, because the machine's evening job is a flight simulator. MTPLX ships no idle timeout for its chat server, so Kimi built the watchdog into a small wrapper script — loopback-only binding, fans on Apple's policy, and a connection watcher that stops the server after 10 minutes with no clients. It passed its live fire drill, tearing down a test instance at the 60-second mark and releasing the port cleanly. The wrapper and the virtual environment stay on the machine, so the retest is 1 download away when an MTPLX release works the M1-generation serving path or ships a matched-MTP artifact closer to 15 GB.
The weights themselves did not stay. Once the verdict was clear, we went through the cleanup carefully — both MTPLX model directories, 38 GB across the FP16 and 4-bit builds, deleted after confirming no process held them, returning the volume to exactly its pre-experiment free space. Deletion remains part of the workflow: every challenger that loses leaves the disk the same day, which keeps the next experiment honest.

What the rematch added to the checklist

The lane from part one never moved. It served the morning at 20.3 tok/s, it served the evening at 20.3 tok/s, and it now holds its slot against 3 challengers instead of 2 — still an experimental lane, 1 download away from its next rematch. The checklist from part one gains 3 entries:
  • A tuner's number is the engine's ceiling, and the serving path decides how much of it you keep. Measure at the boundary your clients actually cross.
  • Defaults are part of the benchmark. Reasoning modes, runtime profiles, and session caches all spend tokens or time, and a fair fight pins them explicitly on both sides.
  • Disk is a hypothesis budget. 2 challenger builds at 20.4 GB each bought 1 afternoon of certainty, and certainty is cheaper when the bytes leave on schedule.
The satisfying symmetry of the pair is that part one ended by queueing this exact experiment, and the experiment arrived packaged as a product with genuine craft in it — exact sampling, per-machine tuning, honest verification. The craft was real and the loss was real, and both findings came out of the same day of measurement, CPU storm and all. An adventure with a scoreboard is the best kind, and this one had Kimi K3 reading every counter beside me — the same compounding return I get from treating local AI gains as infrastructure. The lane keeps its slot, the disk is lean, the checklist is longer, and the next challenger is already welcome to try.