Run two models at once on 48 GB and they both fit, but the thing that breaks is the one nobody measures

We run two models at once on a 48 GB Mac. RAM was never the limit: both fit in 26.78 GB, but generating together drops each one to about 9 tokens a second.

0:00
"Run two models at once on"

The question people ask before buying a 48 GB Mac is whether they can run two models at once. It is the wrong question, and the way to find that out is to load both and watch which number moves. We put a 31B and a 27B in memory together on an M4 Pro, and the memory was so comfortable it was boring. Then we asked both to generate at the same time and the fast model lost 65 percent of its speed.

The pair was gemma-4-31B-it-QAT at Q4_0 and Bonsai-27B at Q1_0, both at 8,192 tokens of context, on a studio work/test stack with 48 GB of unified memory.

Does a second model actually fit on a 48 GB Mac?

Easily, and with less drama than the sizing threads suggest.

With nothing loaded, the machine reported 92 percent of memory free. Loading the 31B took the backend process to 21.97 GB and free memory to 57 percent. Adding the 27B alongside it brought the total resident set across both backend processes to 26.78 GB, with 48 percent still free.

The number worth dwelling on is not the total, it is the swap. Pageouts before the second model went in stood at 1,578,495. After both were resident and both had generated, they stood at 1,578,604. That is a difference of 109 pages, something like 400 kilobytes, over an experiment that moved nearly 27 GB of weights into memory. Nothing was swapped. The machine did not strain.

Residency on its own is close to free in speed terms too. The 31B generated at 12.2 tokens a second alone, and at 12.0 tokens a second with the 27B sitting loaded but idle beside it. Keeping a second model in memory, ready to answer, costs about 1.6 percent.

What happens when both models generate at the same time?

This is where it falls over, and it falls over hard.

Condition31B27B
Alone, nothing else loaded12.2 tok/snot measured
Alone, other model resident but idle12.0 tok/s26.7 tok/s
Both generating concurrently9.0 tok/s9.2 tok/s

The 31B lost a quarter of its speed. The 27B, which is the fast one, lost 65 percent. And they landed on almost exactly the same number: 9.03 and 9.21 tokens a second.

That convergence is the whole finding. If the bottleneck were memory bandwidth or cache pressure, the two models would degrade in proportion to their size and the small one would stay comfortably ahead. Instead they meet in the middle, which is what time-slicing looks like. Both are queued on one GPU, and the GPU hands out turns. The small model was never fast because it was small in memory. It was fast because it had the whole device, and now it has half.

Is it worth running two models at once?

Add up the wall-clock and the answer is mostly no.

Generating 147 tokens on the 31B and 150 on the 27B, one after the other, takes 13.46 plus 6.25 seconds, which is 19.71 seconds. Running both at once finished in 17.27 seconds. Concurrency bought 2.4 seconds, about 12 percent, and in exchange both models felt sluggish the entire time.

There is a harsher way to read the same numbers. Added together, the two models produced 18.24 tokens a second while running concurrently. The 27B on its own produced 26.7. So if the work in front of you is something the smaller model could have handled by itself, splitting it across both engines does not just feel slower, it delivers fewer tokens per second in total than using one model would have. Concurrency here is not buying throughput. It is buying the ability to have two conversations open, and charging for it.

For a batch job with no one watching, 12 percent is 12 percent and the tradeoff is fine. For anything interactive it is a bad deal, because the thing you notice is not total throughput, it is that the quick model you keep around for quick questions has stopped being quick. A 27B answering at 9 tokens a second reads like a much larger model on a much smaller machine.

The pattern that does work is the one the memory numbers already suggested: keep both loaded, use one at a time. Residency costs 1.6 percent of speed and about 5 GB of RAM, and it removes the model load from the critical path entirely, which on this machine is 3.6 to 3.9 seconds of waiting every time you switch. Paying five gigabytes to never wait for a load again is a good trade on a 48 GB machine. Paying it to run both engines at once is not.

What actually limits how many models you can keep loaded?

Memory, eventually, but later than people plan for, and the ceiling depends on what you set the context to rather than on the weights.

Two models at 8K each came to 26.78 GB. The context measurements we took on the same machine put the KV cache for this 31B at 80 KB per token, so pushing it alone to 64K adds 4.4 GB and lands near 26.3 GB for a single model. In other words one model with a long context costs about what two models with short ones cost. The context slider moves memory faster than a second model does, and it is the setting people leave at maximum without thinking.

So the sizing rule that comes out of this is not about parameter counts. Budget the weights, then budget the context separately at whatever your model’s architecture charges per token, then assume you will run one engine at a time regardless of how many are resident. The GPU is the scarce thing on this machine, not the RAM, and it is the only one of the two that no model card and no interface will tell you about. That is the same gap we found in prompt processing, where the number everyone quotes turned out to be measuring something else entirely.

Two limits on what we measured, since both would change the picture. We tested one pair, a 31B against a 27B, and a pair with two similarly sized models would likely converge in the same place but from a shorter distance. We also tested only the llama.cpp Metal backend through LM Studio, and MLX schedules work on the GPU differently enough that the contention numbers deserve their own run rather than an assumption. What is not in doubt is the direction: memory had headroom the whole time, and the device did not.

If you want two models genuinely working at once, the answer on Apple silicon is not a bigger machine. It is a second machine.

Share this