Warum FLUX.2-dev GGUF Q4_K_M 29 GB VRAM auf Apple Silicon nutzt
Der FLUX.2-dev-Modell im quantisierten GGUF-Format Q4_K_M sollte theoretisch etwa 19 GB Speicher nutzen, erreicht aber bei Apple Silicon mit M3-Pro-Chip einen Spitzenwert von 28,92 GB. Analysen zeigen, dass die zusätzlichen Gigabyte auf MPS-Overhead, Gewichtsdequantisierung und Datenübertragungen zwischen CPU und GPU in der gemeinsamen Speicherschicht zurückzuführen sind.
Ladearchitektur und sequenzielle Auslagerung
FLUX.2-dev verwendet den Mistral-Small-3.2-24B-Textencoder, der in voller Präzision 45 GB VRAM benötigt. Für Apple Silicon wird eine 4-Bit-Version über MLX genutzt, die 16 GB verbraucht. Der Prozess folgt einem sequenziellen Auslagerungsmuster:
- Lade den MLX-Encoder (~13 GB).
- Wandle die Eingabe in Embeddings auf der CPU um.
- Lade den Encoder aus und leere den Cache.
- Lade das DiT-Modell (GGUF Q4_K_M) von der Festplatte (~19 GB).
- Initialisiere VAE und Scheduler auf der CPU.
- Übertrage die gesamte Pipeline auf MPS.
Der theoretische Höchstwert entspricht dem Maximum der einzelnen Komponentengrößen, doch die Realität unterscheidet sich aufgrund des Verhaltens der gemeinsamen Speicherschicht.
Testumgebung
- macOS: 26.3.1 (Tahoe)
- Chip: Apple M3 Pro, 36 GB gemeinsamer Speicher
- Python: 3.14.3
- PyTorch: 2.11.0
- diffusers: 0.38.0.dev
- mlx-lm: 0.31.1
Die Profilierung erfolgte mit phys_footprint (ähnlich Activity Monitor) und PyTorch-MPS-Metriken. psutil.rss berücksichtigt keine MPS-Zuweisungen.
Detaillierte Speichernutzungsaufschlüsselung
Schritt-für-Schritt-Messungen
| Phase | phys_footprint | MPS-Treiber | MPS-aktuell |
|-------|----------------|--------------|--------------|
| Basis | 0,27 GB | 0 | 0 |
| MLX-Encoder geladen | 13,22 GB | 0 | 0 |
| GGUF auf CPU | 19,23 GB | 0 | 0 |
| pipe.to(mps) | 23,79 GB | 20,06 GB | 18,75 GB |
| Mittlerer Inferenzzeitpunkt | 26,50 GB | 25,62 GB | 18,78 GB |
Der Spitzenwert von 28,92 GB entsteht während pipe.to("mps"), da gleichzeitig Kopien auf CPU und MPS vorhanden sind.
Aufbau des 28-GB-Spitzenwerts
- DiT Q4_K_M (uint8): 18,59 GB
- VAE (bf16): 0,16 GB
- MPS-Pool-Overhead: +1,31 GB
- IOKit/Metal graphCache: ~3,73 GB (Shaders, Seitentabellen, Limbo-Puffer)
GGUF-Gewichte bleiben auf MPS als uint8; die Dequantisierung zu bf16 erfolgt schichtweise während des Vorwärtsdurchlaufs. Temporäre bf16-Tensoren (bis zu 6,92 GB) sammeln sich im Cache an.
Optimierungsstrategien zur Reduzierung des Spitzenwerts
- Schrittweise MPS-Übertragung: Übertrage Parameter einzeln mit
gc.collect()alle 30 Parameter.
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()
- cache-leeren zwischen Schritten: Nutze einen Callback während der Inferenzschritte mit
torch.mps.empty_cache(), um den Dequantisierungscache zu leeren.
- Nach-Generierung-Leerung: Nach der Inferenz führe
gc.collect()gefolgt vontorch.mps.empty_cache()aus.
Ergebnisse der Optimierungen
| Kennzahl | Vorher | Nachher | Delta |
|--------|--------|---------|-------|
| Spitzen-phys_footprint | 28,92 GB | 27,36 GB | -1,56 GB |
| Stabile Zustand nach Inferenz | 26,25 GB | 21,76 GB | -4,49 GB |
| Generierungszeit (1 Schritt) | 2:04 | 2:11 | +7 Sekunden |
Die Reduktion des Spitzenwerts kommt durch inkrementelle Garbage Collection beim Laden zustande; die Verbesserung im stabilen Zustand resultiert aus dem Leeren des Caches. Nicht optimierbarer MPS-/IOKit-Overhead bleibt bei ~3,5 GB.
Wichtige Erkenntnisse:
- Minimaler stabiler Zustand: ~24 GB, Spitzeninferenz ~27 GB.
- Hauptkosten: 18,59 GB für uint8-DiT-Gewichte + 3,5 GB MPS-Overhead.
- Dequantisierung zu bf16 fügt 3–5 GB temporäre Puffer hinzu.
- CPU→MPS-Übertragung verdoppelt den Spitzenwert wegen gemeinsamer Speicherschicht.
- Optimierungen bringen -1,5 GB Spitzenwert und -4,5 GB stabile Nutzung ohne Qualitätsverlust.
Das echte Minimum für FLUX.2-dev GGUF auf M3 Pro liegt bei 24+ GB gemeinsamem Speicher. Für kleinere Konfigurationen sind aggressivere Quantisierung oder CPU-Auslagerung erforderlich.
— Editorial Team
Noch keine Kommentare.