HardwareApple M4 Max
ModelQwen3.6-35B-A3B
Workload18-turn agent session
StatusDeployed

We run large language models on our own Apple Silicon hardware to power the coding agent our team builds with. The software that serves those models is mlx-vlm, an open-source inference server. We forked it. This is what the stock version does, why it wasn't enough, and what the fork bought us.

Two costs decide whether an agent feels fast

Every response carries two separate costs, and they are fixed by two entirely different mechanisms.

Cost 1 — the wait

How long you stare at nothing before text appears. The model must read the entire conversation before it can say a word.

Cost 2 — the stream

How fast text arrives once it starts. Bounded by how many tokens per second the hardware can produce.

Coding agents punish the first one especially hard. Every turn re-sends the whole growing conversation, so the wait climbs turn over turn. In the 18-turn session we benchmark against, the conversation reaches 10,500 tokens — and re-reading it from scratch every turn means 96,500 tokens of work across the session to produce about 3,000 tokens of output.

Stock mlx-vlm fixes either cost. Not both.

The stock server ships two accelerations. Speculative decoding uses a small draft model to guess several tokens ahead, which the large model verifies in one pass — that fixes the stream. Prefix caching remembers text the server has already read, so repeated history isn't re-read — that fixes the wait. You choose one when you start the server, and you pay the other cost in full.

Launch with speculation
Wait, average
5.9 s
Wait, worst case
11.2 s
Stream
98 tok/s
Session
141 s
Launch with caching
Wait, average
0.6 s
Wait, worst case
1.8 s
Stream
61 tok/s
Session
68 s

The exclusion is not architectural. Upstream simply switches the cache off whenever a draft model is loaded — a one-line precaution, never revisited.

There is a second, quieter gap. Stock caching only remembers text we sent. Everything the model wrote itself is discarded the moment a turn ends, then re-read from scratch on the next one. In an agent session, the model's own output is most of the conversation. Nothing survives a restart — every restart starts cold. And the cache collapses to zero hits once four sessions run at once, which is precisely the multi-agent case.

Three changes to the fork

Ranked by measured impact.

01

We removed the false choice

The cache and the speculation loop turn out to operate on the same underlying data structure and don't actually conflict for our model pairing. The exclusion was a precaution, not a constraint. We now run both at once.

02

We made the model's own output reusable

The fork checkpoints the model's working state mid-generation, at the end of each turn, and on cancellation, holding those checkpoints in a size-capped session store backed by disk. Neither stock mode can do this: the conversation stops being re-read at all, and a restart comes back warm instead of cold.

03

We rewrote the request scheduler

Incoming work now interleaves with in-flight speculation instead of queueing behind it, and a new request can join a batch already in progress. This is where the concurrency gain comes from.

Same hardware, same model, same real session

Measured against the stock server in its best configuration for each metric — not its worst. Streaming speed is weighted by tokens produced, and session time is normalized to a common output volume, since each server picks its own reply lengths.

One session · lower is better
Wait before responseaverage
0.31 s
0.31 s
tie
Wait before responseworst case
0.70 s
0.53 s
1.3× better
Total session time18 turns
52.8 s
37.6 s
1.4× faster
Redundant worktokens re-read
3,765
364
10× less
Streaming speedhigher is better
68 tok/s
100 tok/s
+48%
Four concurrent sessions
Wait before responseaverage
3.57 s
1.12 s
3.2× better
Wait before responseworst case
11.1 s
3.09 s
3.6× better
Aggregate throughputhigher is better
53.9 tok/s
86.0 tok/s
1.6×
Stock mlx-vlm, best modeOur fork
Read this honestly. Against the stock caching mode alone, the single-session win is real but moderate — about 1.4×. The decisive margins are against the speculation mode we had actually been running, and under concurrency, where the stock cache stops working entirely and ours keeps running.

What this costs us to carry

Drop-in compatible

The API contract is byte-for-byte identical to stock. Any OpenAI-compatible client works unchanged; new telemetry is additive only.

Reversible deployment

The fork serves the production port; the stock server stays installed on another, kept stopped because two copies of a 35B model cannot share memory without slowing each other. Switching back is two commands.

Narrow validation

v1 is proven on one model pairing. Other draft-model families fall back to stock behavior rather than failing — conservative by design, but broader coverage is unvalidated work.

Requires our client

The largest win depends on the client returning the model's prior replies in a specific form. Ours does. A third-party client gets stock-caching benefit, not the full gain.

The part that generalizes

There are two lessons here, and the second one cost us more than the first.

The first: the speedup didn't come from a clever algorithm. It came from a default nobody had revisited. Someone shipped a one-line safety measure, it was correct at the time, and every user downstream inherited it as a law of physics. Founders hit this constantly — the tool you build on states a limit in its docs, and you plan around the limit instead of testing whether it still holds. Reading the source took an afternoon and was worth more than a month of working around it.

The second: our first benchmark was wrong, in our own favor. We had measured the fork one way and the stock server another — letting the fork generate its own shorter replies while the baseline replayed a longer recorded transcript — and we compared a full-speed fork against baselines recorded while the machine was in Low Power Mode. Neither was deliberate. Both flattered us, and the published numbers came out roughly three times better than the truth.

Nobody caught it until someone re-derived the numbers from the raw run data and asked why the fork's session had processed a quarter as many tokens as the baseline's. If you are going to publish a benchmark, have someone try to break it first — and be suspicious when a result is exactly as good as you hoped.

Agent quality is still bounded by iteration speed, and the honest version of the win is a session dropping from 141 seconds to 38 against the configuration we had been running, plus 1.6× the throughput at a third of the wait when four agents work at once. Smaller than our first claim. Still the difference between staying in flow and context-switching away.

Benchmarks: replay of a recorded 18-turn agent session on an M4 Max in high power mode, one resident server at a time, all runs taken on a single day against a stock server restored specifically to measure against.

We dig into this stuff every day.

Launch80 is an R&D lab and community of engineers running local models. Come talk local AI, share configs, and compare hardware.

Join the Discord