Skip to content
LTX Mac Farm

AI Video

CUDA Performance on Macs for AI Video: What MLX Actually Gives You

Four Macs died to bring us this information

🧠 📊 🔥

The honest comparison nobody writes: Apple Silicon versus a CUDA card for AI video, from someone who ran a farm of the former for a marketing team.

Where Macs win: model size

Unified memory means the GPU can address the whole machine. A 64GB M4 loads an LTX-2.3 pipeline that a 24GB consumer NVIDIA card simply cannot hold without offloading — and offloading is where “faster hardware” quietly becomes slower.

You also get the model staying in the building, no per-second meter, and a machine that's a laptop again at 5pm.

Where Macs lose: everything else

Raw throughput isn't close. A hero 1080×1920 clip is tens of minutes on an M4, and there's no CUDA ecosystem — you're on MLX, which means the specific model port matters more than the silicon. Plan for throughput across machines, not latency on one.

The part that actually bit us

MLX allocations are invisible to every free-memory reading macOS exposes. vm_stat, memory pressure, Activity Monitor — all of them look fine right up until the kernel kills your render with rc=137.

So a “check if there's RAM free” guard is not a guard. It's a coin flip. Four Macs discovered this simultaneously, mid-sweep, at which point the queue happily re-issued the same jobs and killed them again.

The money pattern: price the job before claiming it

The fix is to stop asking “is there memory now?” and start asking “what does this job cost, and can this Mac afford it?” — before the atomic claim:

# est_peak_gb(type, w, h, frames) -> what this shape of job costs
price="$(price_job "$claimed")"
if ! afford_why="$(mem_can_afford "$need" "$min_ram")"; then
  release_job "$claimed" "$jtype $jdim $afford_why"   # back to the queue
  continue                                            # a bigger Mac takes it
fi

Each worker budgets to 90% of its own installed RAM, injects that cap into the render process, and a released job waits for a machine that can afford it. A memory kill re-queues with a raised MIN_RAM_GB floor and a 90-second drain — because an instant retry reloads a 27GB model into a machine that hasn't recovered yet. That's the runaway loop, and the backoff is load-bearing.

Measure, don't extrapolate

Our first profile numbers were 3–5× too low, which is how the incident happened. Every finished render now writes a sidecar with its real peak:

{ "id": "hero_roofline_v2", "width": 1080, "height": 1920,
  "frames": 97, "duration_secs": 1830,
  "peak_mem_gb": 44.1, "budget_gb": 57.6, "worker": "AIDENWOOD" }

The app counts how many renders exceeded their own budget and shows it on the Farm view. That single number is what you retune the farm against — not a vendor benchmark, and not a guess from a spreadsheet.

The verdict

Macs are a bad GPU and a great farm node: big memory, low power, already paid for, and honest about nothing. Give them admission control and job-level parallelism — see why not tensor-parallel — and four of them out-ship a rented GPU you're afraid to leave running overnight. Then let them work while you sleep.

Build the farm.

Four Macs, a $20 switch and one shared folder. No CUDA, no cloud, no per-render cost.