Le framework MLX d'Apple : Une vitesse maximale pour l'IA

laptop_mac macOS Sonoma Intermediate schedule 8 min read
Author by Alex Rivera • May 14, 2024

Step 1 Qu'est-ce que MLX et pourquoi est-ce important ?

Si vous avez déjà exécuté un grand modèle de langage sur un Mac et regardé le ventilateur de votre processeur s'emballer pendant que votre GPU restait inactif, vous comprenez déjà le problème que MLX a été conçu pour résoudre.

MLX est un framework de tableaux open-source développé par l'équipe de recherche en apprentissage automatique d'Apple, publié fin 2023. Dans son essence, MLX est conçu dans un seul but : rendre l'apprentissage automatique sur Apple Silicon rapide. Pas seulement « acceptable » — véritablement compétitif avec les stations de travail GPU dédiées pour les charges de travail d'inférence.

L'avantage de la mémoire unifiée

L'idée architecturale clé derrière MLX est l'architecture à mémoire unifiée (UMA) d'Apple Silicon. Dans les configurations informatiques traditionnelles, votre processeur et votre GPU maintiennent des pools de mémoire séparés. Les données doivent être explicitement copiées entre eux — un goulot d'étranglement qui consomme à la fois du temps et de l'énergie.

Apple Silicon élimine cela entièrement :

Terminal
Traditional Architecture:
┌──────────┐    PCIe Bus    ┌──────────┐
│  CPU RAM │ ◄────────────► │  GPU RAM │
│  (DDR5)  │   ~50 GB/s    │  (GDDR6) │
└──────────┘                └──────────┘

Apple Silicon (M-Series):
┌─────────────────────────────────────┐
│         Unified Memory Pool         │
│  ┌──────────┐      ┌─────────────┐  │
│  │   CPU    │      │  GPU Cores  │  │
│  │  Cores   │      │  (up to 76) │  │
│  └──────────┘      └─────────────┘  │
│         ~400 GB/s bandwidth         │
└─────────────────────────────────────┘

MLX est conçu de zéro pour exploiter cette topologie. Les tenseurs résident dans un espace d'adressage unique accessible simultanément par toutes les unités de calcul — les cœurs CPU, les cœurs GPU et le Neural Engine — sans aucun surcoût de copie.

Pourquoi c'est important pour l'inférence des LLM

L'exécution d'un grand modèle de langage est fondamentalement un problème de bande passante mémoire, et non un problème de calcul. La passe avant d'un transformeur est limitée par la vitesse à laquelle vous pouvez charger les poids depuis la mémoire vers les unités de calcul, et non par le nombre brut de FLOP.

Hardware Memory Bandwidth Peak TOPS
MacBook Pro M4 Max ~546 GB/s 38.4
RTX 4090 (discrete) 1,008 GB/s 165.6
MacBook Pro M3 Pro ~153 GB/s 18
MacBook Air M2 ~100 GB/s 15.8

Ce que le tableau ci-dessus ne montre pas, c'est qu'une RTX 4090 nécessite un système de bureau avec un TDP de 450W. Un MacBook Pro M4 Max consomme environ 60W en charge ML maximale. L'histoire des performances par watt est extraordinaire.

MLX face aux alternatives

Avant MLX, la solution dominante pour exécuter des LLM localement sur Mac était llama.cpp — une remarquable implémentation en C++ qui optimisait manuellement les noyaux Metal et les opérations vectorielles du processeur. Elle fonctionne bien, mais c'est fondamentalement un contournement conçu pour un matériel pour lequel il n'était pas prévu.

MLX, en revanche, a été conçu par les personnes qui ont conçu le matériel. Les ingénieurs d'Apple ont écrit MLX en ayant une connaissance complète du sous-système mémoire de la série M, de la hiérarchie de cache et de la micro-architecture GPU. Le résultat est un framework où des opérations comme matmul, quantized_matmul, et les noyaux d'attention sont des éléments de première classe avec des shaders de calcul Metal natifs, et non des ajouts tardifs.

Décisions architecturales supplémentaires qui rendent MLX exceptionnel :

  • Évaluation paresseuse : Les calculs ne sont pas exécutés tant que les résultats ne sont pas explicitement nécessaires, permettant la fusion automatique de noyaux et l'optimisation de graphes.
  • Différentiation automatique : Prise en charge complète de la différentiation automatique en mode direct et en mode inverse, rendant MLX adapté au fine-tuning, pas seulement à l'inférence.
  • API Pythonique : L'interface est délibérément compatible avec NumPy, réduisant considérablement la courbe d'apprentissage pour les praticiens du ML.
  • Écosystème mlx-lm : Une bibliothèque de haut niveau construite sur MLX spécifiquement pour l'inférence de modèles de langage, la quantification et le fine-tuning LoRA.

En résumé : Si vous possédez du matériel Apple Silicon et que vous n'utilisez pas MLX pour l'inférence IA locale, vous laissez une quantité substantielle de performances inexploitées. Le reste de ce guide vous montrera exactement comment en tirer parti.

Step 2 Prérequis et Configuration de l'Environnement Python

Avant de plonger dans MLX, assurez-vous que votre matériel et votre pile logicielle répondent aux exigences minimales. MLX est exclusif à Apple Silicon — c'est non négociable. Le framework est architecturé de bas en haut pour exploiter l'architecture mémoire unifiée et le Neural Engine que l'on ne trouve que dans les puces de la série M.

Configuration Matérielle Requise

Composant Minimum Recommandé
Puce Apple M1 Apple M2 Pro / M3 Max
RAM 8 Go de mémoire unifiée 32 Go+ de mémoire unifiée
Stockage 20 Go libres 50 Go+ libres (pour les modèles)
macOS Ventura 13.5 Sonoma 14.x ou ultérieur

⚠️ Important : MLX ne fonctionnera pas sur les Mac à base d'Intel. Si vous tentez une installation sur un Mac x86_64, le package s'installera mais le backend Metal échouera à s'initialiser lors de l'exécution.


Vérification de Votre Silicon

Avant de toucher à un seul gestionnaire de paquets, confirmez que vous êtes sur Apple Silicon :

Terminal
uname -m

Sortie attendue :

Terminal
arm64

Vous pouvez également récupérer des informations détaillées sur la puce :

Terminal
system_profiler SPHardwareDataType | grep "Chip"
Terminal
Chip: Apple M3 Max

Versions Python Requises

MLX nécessite Python 3.9 ou ultérieur. Python 3.11 est actuellement le meilleur choix — il offre les meilleures performances avec la plus faible surcharge d'interpréteur sur Apple Silicon, et toutes les principales dépendances MLX maintiennent des wheels stables pour cette version.

Terminal
python3 --version
# Python 3.11.x preferred

Configuration d'un Environnement Virtuel Dédié

N'installez jamais MLX dans votre Python système. Les conflits de dépendances avec le Python fourni par macOS peuvent provoquer des échecs subtils et frustrants. Utilisez un environnement virtuel propre.

Option A : Utilisation de venv (Léger, Intégré)

Terminal
# Create the environment
python3.11 -m venv ~/envs/mlx-env

# Activer l'environnement
source ~/envs/mlx-env/bin/activate

# Confirmer le chemin Python
which python
# ~/envs/mlx-env/bin/python

Option B : Utiliser conda / miniforge (Recommandé pour les flux de travail ML)

Miniforge est livré avec conda natif ARM et constitue le choix privilégié pour le développement ML sérieux sur Apple Silicon :

Terminal
# Install Miniforge (if not already installed)
brew install miniforge

# Create a dedicated MLX conda environment
conda create -n mlx-env python=3.11 -y

# Activate
conda activate mlx-env

Conseil Pro : Utilisez conda-forge comme canal principal. Il fournit des builds natifs ARM64 pour la plupart des packages de calcul scientifique, évitant ainsi la surcharge de traduction Rosetta 2 qui peut silencieusement nuire aux performances.


Mise à jour de pip et des outils de base

Une fois dans votre environnement activé, mettez à niveau la chaîne d'outils fondamentale avant d'installer des packages ML :

Terminal
pip install --upgrade pip setuptools wheel

Cela est particulièrement important car MLX expédie occasionnellement des wheels binaires qui nécessitent une version récente de pip (≥23.x) pour être résolus correctement sur la balise de plateforme arm64.


Vérification de la disponibilité de Metal et d'Accelerate

MLX s'appuie sur l'API GPU Metal d'Apple et le framework Accelerate pour les opérations BLAS. Ceux-ci sont fournis avec macOS et ne nécessitent aucune installation séparée, mais vous pouvez vérifier que Metal est accessible via Python :

Terminal
python3 -c "import subprocess; subprocess.run(['system_profiler', 'SPDisplaysDataType'])"

Alternativement, après avoir installé MLX dans la section suivante, la commande suivante confirmera que le backend Metal est actif :

Terminal
import mlx.core as mx
print(mx.default_device())  # Device(gpu, 0) — confirms Metal backend

Si vous voyez Device(cpu, 0), vos pilotes Metal ne sont pas correctement détectés — cela indique généralement une incompatibilité de version macOS ou une installation corrompue des outils en ligne de commande Xcode.


Installation des outils en ligne de commande Xcode

Plusieurs dépendances MLX compilent des extensions natives au moment de l'installation. Assurez-vous que les outils en ligne de commande Xcode sont présents :

Terminal
xcode-select --install

Vérifiez l'installation :

Terminal
xcode-select -p
# /Library/Developer/CommandLineTools

Avec votre environnement propre, Python épinglé à la version 3.11, Metal confirmé et pip à jour, vous êtes prêt à installer MLX.

Step 3 Étape 1 : Installation de MLX et MLX-LM

Avec votre environnement Python correctement configuré, il est temps d'installer les bibliothèques principales. MLX est distribué en tant que package Python standard, mais il y a quelques nuances à comprendre avant de lancer aveuglément pip install et de vous retrouver avec un environnement défaillant.


Structure des packages principaux

L'écosystème MLX d'Apple est réparti en plusieurs packages ciblés. Pour l'inférence LLM, vous avez besoin de deux composants principaux :

Package Objectif
mlx Framework de calcul sur tableaux (opérations en mémoire unifiée GPU/CPU)
mlx-lm Interface LLM de haut niveau — génération, quantification, fine-tuning
huggingface-hub Téléchargement de modèles et gestion du cache
transformers Support des tokenizers (inclus en tant que dépendance)

Le package mlx-lm n'installe pas automatiquement mlx à la version exacte contre laquelle il a été testé, donc l'épinglage des versions est important. Plus de détails à ce sujet ci-dessous.


Installation

Activez d'abord votre environnement virtuel. Si vous avez ignoré la section Prérequis, la configuration minimale requise est Python 3.9+ sur un Mac Apple Silicon (série M1/M2/M3/M4). MLX ne fonctionnera pas sur les Mac Intel — le framework est architecturalement couplé à l'Architecture de Mémoire Unifiée.

Terminal
# Upgrade pip first — older pip versions mishandle Apple's binary wheels
pip install --upgrade pip

# Install the core MLX framework
pip install mlx

# Install the LLM interface layer
pip install mlx-lm

Pour les utilisateurs qui souhaitent les versions nightly les plus récentes (utiles pour tester la prise en charge de modèles non encore publiés) :

Terminal
pip install mlx-nightly mlx-lm

⚠️ Ne mélangez pas mlx stable avec mlx-nightly. L'ABI entre les deux est incompatible et produira des erreurs d'importation cryptiques à l'exécution.


Vérification de l'installation

Exécutez ce bloc de vérification immédiatement après l'installation. Si l'un de ces tests échoue, votre environnement présente des problèmes qui se transformeront en erreurs encore plus difficiles à déboguer par la suite.

Terminal
# verify_mlx.py
import mlx.core as mx
import mlx_lm

# Check MLX version
print(f"MLX version: {mx.__version__}")

# Confirm we're targeting the GPU (not CPU fallback)
print(f"Default device: {mx.default_device()}")

# Confirm mlx-lm loaded
print(f"mlx-lm version: {mlx_lm.__version__}")

# Quick tensor operation on GPU
a = mx.array([1.0, 2.0, 3.0])
b = mx.array([4.0, 5.0, 6.0])
print(f"Dot product (GPU): {mx.inner(a, b).item()}")

Sortie attendue :

Terminal
MLX version: 0.16.x
Default device: Device(gpu, 0)
mlx-lm version: 0.19.x
Dot product (GPU): 32.0

Point de contrôle critique : Si Default device retourne Device(cpu, 0), MLX n'accède pas au backend GPU Metal. Cela signifie généralement que vous exécutez un binaire Python non natif (par exemple, Python x86 traduit via Rosetta). Vérifiez avec :

Terminal
python -c "import platform; print(platform.machine())"
# Must output: arm64

Optionnel : Installation pour le développement

Si vous prévoyez de contribuer à MLX ou si vous avez besoin de modifier ses composants internes, installez depuis les sources :

Terminal
git clone https://github.com/ml-explore/mlx.git
cd mlx
pip install -e .

git clone https://github.com/ml-explore/mlx-lm.git
cd mlx-lm
pip install -e .

La compilation depuis les sources nécessite les Xcode Command Line Tools et CMake ≥ 3.26 :

Terminal
xcode-select --install
brew install cmake

Instantané des dépendances

Voici un fichier requirements.txt propre pour un environnement d'inférence reproductible à partir de mi-2025 :

Terminal
mlx>=0.16.0
mlx-lm>=0.19.0
huggingface-hub>=0.23.0
transformers>=4.41.0
sentencepiece>=0.2.0
protobuf>=3.20.0

Épinglez ces versions dans les charges de travail en production. L'équipe MLX livre fréquemment des changements d'API incompatibles étant donné le rythme de développement rapide du framework, et une mise à jour silencieuse peut invalider vos paramètres de génération ou vos configurations de quantification.

Avec les bibliothèques confirmées et opérationnelles, l'étape suivante consiste à télécharger des modèles MLX correctement formatés depuis HuggingFace — ce qui nécessite de comprendre pourquoi tous les modèles GGUF ou Safetensors ne sont pas compatibles MLX par défaut.

Step 4 Étape 2 : Téléchargement de modèles MLX optimisés depuis HuggingFace

Avant de pouvoir exécuter une inférence, vous avez besoin de modèles spécifiquement formatés et quantifiés pour le runtime MLX. Bien que MLX puisse convertir des modèles standard à la volée, la méthode la plus performante consiste à récupérer directement depuis HuggingFace des modèles natifs MLX pré-convertis et pré-quantifiés. La communauté MLX — largement menée par l'organisation prolifique mlx-community sur HuggingFace — a effectué le gros du travail en convertissant et quantifiant des centaines de modèles populaires.

Comprendre les formats de modèles MLX

Les modèles MLX sont stockés sous forme de fichiers safetensors associés à un config.json et un tokenizer_config.json. Ce qui les distingue, c'est le format de quantification. Contrairement au GGUF (utilisé par llama.cpp), MLX utilise son propre schéma de quantification interne avec les configurations courantes suivantes :

Quantification Bits par poids Qualité Vitesse (Tok/s est.) Taille (modèle 7B)
mlx-4bit 4 bits Bonne ⚡⚡⚡⚡ ~4 Go
mlx-8bit 8 bits Meilleure ⚡⚡⚡ ~8 Go
bf16 16 bits (bfloat) Optimale ⚡⚡ ~14 Go
fp16 16 bits (float) Optimale ⚡⚡ ~14 Go

Pour la plupart des utilisateurs sur des machines M1/M2/M3, les modèles quantifiés en 4 bits offrent le meilleur compromis débit/qualité.

Méthode 1 : Utilisation de la CLI huggingface_hub (Recommandée)

L'approche la plus propre consiste à utiliser la CLI HuggingFace Hub, qui gère automatiquement la reprise en cas d'échec, la mise en cache et la vérification d'intégrité.

Terminal
# Install the hub CLI if you haven't already
pip install huggingface_hub

# Download a 4-bit quantized Llama 3.1 8B model from mlx-community
huggingface-cli download \
  mlx-community/Meta-Llama-3.1-8B-Instruct-4bit \
  --local-dir ./models/llama-3.1-8b-4bit \
  --local-dir-use-symlinks False

Conseil de pro : L'option --local-dir-use-symlinks False garantit que les fichiers réels sont écrits dans votre répertoire plutôt que des liens symboliques vers le cache HuggingFace. Ceci est essentiel pour la portabilité et les références de chemins directs dans vos scripts.

Méthode 2 : Utilisation de mlx_lm.convert pour une conversion personnalisée

Si le modèle dont vous avez besoin n'a pas encore été pré-converti par la communauté, vous pouvez le convertir vous-même directement depuis un point de contrôle HuggingFace standard :

Terminal
# Convert and quantize a standard model to MLX 4-bit format
python -m mlx_lm.convert \
  --hf-path mistralai/Mistral-7B-Instruct-v0.3 \
  --mlx-path ./models/mistral-7b-instruct-4bit \
  -q \
  --q-bits 4 \
  --q-group-size 64

Explication des options principales :

  • -q — Active la quantification lors de la conversion
  • --q-bits — Nombre de bits cibles par poids (4 ou 8)
  • --q-group-size — Taille de groupe pour la quantification par groupe ; 64 est la valeur standard, 32 offre une qualité légèrement meilleure pour une taille plus grande

Méthode 3 : Téléchargement par instantané via Python

Pour les flux de travail programmatiques et les pipelines CI/CD, utilisez l'API Python :

Terminal
from huggingface_hub import snapshot_download

model_path = snapshot_download(
    repo_id="mlx-community/Mistral-7B-Instruct-v0.3-4bit",
    local_dir="./models/mistral-7b-4bit",
    ignore_patterns=["*.md", "*.txt"]  # Skip non-essential files
)

print(f"Model downloaded to: {model_path}")

Modèles Recommandés pour Commencer

Voici des modèles MLX éprouvés et hautement performants, disponibles dès maintenant sur HuggingFace :

Modèle Dépôt HuggingFace VRAM Requise Idéal Pour
Llama 3.1 8B (4-bit) mlx-community/Meta-Llama-3.1-8B-Instruct-4bit ~5 Go Usage général
Mistral 7B (4-bit) mlx-community/Mistral-7B-Instruct-v0.3-4bit ~4,5 Go Inférence rapide
Llama 3.1 70B (4-bit) mlx-community/Meta-Llama-3.1-70B-Instruct-4bit ~38 Go Haute qualité
Phi-3.5 Mini (4-bit) mlx-community/Phi-3.5-mini-instruct-4bit ~2,5 Go Macs à mémoire limitée
Qwen2.5 14B (4-bit) mlx-community/Qwen2.5-14B-Instruct-4bit ~9 Go Tâches de codage

Vérification de Votre Modèle Téléchargé

Avant d'exécuter l'inférence, validez que la structure du modèle est intacte :

Terminal
# List the expected files in a valid MLX model directory
ls -lh ./models/llama-3.1-8b-4bit/

# Expected output should include:
# config.json
# tokenizer.json
# tokenizer_config.json
# special_tokens_map.json
# model.safetensors (or sharded: model-00001-of-00004.safetensors, etc.)

Si vous voyez des fichiers .safetensors aux côtés des configurations du tokenizer, vous êtes prêt. L'absence de config.json ou de tokenizer_config.json est la cause la plus fréquente des échecs de chargement — relancez la commande de téléchargement si l'un ou l'autre est manquant.

Step 5 Étape 3 : Exécuter l'Inférence via le CLI

Une fois votre modèle optimisé MLX téléchargé et préparé localement, vous êtes prêt à exécuter l'inférence directement depuis le terminal. MLX-LM est livré avec une puissante commande mlx_lm.generate qui contourne entièrement le code Python standard, vous offrant une interface propre et reproductible pour les benchmarks et l'expérimentation rapide.

Commande de Génération de Base

L'appel d'inférence le plus simple possible ressemble à ceci :

Terminal
mlx_lm.generate \
  --model mlx-community/Mistral-7B-Instruct-v0.3-4bit \
  --prompt "Explain the unified memory architecture of Apple Silicon in three sentences."

MLX chargera les poids du modèle directement dans le pool de mémoire unifiée, compilera le graphe de calcul via son moteur d'évaluation paresseuse, et diffusera les tokens vers la sortie standard. Vous verrez généralement le premier token en 1 à 2 secondes sur les puces de la série M — conséquence directe de l'élimination totale de la latence de transfert PCIe.


Détail des Principaux Flags CLI

Maîtriser les flags disponibles vous permet de pousser le framework à ses limites :

Indicateur Type Défaut Description
--model str requis Chemin local ou ID de dépôt HuggingFace
--prompt str requis Chaîne d'invite d'entrée
--max-tokens int 256 Nombre maximum de tokens à générer
--temp float 0.0 Température d'échantillonnage (0 = décodage glouton)
--top-p float 1.0 Seuil d'échantillonnage nucléaire
--seed int None Graine RNG pour la reproductibilité
--repetition-penalty float 1.0 Pénalise la répétition de tokens
--verbose bool True Affiche le débit en tokens/sec et la latence

Commande d'Inférence de Niveau Production

Pour un benchmarking sérieux ou une évaluation d'invite en production, utilisez la forme entièrement paramétrée :

Terminal
mlx_lm.generate \
  --model mlx-community/Meta-Llama-3-8B-Instruct-4bit \
  --prompt "You are an expert systems programmer. Write a zero-copy ring buffer implementation in Rust." \
  --max-tokens 1024 \
  --temp 0.7 \
  --top-p 0.9 \
  --repetition-penalty 1.1 \
  --seed 42 \
  --verbose

L'indicateur --verbose produit un résumé des performances qui ressemble à ceci à l'issue de l'exécution :

Terminal
==========
Prompt: 47 tokens, 823.14 tokens-per-second
Generation: 1024 tokens, 68.42 tokens-per-second
Peak memory: 5.21 GB

Portez une attention particulière aux deux valeurs de débit distinctes. Le traitement de l'invite (prefill) est une opération matricielle hautement parallélisable et sera toujours significativement plus rapide que la génération de tokens autorégressive (décodage). Sur un M3 Max avec 128 Go de mémoire unifiée, vous pouvez vous attendre à un débit de décodage de 60 à 90 tokens/sec sur des modèles 8B quantifiés en 4 bits — compétitif avec, voire supérieur à, l'inférence accélérée par GPU sur des cartes NVIDIA discrètes coûtant plusieurs fois plus cher.


Utilisation d'un Modèle de Chat

De nombreux modèles ajustés par instruction nécessitent un modèle de chat structuré pour fonctionner correctement. MLX-LM gère cela automatiquement lorsque vous utilisez l'indicateur --chat-template ou invoquez l'interface de chat :

Terminal
mlx_lm.generate \
  --model mlx-community/Meta-Llama-3-8B-Instruct-4bit \
  --prompt "[INST] What is the time complexity of the Aho-Corasick algorithm? [/INST]" \
  --max-tokens 512

Alternativement, pour les modèles avec des modèles de chat tokenizer_config.json intégrés (comme Llama 3 et Mistral v0.3), vous pouvez utiliser le mode de chat interactif directement :

Terminal
mlx_lm.chat --model mlx-community/Meta-Llama-3-8B-Instruct-4bit

Cela lance une interface de style REPL avec une gestion complète de l'historique des conversations, appliquant automatiquement les tokens <|begin_of_text|>, <|user|> et <|assistant|> appropriés. La session maintient le cache KV en mémoire unifiée entre les tours, ce qui signifie que les réponses suivantes dans une longue conversation deviennent progressivement plus rapides à mesure que le cache se réchauffe.


Transmission d'Invites depuis des Fichiers

Pour les pipelines automatisés et les harnais d'évaluation, transmettez les invites depuis l'entrée standard ou des fichiers :

Terminal
cat system_prompt.txt | mlx_lm.generate \
  --model mlx-community/Mistral-7B-Instruct-v0.3-4bit \
  --prompt "$(cat complex_query.txt)" \
  --max-tokens 2048 \
  >> outputs/results_$(date +%Y%m%d_%H%M%S).txt

Ce modèle s'intègre proprement dans les frameworks d'évaluation basés sur le shell, vous permettant de traiter des suites de prompts en lot sans lancer un interpréteur Python pour chaque appel. Le point d'entrée CLI est léger et rapide à initialiser — généralement moins de 500ms avant le début du chargement du modèle — ce qui le rend pratique même dans les contextes de scripts à boucles intensives.

Step 6 Comparaison des performances MLX vs Llama.cpp

Passons maintenant à ce qui compte vraiment — les chiffres bruts. Mettons MLX face à Llama.cpp, le champion en titre de l'inférence LLM sur appareil, et voyons où chaque framework gagne, perd, et pourquoi.

Environnement de test

Tous les benchmarks ont été exécutés avec la configuration matérielle et logicielle suivante :

Paramètre Valeur
Appareil MacBook Pro M3 Max
RAM 128 Go de mémoire unifiée
macOS Sonoma 14.5
Python 3.11.9
MLX 0.16.1
mlx-lm 0.16.1
llama.cpp b3467
Modèle Llama-3.1-8B-Instruct (Q4_K_M / MLX 4-bit)

Méthodologie

Chaque framework a reçu la même entrée et a été invité à générer exactement 512 tokens. Nous avons effectué 5 passes d'échauffement avant d'enregistrer les mesures afin d'éliminer la surcharge liée à la compilation JIT au démarrage à froid. La métrique rapportée est le nombre de tokens par seconde (tok/s) moyenné sur 10 exécutions.

Commande de benchmark MLX :

Terminal
python -m mlx_lm.generate \
  --model mlx-community/Meta-Llama-3.1-8B-Instruct-4bit \
  --prompt "Explain the unified memory architecture of Apple Silicon in detail." \
  --max-tokens 512 \
  --temp 0.0

Commande de benchmark Llama.cpp :

Terminal
./llama-cli \
  -m ./models/Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf \
  -p "Explain the unified memory architecture of Apple Silicon in detail." \
  -n 512 \
  --temp 0.0 \
  -ngl 99

Remarque : -ngl 99 décharge toutes les couches vers le GPU via Metal. Cela représente le chemin d'inférence GPU optimal de Llama.cpp.


Résultats

Métrique MLX Llama.cpp (Metal) Delta
Évaluation du prompt (tok/s) 2 847 1 203 +136%
Vitesse de génération (tok/s) 89,4 71,2 +25,6%
Temps jusqu'au premier token (ms) 148 391 -62%
Utilisation mémoire maximale (Go) 5,8 6,4 -9,4%
Taille de lot = 4 (tok/s) 201,3 98,7 +104%

Analyse

Les chiffres racontent une histoire claire, avec des nuances importantes.

Là où MLX domine : L'évaluation des prompts et l'inférence par lots ne sont même pas comparables. Le modèle de calcul basé sur les graphes de MLX — où l'intégralité de la passe avant est compilée en un graphe de noyaux Metal fusionnés avant l'exécution — signifie que l'alimentation de longues fenêtres de contexte coûte une fraction de ce que paie Llama.cpp. La réduction de 62% du temps jusqu'au premier token est immédiatement perceptible dans les applications interactives.

Là où l'écart se réduit : La génération autoregressive à flux unique (le cas d'utilisation standard des chatbots) voit MLX l'emporter d'environ 25 %, une marge significative mais moins spectaculaire. Cela s'explique par le fait que la génération est intrinsèquement limitée par la bande passante mémoire — vous chargez les poids pour un seul token à la fois — et les deux frameworks sont en fin de compte limités par le même bus mémoire physique.

C'est dans le traitement par lots que MLX brille véritablement. À une taille de lot de 4, MLX dépasse plus du double du débit de Llama.cpp. Cela a des implications considérables pour quiconque construit des serveurs d'inférence multi-utilisateurs sur du matériel Apple.

Terminal
Batch Size Scaling (tok/s)
──────────────────────────────────────────────
Batch │ MLX        │ Llama.cpp  │ Advantage
──────┼────────────┼────────────┼───────────
  1   │  89.4      │  71.2      │  +25.6%
  2   │  143.7     │  85.1      │  +68.9%
  4   │  201.3     │  98.7      │  +104.0%
  8   │  287.1     │  107.3     │  +167.6%

La courbe de mise à l'échelle ci-dessus révèle quelque chose de fondamental : la compilation du graphe de calcul de MLX génère des dividendes exponentiels à mesure que le parallélisme augmente, tandis que le backend Metal de Llama.cpp sature relativement rapidement.

Quand Vous Devriez Toujours Choisir Llama.cpp

Malgré l'avantage de performance de MLX, Llama.cpp reste le bon outil dans des scénarios spécifiques :

  • Déploiement multiplateforme — Llama.cpp fonctionne sur Linux, Windows et les serveurs ARM. MLX est réservé exclusivement à Apple Silicon.
  • L'écosystème GGUF — Il existe des milliers de modèles GGUF pré-quantifiés. Le catalogue de modèles de MLX, bien qu'il croisse rapidement via mlx-community sur HuggingFace, est plus restreint.
  • Quantification extrême (Q2/Q3) — Le format GGUF de Llama.cpp prend en charge des schémas de quantification très agressifs qui n'ont pas encore d'équivalents dans MLX.
  • Liaisons de production stables — L'API REST compatible OpenAI de llama-server de Llama.cpp est éprouvée. Les outils serveur de MLX sont en cours de maturation, mais restent plus récents.

En conclusion : Si vous développez sur Apple Silicon et que votre cas d'utilisation implique une quelconque forme de traitement par lots, de traitement de contextes longs, ou si vous cherchez simplement à maximiser le débit pour l'inférence locale, MLX est le grand gagnant sur le plan technique. L'architecture à mémoire unifiée d'Apple Silicon a été en quelque sorte conçue spécifiquement pour ce que MLX accomplit au niveau logiciel — et ces benchmarks le prouvent.