Pourquoi FLUX.2-dev GGUF Q4_K_M consomme 29 Go de VRAM sur Apple Silicon
Le modèle FLUX.2-dev en format quantifié GGUF Q4_K_M devrait théoriquement utiliser environ 19 Go de mémoire, mais sur Apple Silicon avec un processeur M3 Pro, il atteint un pic à 28,92 Go. L'analyse révèle que ces gigaoctets supplémentaires proviennent de la surcharge MPS, de la déquantification des poids et des transferts de données entre CPU et GPU dans la mémoire unifiée.
Architecture de chargement et déchargement séquentiel
FLUX.2-dev utilise l'encodeur de texte Mistral-Small-3.2-24B, qui nécessite 45 Go de VRAM en précision pleine. Pour Apple Silicon, une version 4 bits via MLX est utilisée, consommant 16 Go. Le processus suit un schéma de déchargement séquentiel :
- Charger l'encodeur MLX (~13 Go).
- Encoder la requête en embeddings sur le CPU.
- Décharger l'encodeur et vider le cache.
- Charger le modèle DiT (GGUF Q4_K_M) depuis le disque (~19 Go).
- Initialiser le VAE et le planificateur sur le CPU.
- Transférer toute la chaîne vers MPS.
Le pic théorique correspond au maximum des tailles individuelles des composants, mais la réalité diffère en raison du comportement de la mémoire unifiée.
Environnement de test
- macOS : 26.3.1 (Tahoe)
- Processeur : Apple M3 Pro, 36 Go de mémoire unifiée
- Python : 3.14.3
- PyTorch : 2.11.0
- diffusers : 0.38.0.dev
- mlx-lm : 0.31.1
Le profilage a été effectué avec phys_footprint (similaire à Monitor d'activité) et les métriques MPS de PyTorch. psutil.rss ne prend pas en compte les allocations MPS.
Détail de la consommation mémoire
Mesures étape par étape
| Étape | phys_footprint | Pilote MPS | MPS actuel |
|-------|----------------|------------|-------------|
| Baseline | 0,27 Go | 0 | 0 |
| Encodeur MLX chargé | 13,22 Go | 0 | 0 |
| GGUF sur CPU | 19,23 Go | 0 | 0 |
| pipe.to(mps) | 23,79 Go | 20,06 Go | 18,75 Go |
| Milieu de l'inférence | 26,50 Go | 25,62 Go | 18,78 Go |
Le pic de 28,92 Go survient lors de pipe.to("mps") en raison de la présence simultanée de copies CPU et MPS des données.
Composition du pic à 28 Go
- DiT Q4_K_M (uint8) : 18,59 Go
- VAE (bf16) : 0,16 Go
- Surcharge du pool MPS : +1,31 Go
- IOKit/Metal graphCache : ~3,73 Go (shaders, tables de pages, tampons limbo)
Les poids GGUF restent en uint8 sur MPS ; la déquantification en bf16 s’effectue couche par couche pendant le passage avant. Des tenseurs bf16 temporaires (jusqu’à 6,92 Go) s’accumulent dans le cache.
Stratégies d'optimisation pour réduire le pic mémoire
- Transfert MPS incrémental : Déplacer les paramètres un par un avec
gc.collect()tous les 30 paramètres.
def _move_to_device_incremental(pipe, device):
n = 0
for attr in list(vars(pipe)):
comp = getattr(pipe, attr, None)
if not isinstance(comp, torch.nn.Module):
continue
for param in comp.parameters():
param.data = param.data.to(device)
n += 1
if n % 30 == 0:
gc.collect()
for buf in comp.buffers():
buf.data = buf.data.to(device)
gc.collect()
torch.mps.synchronize()
torch.mps.empty_cache()
- Vidage de cache entre étapes : Utiliser un rappel pendant les étapes d'inférence avec
torch.mps.empty_cache()pour vider le cache de déquantification.
- Vidage post-génération : Après l'inférence, exécuter
gc.collect()suivi detorch.mps.empty_cache().
Résultats des optimisations
| Métrique | Avant | Après | Variation |
|--------|--------|-------|-------|
| Pic phys_footprint | 28,92 Go | 27,36 Go | -1,56 Go |
| État stable après inférence | 26,25 Go | 21,76 Go | -4,49 Go |
| Temps de génération (1 étape) | 2:04 | 2:11 | +7s |
La réduction du pic provient de la collecte de déchets incrémentielle durant le chargement ; l'amélioration de l'état stable résulte du vidage du cache. La surcharge MPS/IOKit non optimisable reste à ~3,5 Go.
Points clés :
- État stable minimum : ~24 Go, pic d'inférence ~27 Go.
- Coût principal : 18,59 Go pour les poids DiT uint8 + 3,5 Go de surcharge MPS.
- La déquantification en bf16 ajoute 3–5 Go de tampons temporaires.
- Le transfert CPU→MPS double le pic en raison de la mémoire unifiée.
- Les optimisations permettent une réduction de -1,5 Go au pic et de -4,5 Go à l'état stable sans perte de qualité.
Le minimum réel pour FLUX.2-dev GGUF sur M3 Pro est de 24+ Go de mémoire unifiée. Pour des configurations plus petites, une quantification plus agressive ou un déchargement sur CPU est nécessaire.
— Editorial Team
Aucun commentaire pour le moment.