Streaming model shards through Apple Silicon unified memory in MLX
MLX Guide

Sharding big models in MLX: the no_gpu_multi(split) path

August 2026 · Black Sheep AI Research

A 16 GB Mac Mini can read, quantize, and write a 250 GB model without ever holding it in memory. That surprises people, so it tends to attract half-remembered API names. Let’s separate what MLX actually gives you from the search terms that get attached to it.

First, the honest note about “no_gpu_multi(split)”

If you searched for no_gpu_multi(split) expecting an MLX function with that signature, you won’t find one. It isn’t a symbol in MLX’s public API, and it isn’t a call in our conversion pipeline. We’re flagging that up front because the fastest way to lose a day is to grep for a function that was never real.

What the phrase gestures at is a genuine thing: placing and moving model shards through the compute path yourself instead of loading the whole model onto the device at once. On Apple Silicon that concept splits cleanly into two separate mechanisms, and conflating them is where most of the confusion lives:

The rest of this guide is about telling those two apart, because the same word “sharding” means opposite things in each.

Why unified memory changes the question

On a discrete-GPU box, you have host RAM and separate VRAM, and “sharding” usually means chopping a model across several GPUs. Apple Silicon has neither of those axes. CPU and GPU share one pool of unified memory. There is no second card to split onto, and there is no host-to-device copy to amortize. So the interesting split isn’t spatial (across devices) but temporal: how much of the model is resident at any one instant.

That reframing is the whole trick behind fitting large models on small Macs. You never need the entire model in memory simultaneously. You need whatever slice you’re operating on right now, plus room to write the result.

The path that works: streaming conversion

The default tool, mlx_lm.convert(), materializes the entire BF16 model in unified memory before re-quantizing. For a dense 70B model that’s fine on a 192 GB Mac Studio. For a trillion-parameter MoE like Kimi-K2.6 or DeepSeek-V4, the BF16 intermediate would need on the order of 2 TB of RAM, which no Mac has.

A shard-streaming converter sidesteps that entirely. Source checkpoints already arrive as a sequence of model-XXXXX-of-YYYYY.safetensors files, typically 4 to 8 GB each. The converter reads the safetensors index to learn which tensor lives in which shard, then loops over output shards, and for each tensor it reads from the source shard, applies the bit-width from the allocation manifest, quantizes, and writes it out, closing both source and output shards before moving on. Peak memory holds at one source shard plus one output shard plus working space, roughly 15 GB total. The model size never enters the calculation.

We’ve run this end to end on Kimi-K2.6 (1.0 T parameters), DeepSeek-V4 (685 B), GLM-5 (450 B), and Llama-4-Maverick (400 B plus 16 experts), all at a peak below 15 GB. On a 250 GB source model, we’ve measured under 15 GB peak resident set at every stage on a 16 GB Mac Mini. The full trillion-parameter walkthrough has the loop and the memory profile.

Two things worth knowing before you reach for it:

The cost is about 10% slower conversion than mlx_lm.convert(), from per-shard open and close overhead. On a 250 GB model that’s roughly three extra minutes. The real ceiling is disk, not memory.

The bottleneck moves to disk, and that’s a feature

Once memory stops being the constraint, disk becomes it. End-to-end conversion of a 250 GB BF16 model needs about 250 GB for the source (can live on an external SSD), ~25 GB for the quantized output, and ~10 GB of scratch, roughly 285 GB total. A 1 TB Mac Mini has room to spare. We’ve run the same pipeline off a Thunderbolt external SSD on a base 16 GB / 256 GB MacBook Air; sequential read tops out around 2.5 GB/s, so a full pass over 250 GB adds around 100 seconds of pure I/O on top of compute. The engineering rule that makes all of this hold is boring and strict: never materialize the full model. Iterate f.keys() with safetensors.safe_open, use mlx_lm.convert() rather than load-then-quantize-then-save, and the 250-GB-needs-256-GB-of-RAM assumption simply evaporates. The 16 GB Mac Mini writeup documents each stage’s measured peak.

The path that doesn’t have a free lunch

Here’s the honest boundary. Streaming solves preparation: converting, quantizing, verifying, building a release artifact. It does not solve inference of a model larger than your unified memory. During a forward pass the weights have to be addressable to the GPU, and streaming them in per layer means paying disk latency inside your token loop, which is not a viable interactive path. A 192 GB Mac Studio exists precisely for running the large model you prepared on the small one. Full-precision training and fine-tuning hit the same wall for the same reason: gradients and activations need the whole model resident.

And there’s a second, sharper wall on the training side that has nothing to do with bytes. When we trained LoRA adapters on the 128-expert MoE layers of Qwen3.5-35B-A3B, training died with:

[metal::malloc] Resource limit (499000) exceeded

That is not out-of-memory. A 192 GB Mac Pro hit it with only ~60 GB in use. Metal caps the number of buffer descriptors, distinct buffer handles, that can exist at once, and the ceiling is a hard 499,000. LoRA on a SwitchLinear layer spawns parameters for every expert, and the descriptor count scales roughly as num_layers × num_experts × rank × (forward + backward intermediates). At 128 experts across a few target layers, only rank 2 stays under the cap for a full run. Adding RAM changes nothing, because the limit is descriptor count, not bytes. The Metal buffer-limit investigation has the full rank-survival matrix and the workarounds we tried, most of which made it worse.

What to actually do

The trillion-parameter conversion loop, with the exact memory profile and the shard-sizing defaults, is in the research writeup below.

Read the full research →

Continue Reading

From our research and product team.

Quantizing a trillion-parameter MoE in 15 GB of RAM
Quantization

Quantizing a Trillion-Parameter MoE in 15 GB of RAM

A shard-streaming converter does the same job as mlx_lm.convert at a peak of about 15 GB, regardless of how large the source model is.

Metal buffer limits block LoRA scaling on MoE models
MoE Research

Metal Buffer Limits Block LoRA Scaling on MoE Models

Metal caps out at 499,000 buffer descriptors, so LoRA on 128-expert layers dies at rank 2, and adding RAM doesn’t help.

A 16 GB Mac Mini can quantize a 250 GB model
Apple Silicon Engineering

A 16 GB Mac Mini Can Quantize a 250 GB Model

A Mac Mini with 16 GB of unified memory can read, transform, and write a 250 GB model end-to-end because the bottleneck is disk, not memory.

View All Research