{"slug": "running-qwen3-through-the-executorch-mlx-delegate-up-to-4-52x-faster-on-m1-max", "title": "Running Qwen3 Through the ExecuTorch MLX Delegate: Up to 4.52x Faster on M1 Max", "summary": "ExecuTorch's experimental MLX delegate, released in May 2026, enables PyTorch models to run on Apple Silicon GPUs. Testing Qwen3-0.6B on an M1 Max, MLX INT4 achieved 188.9 tokens/s, 4.52x faster than PyTorch MPS BF16 (41.8 tokens/s), with a 71.8% smaller file size. However, INT4 quantization changed the generated output in two of three simple prompts.", "body_md": "Hello, everyone.\n\nThere are now many ways to run an LLM on a Mac, but exporting a PyTorch model for Apple Silicon and executing it in a lightweight runtime is still an evolving path. How much faster is it, and does 4-bit quantization change the output?\n\nToday, I am looking at ExecuTorch's experimental MLX delegate, released in May 2026. It enables PyTorch models to run on Apple Silicon GPUs. I use ExecuTorch 1.3.1 to run Qwen3-0.6B and compare it with PyTorch MPS.\n\nThe short result is that decode throughput was **41.8 tokens/s** with PyTorch MPS BF16, **134.8 tokens/s** with MLX BF16, and **188.9 tokens/s** with MLX INT4. MLX INT4 was 4.52x faster, and its file was 71.8% smaller than BF16. However, INT4 changed the generated output in two of three simple prompts.\n\nExecuTorch is a runtime for running trained PyTorch models on desktops, phones, and embedded devices. Inference means feeding input into a trained model to obtain an output.\n\nThe [MLX delegate](https://pytorch.org/blog/running-pytorch-models-on-apple-silicon-gpus-with-the-executorch-mlx-delegate/) was released on **May 18, 2026**. It sends a PyTorch computation graph to Apple's MLX framework for execution on an Apple Silicon GPU. It supports BF16, FP16, FP32, and 2/4/8-bit quantization, among other formats. It is currently experimental, so its APIs and supported scope may change.\n\nQuantization stores weights with fewer bits to reduce size and computation. I compared ordinary BF16 with INT4, which quantizes the linear layers and embeddings to four bits.\n\n[Qwen3](https://qwenlm.github.io/blog/qwen3/) is an LLM family announced by the Qwen Team on **April 29, 2025**. A single model can switch between a thinking mode for step-by-step reasoning and a non-thinking mode for shorter answers. The family supports more than 100 languages and dialects.\n\nThe [Qwen3-0.6B](https://huggingface.co/Qwen/Qwen3-0.6B) used here is one of the smaller dense models in the family. “Dense” means that it generally uses the full model for each input, unlike a mixture-of-experts model that activates only selected parts. It has a nominal 0.6 billion parameters, 28 layers, and a context length of 32,768 tokens. A token is a small unit of text processed by the model.\n\nI disabled thinking mode for this test and pinned the model revision to `c1899de289a04d12100db370d81485cdf75e47ca`\n\n.\n\n| Component | License |\n|---|---|\n| Qwen3-0.6B weights |\n|\n\nThese permissive open-source licenses allow broad use, including commercial use, but their conditions—such as retaining copyright notices when redistributing—still apply. Check the linked license text for your use case.\n\nOnly one trained model, Qwen3-0.6B, was used. The same weights were tested through three execution paths.\n\n``` php\nsame prompt\n  -> tokenizer: convert text to token IDs\n  -> Qwen3-0.6B\n       ├─ ExecuTorch MLX BF16 -> .pte -> MLX / Metal GPU\n       ├─ ExecuTorch MLX INT4 -> 4-bit .pte -> MLX / Metal GPU\n       └─ PyTorch MPS BF16 ----------------> MPS / Metal GPU\n  -> greedy generation: select the most likely next token each time\n  -> tokenizer: convert token IDs back to text\n```\n\nA `.pte`\n\nfile is a model program exported for ExecuTorch. The complete computation graph could be lowered into a single MLX subgraph. PyTorch MPS BF16 ran the same BF16 weights through the ordinary Transformers/PyTorch path and served as the reference.\n\nThe complete code and JSON report are available in the [executorch-mlx-qwen3 lab in kiarina/labs](https://github.com/kiarina/labs/tree/main/2026/07/22/executorch-mlx-qwen3).\n\nYou need an Apple Silicon Mac, Xcode Command Line Tools, `mise`\n\n, `uv`\n\n, and an internet connection. The first run downloads the Qwen3 weights and creates about 1.53 GB of PTE files in total.\n\n```\ngit clone --depth 1 --filter=blob:none --sparse \\\n  https://github.com/kiarina/labs.git\ncd labs\ngit sparse-checkout set .gitignore .mise/tasks Makefile mise.toml \\\n  2026/07/22/executorch-mlx-qwen3\nmise -C 2026/07/22/executorch-mlx-qwen3 run\n```\n\nThe export and benchmark steps can also be run separately.\n\n```\nmise -C 2026/07/22/executorch-mlx-qwen3 run export\nmise -C 2026/07/22/executorch-mlx-qwen3 run benchmark\nmachine: MacBook Pro (Apple M1 Max, 32 GPU cores, 64 GB)\nOS: macOS 26.5.2\nPython: 3.13.7\nExecuTorch: 1.3.1\nPyTorch: 2.12.1\nTransformers: 4.56.1\nmodel: Qwen/Qwen3-0.6B\ngeneration: greedy, batch 1, up to 16 tokens\nPTE: custom MLX SDPA / KV cache, requested maximum sequence 128\n```\n\nFor performance, each backend was given the same Japanese prompt and forced to generate 16 tokens. The reported values are medians from five trials after warm-up. Prefill reads the input and produces the first token; decode generates the remaining tokens one at a time.\n\nEach backend ran in a separate process. The MLX path loaded a fresh forward method and initialized its KV cache for each trial, while the PyTorch path created a fresh cache for every trial.\n\n| PTE | Size | Relative to BF16 | Export time | SHA-256 |\n|---|---|---|---|---|\n| MLX BF16 | 1,192,264,196 bytes | 100.0% | 41.61 s | `83da47c2…bfb8c0` |\n| MLX INT4 | 335,662,976 bytes | 28.2% | 54.55 s | `0e30a054…71267` |\n\nINT4 was 856,601,220 bytes, or **71.8% smaller**, than BF16. Quantization itself takes work, so exporting INT4 took about 13 seconds longer.\n\nThe input was the Japanese prompt “Briefly explain local inference on Apple Silicon.”\n\n| Backend | Load | Median prefill | Median decode | 16-token total | Peak RSS increase |\n|---|---|---|---|---|---|\n| ExecuTorch MLX BF16 | 0.002 s | 0.020 s |\n134.8 tokens/s | 0.131 s | 1.27 GiB |\n| ExecuTorch MLX INT4 | 0.003 s | 0.028 s | 188.9 tokens/s |\n0.108 s |\n0.47 GiB |\n| PyTorch MPS BF16 | 0.726 s | 0.038 s | 41.8 tokens/s | 0.396 s | 0.14 GiB |\n\nMLX BF16 decoded **3.22x** as fast as PyTorch MPS BF16, while MLX INT4 was **4.52x** as fast. INT4 was also 1.40x faster than MLX BF16 and reduced the RSS increase by about 63%.\n\nThere are important qualifications. The MLX load figure measures only opening the PTE program, not all work needed to materialize weights for GPU use. RSS measures the process's main memory, not GPU memory itself. PyTorch reported 1.20 GiB of MPS driver-allocated memory at the end of the run. Because GPU memory was not measured on the same basis, the table does not prove that PyTorch used the least memory.\n\nOn the first invocation only, BF16 prefill took 0.434 seconds and INT4 took 1.014 seconds. Cold starts that include Metal setup and initial compilation were much slower than the warmed-up figures.\n\nI compared the generated token sequences on three short prompts.\n\n| Prompt | PyTorch MPS BF16 | MLX BF16 | MLX INT4 |\n|---|---|---|---|\n| Answer Japan's capital in one word |\n`日本の首都は、**大阪**です。` (Japan's capital is Osaka.) |\nExact token match |\n`日本の首都は、**东京**です。` (Japan's capital is Tokyo.) |\n| Answer 1+1 with one digit | `1+1=2` |\nExact token match | `1` |\nCopy `MLX` unchanged |\n`MLX` |\nExact token match | Exact token match |\n\nMLX BF16 matched PyTorch MPS BF16 token for token in all three cases. Within this narrow test, changing the execution path to MLX did not introduce a difference.\n\nINT4 matched in only one case. Quantization represents numbers more coarsely, so when candidate next tokens have similar scores, their ranking can change.\n\nThe capital answer was wrong in both BF16 and INT4. The small 0.6B model, prompt, and greedy decoding could all contribute, but three questions are not enough to identify the cause. This probe checks differences between backends; it does not certify the model's knowledge or answer quality.\n\nEven BF16 decoded 3.22x as fast as PyTorch MPS. Exporting a PyTorch model to a lightweight Mac runtime looks promising.\n\nINT4 reduced the file from about 1.19 GB to 336 MB and produced the highest decode rate. Yet it changed two of only three token sequences. It should be evaluated on a task-specific quality set before adoption.\n\nThis comparison covers an ExecuTorch MLX pipeline versus a Transformers/PyTorch MPS pipeline. Their runtimes, cache handling, and execution paths differ, so the numbers describe the complete pipelines.\n\nThe dependency metadata for `executorch==1.3.1`\n\nallowed PyTorch 2.13.0, but importing the published ExecuTorch extension failed because the `materialize_cow_storage`\n\nsymbol was missing. Pinning PyTorch to 2.12.1 made the same wheel work. The failure can be reproduced with this optional task:\n\n```\nmise -C 2026/07/22/executorch-mlx-qwen3 run probe-torch-2-13\n```\n\nThe bundled PTE inspector also failed because its included `flatc`\n\ndid not recognize the `--json`\n\noption. I verified full-graph delegation from the partitioner log emitted during export instead.\n\nLowering the entire Qwen3-0.6B graph to MLX and increasing decode throughput by more than 3x without changing the BF16 tokens was a good result. INT4 reduced the file to less than one-third of its BF16 size and reached about 189 tokens/s, which is attractive when embedding a small model on a Mac.\n\nThe output changes from 4-bit quantization appeared immediately, even in this tiny probe. Quantization is not a free speedup. Paired with a quality suite for the real task, this path could work well for local helper features or small, responsive agents.", "url": "https://wpnews.pro/news/running-qwen3-through-the-executorch-mlx-delegate-up-to-4-52x-faster-on-m1-max", "canonical_source": "https://dev.to/kiarina/running-qwen3-through-the-executorch-mlx-delegate-up-to-452x-faster-on-m1-max-4lc2", "published_at": "2026-07-22 03:57:08+00:00", "updated_at": "2026-07-22 04:04:18.730310+00:00", "lang": "en", "topics": ["machine-learning", "large-language-models", "developer-tools"], "entities": ["ExecuTorch", "MLX", "Apple Silicon", "Qwen3", "Qwen Team", "PyTorch", "M1 Max"], "alternates": {"html": "https://wpnews.pro/news/running-qwen3-through-the-executorch-mlx-delegate-up-to-4-52x-faster-on-m1-max", "markdown": "https://wpnews.pro/news/running-qwen3-through-the-executorch-mlx-delegate-up-to-4-52x-faster-on-m1-max.md", "text": "https://wpnews.pro/news/running-qwen3-through-the-executorch-mlx-delegate-up-to-4-52x-faster-on-m1-max.txt", "jsonld": "https://wpnews.pro/news/running-qwen3-through-the-executorch-mlx-delegate-up-to-4-52x-faster-on-m1-max.jsonld"}}