Zurück zur Startseite

FLUX.2-dev GGUF Q4_K_M: 29 GB VRAM auf Apple Silicon

Analyse des Speicherverbrauchs durch FLUX.2-dev GGUF Q4_K_M-Modell auf Apple M3 Pro ergab Peak von 28,92 GB statt erwarteter 19 GB. Hauptposten — MPS-Overhead und Gewichts-Dequantisierung. Vorgeschlagene Optimierungen mit inkrementellem Transfer und empty_cache, Reduzierung des Peaks um 1,5 GB.

29 GB VRAM in FLUX.2-dev GGUF: Aufschlüsselung und Optimierungen auf M3 Pro
Advertisement 728x90

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.

Google AdInline article slot

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 |

|-------|----------------|--------------|--------------|

Google AdInline article slot

| Basis | 0,27 GB | 0 | 0 |

| MLX-Encoder geladen | 13,22 GB | 0 | 0 |

| GGUF auf CPU | 19,23 GB | 0 | 0 |

Google AdInline article slot

| 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 von torch.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

Advertisement 728x90

Weiterlesen