pytorch / pytorch/executorch

MLX encode allocates ~0.9 GB on iOS for a program that peaks at 208 MB on macOS

Open
#22,513 3 comments 0 reactions 2 assignees View on GitHub

@shoumikhin is already working on this.

Since Sep 10, 2026.

bug module: ios module: mlx triaged
Dominant language
Python
Stars
5k
Forks
1.2k
Avg merge
2d 10h
Merged PRs (30d)
581

Description

🐛 Describe the bug

The same Whisper-small MLX .pte uses four to six times more memory on an iPhone than on a Mac, on the same ExecuTorch version and the same delegate. The gap is entirely in the encode method, and it is not precision specific.

Measurements

Whisper-small (multilingual), exported with ExecuTorch 1.4.1, two methods (encode, decode). The audio input is a dynamic dim resolved to its maximum, 480000 samples, in both harnesses.

macOS (executor_runner, whole-process peak RSS, one method per process), bf16:

method peak RSS
encode 208.0 MB
decode 328.3 MB

Flat at 1, 5 and 20 executions, so nothing accumulates per run.

iPhone 16, iOS 26.6.1, Release build, process footprint sampled across each method's execute loop:

method bf16 int8
decode +0.06 MB +0.00 MB
encode +930 MB +680 MB
pipeline peak 1385 MB 1204 MB
What this rules out
  • Not a bf16 problem. int8 pays +680 MB for the same method. bf16's excess over int8 is about 250 MB, roughly its extra weight bytes (the two files differ by 195 MB).
  • Not per-execution accumulation. The peak is flat in iteration count on both platforms.
  • Not decode. It is free on device to within 0.06 MB.
  • Not the exported graph. The identical file is the one that peaks at 208 MB on macOS.

That leaves the iOS MLX runtime's allocation behaviour for this method as the only remaining difference.

Caveats

macOS numbers are whole-process peak RSS under a bare runner; device numbers are footprint deltas inside a React Native app, so the two are not measured identically. Even comparing totals rather than deltas the gap stands (208 MB against 1372 MB). macOS Metal and iOS Metal also have different buffer residency behaviour, which may be the whole answer.

Practical impact

This is what makes the model unusable at the larger sizes. The published small bf16 build was withdrawn after being jetsam-killed at 3.80 GB on this device, though I could not reproduce that specific figure here: my runs peaked at 1.2 to 1.4 GB and survived.

Versions
  • ExecuTorch 1.4.1
  • Device: iPhone 16 (iPhone17,3), iOS 26.6.1, Release build
  • Backend: MLXPartitioner with get_default_passes()
  • Model: OpenAI Whisper small, compute dtype bf16 and int8, two-method program

Supersedes #22016, which framed this as a bf16-specific leak. That framing is wrong: both precisions pay the same encode cost.

cc @shoumikhin @cbilgin @metascroy

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.