Lab 022 · Editorial lab

Memory bandwidth sets how fast a 31B dense model works, not how well.

By Keith Townsend · October 19, 2026

Can a DGX Spark run a 31B model well enough for an AI coding agent? It holds the model easily, but every word the model writes means reading all 31 billion of its weights from memory, and the Spark’s memory is slow. This lab ran the same model through the same coding agent on a Spark and on a Mac Studio, whose memory is more than four times faster. The Studio was about five times faster per task, and both solved about the same tasks. On the Spark that is too slow to be practical, and with a time limit per task it stops finishing them.

One dense model (Gemma 4 31B, 4-bit), one Mac Studio (M5 Ultra, 80-core GPU, 256 GB) and one DGX Spark, both at two concurrent sessions with a two-hour ceiling per task. The Studio ran llama.cpp with a Q4_K_M file; the Spark ran vLLM with NVIDIA’s NVFP4 file, measured in August. Runtime, file format, proxy route and date differ along with the hardware, so the speed ratio is not bandwidth alone. The matched comparison is the 11 tasks the single Spark ran, the half of the pool that Lab 013’s rented L40S venue had failed. A one-task solve difference is noise on this pool. Bandwidth figures are the vendors’ published numbers, not measured here.

Do Size hardware for a model with many active parameters by memory bandwidth, not memory capacity. What counts is the bytes read per token: a 31B dense model reads all 31 billion weights, a 26B mixture-of-experts model with 4B active reads about 4 billion. The Spark held the 31B dense model easily and decoded it at 7.0 tokens per second; the Studio decoded it at 42.8. Both solved about the same tasks, and the Studio’s median task took about one fifth of the Spark’s time, 12.8 minutes against 61.0.

Don’t Do not plan agentic work around a 31B dense model on a DGX Spark. It is too slow to be practical, as Lab 013 found. On the 11 tasks a single Spark ran, its five solves took 43 to 66 minutes each. With a 60-minute limit per task it would have kept three, with a 40-minute limit none. The Studio’s six solves all finished within 13 minutes.

Don’t Do not trust a serving stack until it has run one real agentic task. On the Studio, oMLX passed every short test with this model and then called the same file read more than 600 times per session in the harness. llama.cpp on the same box ran clean. Read the session log, not just the speed and the error log.

Self-funded editorial. The Mac Studio was bought by the practice, and the DGX Spark was bought at retail for an earlier lab. Disclosure: Google Cloud is a client of this practice, and the model under test, Gemma 4 31B, is a Google model. Google did not commission, fund, preview or have any say in this lab. Apple and NVIDIA hardware are both under test, and Anthropic’s Claude Code is the agent harness on every arm. Neither Apple, NVIDIA nor Anthropic is a client. No vendor paid for this answer.

Can a DGX Spark run a 31B model well enough for an AI coding agent? It holds the model easily. The problem is speed: every word the model writes means reading all 31 billion of its weights from memory, and the Spark's memory is slow. I ran the same model, Gemma 4 31B, through the same coding agent on a Spark and on a Mac Studio with an M5 Ultra, whose memory is more than four times faster. On the same 11 tasks the Spark solved 5 and the Studio solved 6. The Spark's median task took 61 minutes. The Studio's took 12.8. Faster memory made the model faster, not smarter.

Why the active size is what matters

A model's decode speed is roughly memory bandwidth divided by the bytes it reads per token. That's its active parameters times their precision. A dense model reads every weight, so a 31B dense model reads all 31 billion. A mixture-of-experts model reads only the experts it routes to. So dense isn't the variable. Size is. On the same Spark, Lab 016 measured a 12B dense model at 22.0 tok/s and a 26B mixture-of-experts model with about 4B active at 30.0. Small active models don't scale perfectly with bytes, because routing and kernel overhead take a bigger share, but they're nowhere near the 31B. Capacity tells you whether the model fits. Bandwidth, against the active size, tells you how fast it runs once it does. The Spark has 128 GB, far more than this model needs, and it still decoded it at 7.0 tokens per second. The Studio decoded it at 42.8.

That's about 6x, and not all of it is memory. NVIDIA's 4-bit file reads about 15.5 GB per token, so 273 GB/s caps the Spark at 17.6 tok/s. It reached 40 percent of that, because the GB10 has no native 4-bit floating-point conversion. Give the Spark a perfect kernel and it's still 2.4x under what the Studio actually measured. Better software narrows the gap. It can't close it. I didn't run the same runtime on both boxes, and peer testing of this pairing already says the same thing.

What the time buys

The harness turns decode speed into real work. Twenty-two repair tasks from real pull requests, two sessions at a time, a two-hour ceiling per task, and a deterministic gate that runs the held-out tests. In Lab 013 I ruled the 31B out of this harness on the Spark: too slow to be practical. My notes kept one partial run that shows why. A single Spark worked 11 of the tasks in August. On those 11, summed task time was 10.86 hours on the Spark against 3.79 on the Studio. Three tasks flipped between the boxes, one toward the Spark and two toward the Studio, which is normal for this pool at its borderline.

The Studio didn't get there by cutting corners. Its sessions took more turns than the Spark's, 176 against 126, at 78 seconds per turn against 310.

So the bandwidth bought time, not answers. But that only holds because the ceiling was two hours. The Spark's five solves took 43.4 to 66.2 minutes each. Cap tasks at 60 minutes and it keeps 3 of them. Cap them at 40 and it keeps none. The Studio's six solves all finished inside 12.9 minutes. If your agent works to a time budget, the slow box doesn't just make you wait. It loses answers.

On the full pool the Studio solved 15 of 22 in 2 hours 30 minutes, with no timeouts. The single Spark never ran the full pool. At its pace it would take longer than a working day.

The runtime nearly hid all of this

My first Studio run failed, and nothing reported an error. The model passed every check I run before a pool on oMLX, the server Lab 020 used: a tool call, decode speed, and recall of a planted fact at 38,000 tokens. Then, in the harness, the first two sessions read the same file 603 and 628 times. Turning off the prefix cache didn't stop it. A different model file stopped the loop and wrote 32,000 tokens of nonsense instead. llama.cpp on the same box, same settings, worked the task like a normal agent: 15 turns, reading, editing and rerunning tests.

If I'd stopped at oMLX, I'd have blamed the model or the Mac. Neither was the problem.

So what?

If you're sizing hardware for a model with a lot of active parameters, size it for bandwidth. Count the active parameters, not the label on the model. Capacity gets the model loaded. Bandwidth decides whether an agent waits 13 minutes or an hour for each task, and under a tight time budget it decides whether the agent finishes at all. A DGX Spark holds a 31B dense model and takes about five times as long per task as the Studio, which is too slow to be practical. Give it a small active model and it's a different box. Where's the threshold between 273 GB/s and 1.2 TB/s? I don't know yet. That's the next measurement. Whatever you buy, run one real agentic task on your serving stack before you trust it, and read the session, not just the speed.

The numbers

Median minutes per task, same 11 tasks
One DGX Spark (vLLM, NVFP4, August 2026) against the Mac Studio (llama.cpp, Q4_K_M), two sessions each, same harness, two-hour ceiling. The Spark’s one timeout counts at 120 minutes; excluding it, the Spark’s median is 59.1. The 11 are the tasks the single Spark ran, the harder half of the pool.
61.0 against 12.8
Solved, same 11 tasks
Spark against Studio. Three tasks flipped between them: one toward the Spark, two toward the Studio.
5 against 6
Minutes per solved task
Spark against Studio. Under a 60-minute ceiling the Spark keeps 3 of 5 solves; under 40 minutes, none.
43.4 to 66.2 against 2.7 to 12.9
Summed task time, same 11 tasks
Spark against Studio.
10.86 against 3.79 hours
Seconds per turn, same 11 tasks
Spark against Studio. The Studio sessions took more turns, 176 against 126.
310 against 78
Single-stream decode, 256-token probe
Spark (vLLM, NVFP4) against Studio (llama.cpp, Q4_K_M). oMLX on the Studio: 46.2. 8-bit on the Spark: 6.8 to 6.9.
7.0 against 42.8 tok/s
Memory bandwidth, published
GB10 against M5 Ultra, 4.4x. Vendor figures, not measured here.
273 GB/s against 1.2 TB/s
Spark 4-bit decode, share of its bandwidth limit
Computed here: Lab 013’s measured 7.0 tok/s against 17.6 tok/s, which is 273 GB/s over about 15.5 GB read per token.
about 40%
Full pool, Mac Studio
Run sf_gemma31b_studio_r2, 2026-10-18. 2 h 30 min elapsed, no timeouts, no session errors, median 7.4 min per task.
15 of 22
Two sessions on the Studio, llama.cpp
58.0 aggregate, short prompts. oMLX ran 43.1 per stream, 85.0 aggregate.
29.3 tok/s per stream
Studio decode at harness-sized context
Measured on oMLX only, after 35.8K and 48.1K cold prompt tokens. Not measured on llama.cpp. Prefill on llama.cpp: 909 tok/s at 2.4K, 728 at 38.4K.
25.8 and 21.9 tok/s
oMLX run, file reads per session
Run 1, first two sessions, stopped at 71 minutes with 0 of 22 done. On llama.cpp the diagnostic run of one of those tasks took 15 turns.
603 and 628
Metered spend
Owned hardware throughout.
$0

Where each layer belongs

Read for portability. Could you take this layer elsewhere without rebuilding? How to read this table

LayerMac StudioDGX Spark
Layer 0 · Substrate
Compute, Fabric & Storage Substrate
Retained on both. Each box ran its own 4-bit conversion of the same published open weights, NVIDIA’s NVFP4 file on the Spark and a GGUF file on the Studio, and either conversion can be redone from those weights, so nothing at this layer tied the workload to one vendor’s silicon. Commodity NVIDIA hardware is Retained by the instrument’s rule. On the Studio both runtimes tried are open source. Bandwidth decides how fast the model runs at this layer, not who controls it.
RetainedRetained
Layer 2C · Reasoning
Agentic Infrastructure — The Reasoning Plane
Retained on both, because open weights ran on self-operated serving with the configuration written down. The Studio result shows what that ownership costs: the first runtime chosen produced a broken agent and the second did not, and only the operator was in a position to catch it.
RetainedRetained

Method and disclosure

Self-funded. The Mac Studio was bought by the practice. The DGX Spark was bought at retail. No vendor was briefed, reviewed this, or was given advance sight. Google Cloud is a client of this practice, and the model under test is a Google model; Google had no part in this lab.

The repair pool, its certification and the deterministic gate are the instrument used in Labs 011 to 014, unchanged, run through the same v1 Claude Code harness and litellm proxy at two concurrent sessions with a 7,200-second ceiling per task. The launcher and serving configuration are recorded with each result.

Studio, run 2: llama.cpp b11399 (commit 2ca15f5), built from source with Metal, unsloth/gemma-4-31B-it-GGUF Q4_K_M (revision c1ac76e, SHA-256 verified), two slots of 131,072 tokens, flash attention on, reasoning off, temperature 1.0, top_p 0.95, top_k 64. The harness ran on a DGX Spark and reached the Studio through an SSH relay, because bench code is not run on a machine on the author’s personal network.

Studio, run 1 and diagnostics: oMLX v0.7.1rc1 (commit 7a5a3d7) on MLX 0.32.4, with mlx-community/gemma-4-31b-it-4bit (revision 696d436c) and mlx-community/gemma-4-31b-it-nvfp4 (revision a0a935b). Run 1 was stopped at 71 minutes and is void. The diagnostics ran one task each.

Spark comparator: arm sf_gemma31b_nvfp4_gb10, 2026-08-03 and 04, NVIDIA’s vLLM image, nvidia/Gemma-4-31B-IT-NVFP4, memory utilization 0.70, 131,072-token context, two sessions.

The venue preflight was changed for this lab to read llama.cpp’s context field, which reports under a different name than vLLM and oMLX. Its behavior on the other servers is unchanged.

Reviewed before publication by a model from a different family (Gemini, via the Antigravity CLI) instructed to attack the record. Its findings and their disposition are kept with the lab’s working notes.

The harness, the gate and the certification tooling are public. The assessment methodology, the vendor grades and the judgment that turns a score into a placement are not.

The essay on this page was drafted by Claude Opus 5.5, an Anthropic model, from this record under the author’s voice specification, passed the deterministic post gate, and was validated by the author before publication. Anthropic’s Claude Code is the agent harness on every arm of this lab, so the drafting model’s vendor also appears on the bench.

The essay on this page was rendered from the lab’s frozen structured record by a model writing under this practice’s voice specification, with a deterministic gate checking every figure against the record before publication. The record is the canonical surface: where prose and record disagree, the record rules.

Download the raw lab detail (Markdown)