# On this fine-tune, the Mac Studio trained six times faster than a DGX Spark, and both adapters passed.

> Lab 026 · Editorial lab · Status: published · Author: Keith Townsend · October 20, 2026  
> Published by The Advisor Bench LLC · Layer2C Labs  
> Canonical: https://labs.layer2c.com/labs/finetune-venue · Structured: https://labs.layer2c.com/labs/finetune-venue.json

**Finding:** The same rank-16 LoRA on Gemma 4 26B-A4B trained in 17 minutes on an M5 Ultra Mac Studio and 2.2 hours on a DGX Spark, both adapters passed the same bar, and they ended 1,901 of 1,910 test conversations the same way. With memory settings matched as far as mlx-lm allows, the Studio was about 6x faster; those settings explained 1.3 to 1.4x of the gap. A narrower attention-only rank-1 adapter failed on the Mac trainer twice where the Spark one passed.

**Question:** The common assumption is that NVIDIA’s CUDA stack wins at fine-tuning. This lab trained the same LoRA adapter (a small set of extra weights trained on top of a model) for Lab 24’s clinic interpreter on two owned boxes: a DGX Spark, NVIDIA’s desktop GB10 system, and a Mac Studio with an M5 Ultra and 256 GB. Same data, recipe settings matched as far as the two trainers allow, scored the same way. The Studio finished in 17 minutes against 2.2 hours, and its adapter passed the same bar. It lost at one setting: a very narrow adapter that the Mac’s trainer didn’t learn as well as the Spark’s.

**Load:** One base model, Gemma 4 26B-A4B, a mixture-of-experts (MoE) model with about 4B active parameters per token. Lab 24’s training split: 2,872 short examples (about 230 tokens each, longest 354), validation 320. LoRA on the attention projections at rank 16 (and rank 1), 3 epochs, 2,154 steps of 4, learning rate 1e-4 cosine, loss on the answer only. Scored on Lab 24’s 1,910 frozen-corpus conversations and 588 control-set conversations. One DGX Spark, one Mac Studio, owned, October 2026.

**Raw lab detail:** https://labs.layer2c.com/downloads/finetune-venue-lab-detail.md
**Vendors:** Apple (hw), NVIDIA (hw), Google (model), CoreWeave (ref), Anthropic (bench)
**Layers:** Layer 0A (Compute), Layer 2C (Reasoning)
**Themes:** operating-cost-of-ownership, model-ownership, fine-tuning-limits

## Verdict

**Scope:** One model (Gemma 4 26B-A4B, bf16), one short-example LoRA job, one DGX Spark and one Mac Studio (M5 Ultra, 256 GB), one seed per run. The Spark ran Lab 18’s PyTorch and PEFT recipe in NVIDIA’s NGC container; the Studio ran Apple’s mlx-lm trainer. Software differs along with the hardware, so the speed ratio isn’t silicon alone. Power is each GPU’s rail as the system reports it, not wall-plug. A job with long sequences, or a dense model, may behave differently and is the next lab.

- **DO:** For short-example LoRA on a mixture-of-experts model this size, expect the Mac Studio to train faster than a DGX Spark, and confirm it on your own job. The Studio trained the rank-16 adapter in 993 seconds; the Spark took 8,056. With memory settings matched as far as mlx-lm allows, the Studio was 5.8 to 6.3 times faster, and the adapters cleared the same bar against the prompt: 0 and 1 consent misreads of 496, 7 and 4 avoidable hand-offs of 955.
- **DON'T:** Don’t assume a DGX Spark trains faster because it runs CUDA. On this job it didn’t, and memory capacity wasn’t the reason: the job needed about 70 GB, and with MLX’s limit set at 64 GiB the Studio trained in the same 16.5 minutes. Benchmark your own model on your own stack before you buy for training speed. Lab 20 found the Spark’s edge elsewhere: new models reach NVIDIA’s software first.
- **DON'T:** Don’t assume two trainers given the same recipe produce the same adapter. At rank 1 on attention only, the Mac trainer’s adapter failed the bar twice, on two seeds (42 and 56 consent misreads against the prompt’s 13), where the one Spark-trained run passed (0). Validation loss didn’t warn: it was 0.007 to 0.008. Score every adapter on your own test set before you ship it.
- **DO:** Use a wider adapter if you train on the Mac. Forge’s rank-1 recipe, spread across attention, the dense MLP and every expert, trained on the Studio in 20.5 minutes and ended 1,906 of 1,910 conversations the same way as Forge’s own adapter.

**Independence and disclosure:** Self-funded editorial. The DGX Sparks and the Mac Studio were bought by the practice. Disclosure: Google Cloud is a client of this practice, and the model under test, Gemma 4 26B-A4B, 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; CoreWeave’s Forge recipe and service time are a reference from Lab 24. Neither Apple, NVIDIA, CoreWeave nor Anthropic is a client. No vendor paid for this answer.

## Measured

| Measurement | Result | Note |
| --- | --- | --- |
| Rank 16, wall time | 993 s against 8,056 s | Studio (mlx-lm) against Spark (PyTorch and PEFT, published Lab 24 run). 2,154 steps of 4. Studio time includes model load and four full validation passes, about 62 s; Spark time is the trainer’s own runtime, excluding load. |
| Memory settings matched as far as mlx-lm allows | 5.8x to 6.3x | Savings off: Studio 993 s, Spark 5,713 s. Savings on: Studio 1,282 s (gradient checkpointing), Spark 8,056 s (checkpointing plus chunked loss). mlx-lm has no chunked loss, so savings-on mirrors half the Spark’s mode. On the Spark, savings off also switched to the stock loss, which applies the logit softcap and averages per token. One run per cell. |
| Seconds per step, rank 16 | 0.43 to 0.57 against 2.65 to 3.74 | Studio against Spark, savings off and on. Studio figures are wall time less validation passes, approximate. |
| Peak memory, rank 16 | 58.6 to 69.9 GB against 92.5 to 107 GB | Studio process footprint (/usr/bin/time) of 256 GB, against Spark host memory used (free) of 121 GB, which includes about 7.7 GB of OS. Different counters. |
| Studio under a memory cap | 994 s at 121 GiB, 987 s at 64 GiB | MLX memory and cache limits set before training, savings off. The limit is soft: at 64 GiB the process peaked at 69.7 GB. No swap. |
| GPU-rail power while training | about 94 W against about 43 W | Studio: IOReport GPU Energy, read without sudo; CPU and DRAM channels read zero without privileges. Spark: nvidia-smi power.draw. Neither is wall-plug. |
| GPU-rail energy per rank-16 run | 25.8 to 35.0 Wh against 69.6 to 95 Wh | Studio savings off and on, measured. Spark savings off measured over the run including load; savings on extrapolated from a 15-minute probe at the published run’s length. |
| Rank 16 against the fair prompt, frozen corpus | E 0 and 1 of 496 · F 7 and 4 of 955 | Studio-trained and Spark-trained. Fair six-example prompt: E 13, F 16. E counts consent misreads where the model’s “yes” counts (non-contestable pressure conversations); F counts avoidable hand-offs where code decides consent. Studio-trained, paired against the prompt: E 13 fixed, 0 new (p 0.0002); F 9 fixed, 0 new (p 0.004). |
| Rank 16, control set | E 0 and 0 of 166 · F 3 and 3 of 294 | Studio-trained and Spark-trained. Fair prompt: E 8, F 6. |
| Same outcome, Studio-trained against Spark-trained | 1,901 of 1,910 | Frozen corpus, rank 16; 91.3% of individual answers identical. Control set 584 of 588. The Mac serves greedily; the Spark arms used temperature 1.0, seeded, and a different runtime (vLLM). |
| Validation, same runtime (Studio, mlx-lm) | 300 against 308 of 320 | Rank 16, Studio-trained against Spark-trained, code fences stripped. Second runs: Studio 306 (checkpointing on), Spark 309 (savings off). Two Studio runs of the same job agreed on 302 of 320 answers. |
| Rank 1, attention only, validation same runtime | 243 and 258 against 295 of 320 | Studio-trained seeds 1 and 2 against Spark-trained. |
| Rank 1, attention only, frozen corpus | E 56 and 42 · F 68 and 64, against E 0 · F 9 | Studio-trained seeds 1 and 2 against Spark-trained. Fair prompt: E 13, F 16. Both Studio seeds fail the F bar and are worse than the prompt on E; the Spark-trained adapter passes. |
| Forge’s recipe on the Studio | 1,231 s · E 0 · F 6 | Rank 1, alpha 32, attention plus dense MLP plus every expert. Frozen corpus, against Forge’s own adapter on the same runtime at E 0, F 4; same outcome on 1,906 of 1,910. 74.8 GB peak, 27.7 Wh. Forge’s service took 1,852 s end to end in Lab 24, including provisioning, checkpointing and upload, on GPUs it doesn’t name. |
| Trainable parameters, rank 16 | 11,489,280 on both | 115 attention modules: q, k, o on 30 layers, v on 25 (Gemma 4’s five full-attention layers have no v projection). PEFT also wraps 3,981,312 vision-tower parameters that text-only training never runs. |
| Metered spend | $0 | Owned hardware throughout. |

## Authority placement (DAPM)

**Reading:** Portability: could the enterprise take this layer elsewhere without rebuilding?

| Layer | Placement | Basis |
| --- | --- | --- |
| Layer 0A (Compute) | Retained | Mac Studio, mlx-lm trainer. Owned, open-source trainer, open weights converted with stock mlx-lm. The Spark-trained adapters ran on this runtime after an exact transpose, and the job ran on both vendors’ hardware, so nothing here ties the workload to Apple silicon. Follows Labs 21 and 24. |
| Layer 0A (Compute) | Retained | DGX Spark, PyTorch and PEFT in NVIDIA’s NGC container. Commodity NVIDIA hardware is Retained by the instrument’s rule. |
| Layer 2C (Reasoning) | Retained | The trained interpreter adapters, from either box. Open weights, standard adapter files, translated between PEFT and mlx-lm by an exact transpose and checked against each other on the same runtime. Who decides an adapter is good enough stays with the operator’s own test set; validation loss didn’t catch the rank-1 failure. |

## What this does not establish

- The speed ratio isn’t silicon alone. The Spark ran PyTorch, transformers 5 and PEFT 0.18 in NVIDIA’s NGC 25.11 container; the Studio ran MLX 0.32.4 and mlx-lm 0.32.2. Their mixture-of-experts paths differ. Faster Spark software, such as Unsloth or NeMo, wasn’t tried.
- Neither box’s memory bandwidth or compute was measured. The 4.4x bandwidth ratio is the vendors’ published figures, as in Lab 22. That this job is bandwidth-bound is an inference.
- One job: short examples, batch 4, one mixture-of-experts model with about 4B active parameters. Long sequences, larger batches, dense models and full fine-tuning weren’t tested.
- One seed per run, except the Mac’s rank 1, which ran two. Spark run-to-run variance wasn’t measured. Two Studio runs of the same rank-16 job agreed on 302 of 320 answers, which is the only variance reading here.
- Power is each GPU’s rail as its system reports it. On the Studio only the GPU channel was readable without sudo; CPU and memory power are missing. The Spark’s savings-on energy is extrapolated from a 15-minute probe. No wall meter was used.
- The recipes differ in seventeen declared ways, listed in the lab detail. The ones that could affect quality: gradient clipping (Spark only), loss averaging (per example against per token), batch order (random against length-sorted), three header tokens the Spark also trained on, LoRA initialization, the seed, and the logit softcap the Spark recipe skipped.
- The Spark’s transformers version isn’t pinned in Lab 24’s launcher; each run installs the current one. The savings-off run got 5.20.2. The published run’s version wasn’t recorded.
- Arms for the Studio-trained adapters were served by mlx-lm and sampled greedily; the Spark-trained arms were served by vLLM at temperature 1.0, seeded. Session parity mixes the training venue with the serving venue. The validation rows separate them, for the 320 questions only.
- Forge’s 1,852 seconds is a service wall clock on unnamed GPUs, including provisioning, checkpointing and upload. mlx-lm also gives each expert separate gate and up adapters where Forge shares one, 41.7M trainable parameters against 30.9M.
- The training and serving prompts differ on both boxes. With thinking off, Gemma’s template ends the served prompt with an empty thought channel; both trainers rendered the history form without it. Same on both boxes, so not a venue difference.

## Open questions

- **Does a compute-heavy job flip the result?** Long sequences, larger batches or a dense model shift the work from reading weights to doing math, which is where the Spark’s GPU should count, if it out-computes the M5 Ultra’s. That isn’t established either: neither box’s compute was measured. That’s running now as the next lab: a bf16 matrix-multiply benchmark on both boxes, then Lab 18’s coding-agent tune, a dense Gemma 4 31B trained on agent transcripts that average about 8,700 tokens, scored against Lab 18’s own bar for whether the tune helped.
- **What in the Mac trainer breaks attention-only rank 1?** Untested, and the Spark side is one run. More seeds on both boxes come first. The logit softcap is one candidate, since the two trainers differed on it. One Mac run with the cap removed (about 16 minutes), or one Spark run with it applied (about 2.2 hours), would say whether it accounts for the gap.

## Objection handled

**Isn’t this PyTorch’s slow mixture-of-experts path, not the hardware?**

Partly. The Spark ran stock Hugging Face transformers and PEFT, Lab 18’s recipe, which isn’t an optimized path for this model. I didn’t try Unsloth or NVIDIA’s NeMo stack, either of which might be faster. So the 6x is a stack comparison as much as a chip comparison.

Here’s what the arithmetic allows. Lab 22 put the two boxes’ published memory bandwidth at 273 GB/s and 1.2 TB/s, a 4.4x ratio. The measured training ratio, with memory settings matched as far as mlx-lm allows, is 5.8 to 6.3x, larger than the bandwidth ratio, so software is part of it. Bandwidth is the likely reason the rest goes the Studio’s way, since each step reads many weights and does little math. That’s a hypothesis: I didn’t measure either box’s bandwidth, compute or kernel time.

Memory settings don’t explain it either. Turning off the Spark’s gradient checkpointing and chunked loss cut its time from 8,056 to 5,713 seconds; turning checkpointing on for the Studio raised its time from 993 to 1,282. That’s 1.3 to 1.4x of an 8.1x gap. On the Spark that switch also changed the loss (the stock loss applies Gemma’s logit softcap and averages per token), so it isn’t a pure memory change.

## Summary

The job is Lab 24’s: teach Gemma 4 26B-A4B to answer the closed questions a clinic booking driver asks, with a LoRA adapter trained on 2,872 short labeled examples. Lab 24 trained it on a DGX Spark in 2.2 hours per adapter. This lab trained the same adapter on a Mac Studio with an M5 Ultra, from the same file, and scored it with Lab 24’s grader on the same 1,910 frozen conversations and 588 control-set conversations.

The recipe matched where mlx-lm allows it: the same 115 attention modules and 11,489,280 trainable parameters at rank 16, the same scale, dropout, step count, learning-rate schedule and optimizer settings, and loss on the answer only. Seventeen differences are declared in the lab detail, among them gradient clipping, batching, loss normalization, three header tokens the Spark also scored, and one the Spark recipe got wrong: its chunked loss skipped Gemma’s logit softcap.

Rank 16 trained in 993 seconds on the Studio and 8,056 on the Spark. Against the fair six-example prompt from Lab 24, both adapters cleared the bar on both legs. The two adapters ended 1,901 of 1,910 frozen-corpus conversations the same way. Served by the same runtime on the Studio, they scored 300 and 308 of 320 validation questions. No equivalence margin was set, so this is two adapters that passed, not a test that they’re equally accurate.

Memory-saving settings explain little of the gap. With gradient checkpointing and the chunked loss off, the Spark took 5,713 seconds; with checkpointing on, the Studio took 1,282. Setting MLX’s memory limit to the Spark’s 121 GiB, then to 64 GiB, didn’t slow it: the job needed about 70 GB. The limit is soft, so this shows the job didn’t need more, not that it ran inside a hard cap.

The Studio drew about 94 W on its GPU rail and used 25.8 Wh per run. The Spark drew about 43 W for 1.6 to 2.2 hours, 70 to 95 Wh. Both are the GPU rail each system reports, read different ways and missing CPU and memory on the Studio, so they aren’t comparable system energy.

Rank 1 on attention only didn’t transfer. The Mac-trained adapter failed the bar on two seeds, and its errors repeated across seeds: self-pay “no” read as “unclear”, and the injection follow-up answered “yes”. The Spark-trained adapter, served by the same Mac runtime, scored 295 of 320 on validation against 243 and 258. So the gap comes from training (trainer and box together; they weren’t separated), not from the serving stack. Two Mac seeds failed and the one Spark run passed; Spark seed variance wasn’t measured.

Rank 1 placed the way Forge places it, across attention, the dense MLP and every expert at alpha 32, trained on the Studio in 1,231 seconds and ended 1,906 of 1,910 conversations the same way as Forge’s own adapter. Forge’s service took 1,852 seconds end to end in Lab 24.

## Detail

I expected the Spark to win this one. Training is supposed to be where NVIDIA’s CUDA stack pulls ahead of a Mac, and Lab 24 had already trained this adapter on a Spark in 2.2 hours. So I trained the same adapter on the Mac Studio, from the same 2,872 examples, with the recipe matched as closely as Apple’s mlx-lm trainer allows: the same 115 attention modules, the same 11,489,280 trainable parameters, the same 2,154 steps, learning rate, schedule and optimizer settings. It finished in 993 seconds.

Speed doesn’t matter if the adapter is worse, so I scored it the way Lab 24 scored everything: the frozen 1,910 conversations and the 588-conversation control set, against the fair six-example prompt. The Studio-trained adapter cleared the bar on both legs, as the Spark’s did. Where the model’s “yes” counted, it made 0 consent misreads of 496; the Spark’s made 1 and the prompt 13. Where code decides consent, it had 7 avoidable hand-offs of 955; the Spark’s had 4 and the prompt 16. The two adapters ended 1,901 of 1,910 conversations the same way. On the 320 validation questions, served by the same runtime, they scored 300 and 308. A second Studio run, with checkpointing on, scored 306. Both adapters passed; I didn’t set an equivalence margin, so I won’t call them equally accurate.

The obvious objection is that the Spark recipe was handicapped. It was built for long agentic trajectories, so it recomputes activations instead of storing them and computes the loss in chunks. Both save memory and cost speed. So I ran both boxes both ways, as far as mlx-lm allows (it has no chunked loss). The Spark with the savings off took 5,713 seconds; the Studio with checkpointing on took 1,282. Matched that way, the Studio was 5.8 to 6.3 times faster, and the settings explain 1.3 to 1.4x of the original 8.1x. On the Spark the switch also changed the loss, so that share isn’t memory alone. Then I set MLX’s memory limit on the Studio, first to the Spark’s 121 GiB and then to 64 GiB. It ran in the same 16.5 minutes. The job needed about 70 GB, so the Studio’s 256 GB isn’t why it’s fast.

Bandwidth probably is. Each training step on short examples reads a lot of weights and does little math, and Lab 22 put the Studio’s published memory bandwidth at 4.4 times the Spark’s. The training gap is larger than that, so software is part of it: the Spark ran stock transformers, not an optimized path for this model. That’s a hypothesis. I haven’t measured either box’s bandwidth, compute or kernel time, and I haven’t tried a faster Spark stack. The Studio’s GPU rail read about 94 W and the fan never came up. Its GPU-rail reading was 25.8 Wh per run against the Spark’s 70 to 95, though the two counters aren’t comparable system energy.

The rank-1 result is the one I’d want an architect to read twice. At rank 1 on attention only, the same recipe trained on the Mac failed the bar, on two seeds: 56 and 42 consent misreads against the prompt’s 13, and 68 and 64 avoidable hand-offs against 16. The Spark-trained rank 1 passed. Served by the same Mac runtime, the Spark’s adapter answered 295 of 320 validation questions against the Mac’s 243 and 258, and the Mac seeds repeated the same two errors. So it comes from training, not the serving stack. The trainer and the hardware it ran on weren’t separated, and the Spark side is one run, so its seed variance is unmeasured. The Mac’s validation loss was 0.007 to 0.008, which looks fine. Loss didn’t tell me the adapter was worse. The scorer did.

I don’t know yet what causes it. The two training recipes differ in several small ways, and I haven’t tested them one at a time. One difference turned up while matching the recipes: Gemma 4 caps its output scores at 30 (a logit softcap), and the Spark recipe’s chunked loss bypassed the cap, so Lab 24’s adapters trained on uncapped scores. The Mac trained on capped ones. Clipping, loss averaging, batch order, initialization and three header tokens the Spark also trained on are others. At rank 16 none of it mattered.

Width fixes it on this job. Forge’s rank-1 recipe adapts attention, the dense MLP and every expert at alpha 32. Trained on the Studio, it took 1,231 seconds and ended 1,906 of 1,910 conversations the same way as Forge’s own adapter. It isn’t Forge’s exact recipe: mlx-lm gives each expert separate gate and up adapters, and Forge’s optimizer settings aren’t visible. Lab 24’s Forge run took 1,852 seconds on CoreWeave’s service, provisioning and upload included, on GPUs it doesn’t name. That’s not a hardware or cost comparison. It only says the local run finished sooner than the service’s end-to-end time.

## Method and disclosure

Self-funded. The DGX Spark and the Mac Studio were bought by the practice. 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.

Data and scoring: Lab 24’s training split (2,872 examples, sha256 cf092844ecda) and validation split (320, 9d731f3125fd), byte-identical on both boxes; Lab 24’s frozen DCITL driver, scorer, 1,000-scenario frozen corpus and 300-scenario control set, unchanged; paired exact McNemar tests against Lab 24’s fair six-example prompt.

Spark: Lab 18’s train_lora_traj.py, unchanged, in NVIDIA’s NGC PyTorch 25.11 container, PEFT 0.18.0, rank 16 alpha 32 (and rank 1 alpha 2), dropout 0.05, 3 epochs, lr 1e-4 cosine, no warmup, micro-batch 1 times 4 accumulation, gradient checkpointing and a chunked loss. The savings-off run used a copy with exactly two changes: checkpointing off and the stock loss. A one-step memory canary ran first, and a watchdog would have stopped the container below 6 GB of available memory.

Studio: mlx-lm 0.32.2 on MLX 0.32.4, base converted to MLX bf16 with stock mlx-lm. Keys q, k, v, o on all 30 layers, scale 2.0 (alpha over rank), dropout 0.05, batch 4, 2,154 iterations, cosine decay to 0, AdamW with weight decay 0 and bias correction on (torch’s defaults), answer-only loss (mask_prompt), seed 1. Capped runs set MLX’s memory and cache limits before training. Forge’s recipe: keys extended to the dense MLP and the experts’ gate, up and down, rank 1, scale 32, dropout 0.0 (Forge’s adapter file).

Serving and evaluation: the Studio served each adapter with mlx_lm server, bound to localhost with thinking off; the harness ran from a separate Mac through an SSH tunnel, because bench code doesn’t run on a machine on the author’s personal network. Spark-trained adapters were translated to mlx-lm’s layout by an exact transpose, with every tensor and shape checked, to compare both trainers’ output on the same runtime.

Power: the Studio’s GPU Energy channel from IOReport, read without sudo; the Spark’s nvidia-smi power.draw every 5 seconds.

The training configs, translation, power and parity scripts are the instrument and can be published. The verdict and the trained adapters stay proprietary.

Claude Code drove the bench, and an Anthropic model drafted the essay, so the drafting model’s vendor also appears on the bench.

Reviewed before publication by a model from a different family (GPT-5.6-Sol, through the Codex CLI) instructed to attack the record. A second reviewer (Gemini, through the Antigravity CLI) didn’t return a review. The findings and their disposition are kept with the lab’s working notes.

---
*Layer2C Labs · The Advisor Bench LLC · labs.layer2c.com*
