# Lab 021 raw lab detail: Memory decides which model can do the work.

Two Macs with identical file systems and applications, measured on 2026-10-17 and 2026-10-18.

| | Workstation (owned) | Studio (owned) |
|---|---|---|
| Machine | MacBook Pro, Apple M5 Max | Mac Studio, Apple M5 Ultra |
| Unified memory | 36 GB | 256 GB |
| GPU cores | 32 | 80 |
| macOS | 26.6.2 | 27.0.1 |
| Apple default GPU working-set cap (MLX `max_recommended_working_set_size`) | 28.1 GB (78%) | 222.7 GB (87%) |

## The instrument

The certified 22-task pool from loopcontrolbench: Python repair tasks reconstructed from real
merged pull requests, each scored by held-out tests in a network-isolated container. The model
never scores itself. Claude Code 2.1.218 drives each session with file read, edit and shell. Two
concurrent sessions (PAR=2) and a 7,200-second ceiling per task, hardest-first order, no resumes,
except where a cell says otherwise.

The harness ran on a DGX Spark, never on a Mac. Both Macs sit on a personal network, and the
harness executes model-written code. Each Mac's server was reached over SSH tunnels relayed
through the workstation, with a dedicated LiteLLM proxy (`openai/` provider, request timeout
1,800 s, no retries).

## The serving stack, identical on both Macs

| Component | Pin |
|---|---|
| oMLX | v0.7.1rc1, commit `7a5a3d75` |
| MLX / mlx-metal | 0.32.4 |
| mlx-lm | git `94cdcae1` |
| Package set | one freeze, identical after name normalization |
| Custom kernels | built on the Studio, absent on the Max (no Xcode); none target Gemma or Flash-Next |
| Route | oMLX Responses API on both Macs |
| Instances | one model per instance, own state directory, `--no-hf-cache` |

## Models

| Model | Build | Size | Sampling |
|---|---|---|---|
| Gemma 4 26B-A4B | `mlx-community/gemma-4-26b-a4b-it-4bit`, rev `0d77464` | 14 GB on disk, 15.0 GB loaded | temp 1.0, top_p 0.95, top_k 64 |
| Qwen3.8-Flash-Next | `mlx-community/Qwen3.8-Flash-Next-4bit`, rev `07b5dc6` | 111.5 GB on disk, 104.95 GB loaded | temp 1.0, top_p 0.95, top_k 20, thinking on |

The Gemma checkpoint was copied between Macs and SHA-256 verified on all three shards. The
Flash-Next files were verified by size against the published tree (34 files, no mismatches).

KV cache per token, from the model configs: Gemma about 20 KB (5 of 30 layers are full attention,
the rest are sliding-window); Flash-Next about 24 KB (12 of 48 layers full attention, the rest
linear attention). Measured cost per full-context Flash-Next session was about 13 GB once scratch
and linear-attention state are included.

## The memory guard

oMLX refuses work it predicts won't fit. Each tier holds back part of RAM:

| Tier | Reserve |
|---|---|
| safe | 20% of RAM, 6 to 16 GB |
| balanced | 8%, 3 to 8 GB |
| aggressive | 2%, 1.5 to 4 GB |
| custom | a pinned ceiling, 2 GB static reserve |

The dynamic ceiling is free plus inactive memory, minus the reserve. Other applications move it
every second.

## Gemma 4 26B, 36 GB Max

**Load, before any probe.**

| State | safe-tier ceiling | Outcome |
|---|---|---|
| Normal day's apps open (Chrome 10.0 GB resident, plus others) | 8.19 GB | refused, HTTP 507 |
| Chrome, Slack, Creative Cloud, OneDrive, Zoom closed | 14.27 GB | refused |
| 1Password, Messages and Adobe helpers also closed | 14.50 GB | refused, 0.5 GB short |

What remained was the operator's own stack: the desktop Claude app (2.65 GB), Claude Code (1.51 GB)
and Spotlight indexing (about 2.1 GB). The Max then ran at the balanced tier, 8% reserve, as the
proportional match to the Studio's safe reserve of 16 of 256 GB (6.25%). Ceiling at start: 21.1 GB.

**Envelope probe at balanced.**

| Step | Result | Peak wired |
|---|---|---|
| Tool call | pass | 18.1 GB |
| Decode 1 / 2 / 4 / 8 streams, aggregate | 110.3 / 175.4 / 230.8 / 271.6 tok/s | up to 21.4 GB; soft pressure at 8 streams |
| One session, 32K | 2,418 tok/s prefill, TTFT 13.2 s | 20.7 GB |
| One session, 128K | 1,442 tok/s, TTFT 88.5 s | 25.3 GB |
| One session, 250K | refused at preflight: ~30.1 GB needed against a 22.8 GB prefill limit | |
| Two sessions, 128K | both aborted mid-prefill at the hard watermark, model unloaded | 25.3 GB |

Peak wired includes about 3.5 GB the system holds.

**Pool, PAR=2: 1/22.**

| Outcome | Count |
|---|---|
| Sessions ended by a guard refusal | 20 of 22 |
| Sessions refused before their first turn (opening request 28K to 61K tokens, neighbor resident) | 5 |
| Tool-call perseveration loops before the guard ended them (up to 266 repeats of one call) | 4 |
| Misses on tasks a prior Gemma 26B arm solved on a Spark | 13 |
| Solved | 1 (the fix landed before the session's final refused turn) |

The harness records a guard-ended miss as `NO_EDIT_APPLIED` with no session error. The
classification above comes from reading each session log for the guard's refusal text.

Published records on the same pool and harness, 14/22 as NVFP4 at PAR=2 on one DGX Spark under vLLM
(Lab 013's raw detail), and 15/22 in Lab 018 with a corrected tool-call parser. Against Lab 013's
arm, the Max missed 13 of the 14 tasks Gemma solved there.

## Gemma 4 26B, 256 GB Studio

Safe tier, ceiling 210.8 GB, Chrome, Claude and Spotlight left running.

| Step | Result | Peak wired |
|---|---|---|
| Decode 1 / 2 / 4 / 8 streams, aggregate | 163.7 / 268.0 / 391.4 / 539.2 tok/s | 23.0 GB |
| One session, 32K | 5,391 tok/s, TTFT 5.9 s | 22.4 GB |
| One session, 128K | 3,317 tok/s, TTFT 38.5 s | 27.2 GB |
| One session, 250K | served, TTFT 124.7 s | 36.0 GB |
| 2 / 4 / 8 sessions at 250K | all served, 259.6 / 518.1 / 1,075.7 s | 36.1 / 48.8 / 94.8 GB |

No guard events, no swap. Prefill runs one request after another: in the 8-session step the last
session waited 1,066 s for its first token.

**Pool.** Run 1 stalled at 5 of 22 (0 solved) when its harness host stopped answering. Run 2, a
fresh run from a second Spark of the same class with a cold cache, was stopped at 7 of 22 (0 solved,
no guard events) by the author's call once the comparison was clear. One of those seven was solved
by Lab 013's Spark arm; the Studio gave it 50 turns and it still missed. Runaway
generations to Claude Code's 32,000-token output cap appeared on this venue too; they also appear
in Lab 013's Spark record (38 blocks over 40,000 characters across 17 of its 22 sessions).

## Qwen3.8-Flash-Next, 256 GB Studio

Sparse mixture of experts: 512 experts, 10 active per token, 48 layers, native context 262,144.

**Envelope probe, safe tier (202.5 GB ceiling).**

| Step | Result | Peak wired | Model process, est. |
|---|---|---|---|
| Loaded (tool call) | pass | 111.7 GB | ~107 GB (measured later: 104.95 GB phys_footprint) |
| Decode 1 / 2 / 4 / 8 streams | 106.3 / 138.8 / 213.4 / 287.1 tok/s aggregate | 111.3 to 112.5 GB | ~107 GB |
| One session, 32K | TTFT 7.7 s, decode 74.1 tok/s | 114.5 GB | ~109.5 GB |
| One session, 128K | TTFT 24.5 s, decode 59.5 tok/s | 119.3 GB | ~114 GB |
| One session, 250K | TTFT 48.0 s, decode 69.2 tok/s | 125.8 GB | ~121 GB |
| 2 sessions, 250K | second TTFT 98.6 s | 136.6 GB | ~131.5 GB |
| 4 sessions, 250K | last TTFT 195.8 s | 166.3 GB | ~161 GB |

Estimates subtract about 5 GB of Studio baseline, inferred from the Gemma runs.

**Pool, native, PAR=2: 20/22 in 1 h 25 m.** No timeouts, no guard events. Misses:
`dateutil-pr700` and `prettytable-pr296`. Of the 8 tasks Gemma 4 26B missed in Lab 013, it
solved 7, in 8 to 25 minutes each, and the 120 GB cell below solved all 8.

## The emulated 128 GB M5 Max

The Studio stood in for a 128 GB M5 Max by capping the oMLX process with its custom memory guard.
The cap models the server process only. The Studio's own apps and macOS sat outside it, and the
Studio has about twice the laptop's GPU cores, so wall clock flatters a real one.

| Cell | Ceiling | Result |
|---|---|---|
| Stock | 112 GB (128 minus the 16 GB safe reserve) | refused at preflight: a ~55K prefill needed 106.48 GB against a 106.40 GB prefill limit (ceiling minus a 5% margin) |
| Wired limit raised | 120 GB | 20/22 at PAR=1 in 1 h 43 m |

In the 120 GB cell: 0 refusals, 0 aborts, 0 hard-pressure events, 316 soft-pressure events, peak
115.5 GB, largest prompt 142,682 tokens. Misses: `prettytable-pr345` and `requests-pr7433`, both
model early stops (the session ended on a message announcing a next step that never got a tool
call). It solved both tasks the native cell missed. 120 of 128 GB leaves about 8 GB for macOS and
every app, under the 6 to 16 GB the 36 GB Max's own apps used on 2026-10-17.

## Is the workload decode-heavy?

From the oMLX logs of both Gemma cells:

| | Max | Studio (partial run) |
|---|---|---|
| Requests served from the prefix cache | 98% | 99% |
| Prompt tokens reused from cache | 63.4M | 88.5M |
| New prefill computed | 0.83M | 0.79M |
| Decode tokens | 507K | 462K |
| of which runaway generations | 69% | 90% |
| Decode share of compute time, runaways excluded | 71 to 89% | 54 to 81% |
| Re-prefill cost with no cache | ~12 h | ~7 h |

The time split uses envelope-probe rates, not per-request timings. The workload runs as
decode-heavy because the cache absorbs the re-sent context. When memory pressure dropped the
cache on the Max, the refused requests were cold prefills of 28K to 101K tokens.

## The prefix cache on disk

oMLX's `ssd_cache_max_size: auto` sized the SSD prefix cache to about half the free disk.

| | Max | Studio |
|---|---|---|
| Peak on disk at one time | 367 GB (one arm) | ~1.2 TB (one directory, before a delete) |
| Cumulative written, lower bound | 367 GB | at least ~2.0 TB |

Lower bound because only retained bytes are known; evictions weren't measured. Peak answers how much
free disk a box needs. The cumulative total is SSD wear. Neither scales with RAM. Nothing in the
server log warns about either.

## Declared confounds

- macOS 26.6.2 on the Max against 27.0.1 on the Studio.
- Guard tier: balanced on the Max, safe on the Studio, matched by share of RAM reserved.
- Gemma's prior records: NVFP4 under vLLM on a Spark in Lab 013, and Lab 018's corrected-parser run,
  against MLX 4-bit here.
- The Studio's Gemma run 2 and both Flash-Next cells ran their harness on a second Spark of the
  same class. Its harness copy lacks two bookkeeping fields (an instrument tag in the preflight stamp
  and a completion manifest); the session code is identical.
- One replicate per cell at temperature 1.0.
- Memory bandwidth wasn't measured. Decode differences are measured rates.

## Disclosure

Self-funded editorial. Both Macs were bought by the author at retail. Google Cloud is a client of The CTO Advisor LLC, and Google's Gemma 4 is one
of the two models. Apple, Alibaba and NVIDIA are not clients. Anthropic's Claude Code is the harness
in every arm, and an Anthropic model drafted the lab record.

## The configuration matrix: 36 GB to 512 GB

Capacity arithmetic across the memory options, anchored on this lab's measurements, with the 96 GB Ultra
against the 128 GB Max as the close call. Every input is measured in this lab or marked as an
estimate. Nothing in this section is a new measurement.

**Inputs.** Two envelopes per configuration. *Desktop*: oMLX `safe` tier (reserve 20% of RAM, 6 to 16
GB) minus about 21 GB of system and a normal day's apps, back-solved from the 36 GB Max's measured 8.2
GB ceiling with apps open. (The Studio measured 210.8 GB against the formula's 219, so the formula runs
a little generous at the top.) *Dedicated*: the wired limit raised, about 8 GB left for macOS and apps,
as in the 120-of-128 emulation. A session is about 150K tokens of context, the size of the largest
prompt in the Flash-Next pool (142,682).

| Model | Loaded | Per ~150K session | Record on this pool | Basis |
|---|---|---|---|---|
| Gemma 4 26B 4-bit | 15.0 GB | ~7 GB | 1/22 Max; 14/22 on a Spark (Lab 013); 15/22 (Lab 018) | measured |
| Qwen3.8-27B 4-bit (dense) | 16.1 GB | ~10 GB | not cited | estimate (64 KB/token KV); never run on a Mac |
| Qwen3.8-Flash-Next 2-bit | ~76 GB | ~8 GB | none | estimate (80.8 GB on disk); quality never tested |
| Qwen3.8-Flash-Next 4-bit | 105 GB | ~8 GB | 20/22 Studio, 20/22 at 120 GB | measured |
| DeepSeek V4 Flash FP8 | 150 GB | ~7 GB | 22/22 Studio (Lab 020) | resident measured; per-session estimate |

**Sessions that fit, desktop / dedicated** ("no" = can't hold one session):

| Configuration (GPU cores, price) | Gemma 26B | Qwen 27B dense | Flash-Next 2-bit | Flash-Next 4-bit | DeepSeek V4 Flash |
|---|---|---|---|---|---|
| M5 Max 36 GB (32c, $4,099) | no / 1 | no / 1 | no / no | no / no | no / no |
| M5 Max 48 GB (40c) | no / 3 | no / 2 | no / no | no / no | no / no |
| M5 Max 64 GB (40c) | 2 / 5 | 1 / 3 | no / no | no / no | no / no |
| **M5 Ultra 96 GB (64c, $5,499)** | 6 / 10 | 4 / 7 | no / 1 | **no / no** | no / no |
| **M5 Max 128 GB (40c, ~$5,849 unconfirmed)** | 10 / 15 | 7 / 10 | 1 / 5 | **no / 1** | no / no |
| M5 Ultra 256 GB (80c, $10,799 to $12,299) | 29 / 33 | 20 / 23 | 17 / 20 | 13 / 17 | 9 / 14 |
| M5 Ultra 512 GB (80c, unpriced) | 65 / 69 | 45 / 48 | 48 / 52 | 45 / 48 | 46 / 50 |

The matrix reproduces every measured point: the Max's 8 GB desktop ceiling, one Gemma session at 36
GB, Flash-Next 4-bit refused at the stock 128 GB envelope and one session with the limit raised.

**Read the session counts as memory capacity, not throughput.** On the Studio, oMLX prefilled
concurrent long prompts one after another, so the eighth Gemma session waited 1,066 s. More sessions
than about two of these agents add queue, not work.

**96 GB Ultra against 128 GB Max.** At about the same price, they split on the model, not on speed.
- The 128 GB Max is the only one of the two that holds the model this lab measured doing the work,
  Flash-Next at 4 bits, and only as a dedicated machine with the wired limit raised.
- The 96 GB Ultra can't load Flash-Next 4-bit at all. It can hold the 2-bit build only as a dedicated
  machine, and that build's quality is untested.
- For a model that fits both, such as the dense Qwen3.8-27B, the Ultra has 64 GPU
  cores against 40, and the Max has more session headroom (7 against 4 on the desktop). Decode speed on
  a dense model tracks memory bandwidth. Neither machine was measured on it.

The deciding question is which model does the work. If it's Flash-Next 4-bit, the 128 GB Max is the
only laptop-class answer, and it costs the desktop. If a model under ~60 GB does the work at that
score, the 96 GB Ultra is likely the better machine, on cores and on headroom for the desktop. A model's score
carries across platforms once the platform clears a floor: RAM for the model and its sessions, memory
speed for the harness ceiling, and a serving stack that serves the model correctly. Where that speed
floor sits for a dense model on Apple silicon wasn't measured.

Sources for configurations and prices (launch coverage and retailer listings, not Apple's configurator):
[Guru3D](https://guru3d.com/story/apple-m5-ultra-mac-studio-launches-with-36core-cpu-80core-gpu-and-512gb-unified-memory/),
[Thurrott](https://www.thurrott.com/?p=340698), [Appleosophy](https://appleosophy.com/2026/03/03/apple-introduces-new-macbook-pro-with-m5-pro-and-m5-max-chips/),
[Hoxton Macs](https://www.hoxtonmacs.co.uk/products/macbook-pro-14-inch-m5-max-18-core-space-black-2026-premium),
[Let's Data Science on Tom's Hardware](https://letsdatascience.com/news/macbook-pro-delivers-ultraportable-m5-max-performance-8900a641),
[9to5Mac](https://9to5mac.com/2026/03/03/with-the-latest-chips-the-macbook-pro-now-maxes-out-at-7349/), and Lab 020's stated prices
for the 256 GB Studio and the 36 GB Max. One source lists a 96 GB M5 Max option and others don't; it's left out.
