Logo VRAMfarm VRAMfarm
IT
Registrati

11 novembre 2026 · Team VRAMfarm

CUDA out of memory: come risolverlo in ComfyUI, con gli LLM e nel training

Un terminale con un errore CUDA out of memory accanto alla barra della memoria di nvidia-smi su una GPU a noleggio
torch.OutOfMemoryError: CUDA out of memory. Tried to allocate 2.00 GiB.
GPU 0 has a total capacity of 23.65 GiB of which 1.12 GiB is free.
Of the allocated memory 20.10 GiB is allocated by PyTorch, and 1.31 GiB
is reserved by PyTorch but unallocated.

Questo messaggio dice una cosa sola: in quel momento il programma ha chiesto 2 GiB di memoria video in più di quelli rimasti. La scheda sta bene, e non sempre è troppo piccola. Nella maggior parte dei casi si risolve in pochi minuti con una di quattro leve: una precisione più bassa, un’unità di lavoro più piccola (batch, risoluzione, contesto), liberare la memoria occupata da qualcos’altro o rimediare alla frammentazione. Quando nessuna funziona senza rovinare qualità o velocità, la risposta più economica è una scheda più grande.

Leggi l’errore prima di cambiare qualsiasi cosa

Ogni parte del messaggio ti dice qualcosa:

  • “Tried to allocate 2.00 GiB” è la singola richiesta che è fallita, non il totale che serve al tuo lavoro. Abbassare un po’ le impostazioni può solo spostare il crash qualche step più in là.
  • “Total capacity of 23.65 GiB” su una scheda da 24 GB è normale. Il driver tiene per sé una piccola parte della memoria, e il contesto CUDA si prende qualche centinaio di megabyte in più prima che il modello si carichi.
  • “Reserved by PyTorch but unallocated” è memoria che PyTorch tiene ma non può usare per questa richiesta. Se arriva a qualche gigabyte, il problema è la frammentazione; vedi più sotto.
  • Le righe “Process 1234 has 9.80 GiB memory in use”, quando ci sono, indicano che un altro processo condivide la scheda.

RuntimeError: CUDA error: out of memory senza dettagli di solito arriva da una libreria come cuBLAS o da un kernel personalizzato, spesso durante l’avvio. Le soluzioni sono le stesse.

Controlla con nvidia-smi chi occupa la memoria

Lancia nvidia-smi nel terminale di Jupyter o via SSH. La riga che conta è quella di ogni GPU:

| 0  NVIDIA GeForce RTX 4090  ...  | 21540MiB / 24564MiB |  97%  Default |

Il primo valore è la memoria in uso, il secondo il totale della scheda, poi il carico della GPU. Per seguirlo in tempo reale mentre il lavoro gira:

nvidia-smi --query-gpu=memory.used,memory.total,utilization.gpu --format=csv -l 1

Su un server a noleggio l’elenco dei processi in fondo è spesso vuoto, anche quando la memoria è occupata. Il server è un container, e nvidia-smi non vede i nomi dei processi oltre quel confine. Usa invece ps aux | grep python.

Tre cose occupano spesso memoria che non ti aspetti:

  1. La cache di PyTorch. I tensori liberati restano riservati per essere riutilizzati, quindi nvidia-smi li mostra come occupati. torch.cuda.memory_allocated() mostra cosa è davvero in uso, torch.cuda.memory_reserved() cosa è trattenuto. torch.cuda.empty_cache() restituisce la cache, e serve solo quando ne ha bisogno un altro processo.
  2. Un altro processo sulla stessa scheda. Un kernel Jupyter che ha caricato un modello tre celle fa, un vecchio ComfyUI ancora acceso, uno script di training andato in crash a metà. Riavvia il kernel o fai kill del PID. Se il tuo script muore ogni volta che parte un’altra app sulla scheda, il motivo è questo: si contendono la stessa memoria.
  3. La riserva di vLLM. Di default vLLM si prende il 90% della scheda all’avvio. Qualsiasi altra cosa avvii accanto finirà la memoria.

ComfyUI, Flux e Wan: le soluzioni in ordine

Scendi lungo la lista e fermati quando funziona:

  1. Usa pesi FP8 o GGUF. Flux.1 dev a 16 bit sono circa 24 GB solo di pesi; in FP8 circa 12 GB. Il suo text encoder T5-XXL scende da circa 9,5 GB a circa 5 GB in FP8. Le versioni GGUF (con i nodi ComfyUI-GGUF) vanno oltre: Q8 va bene per le schede da 16 GB, Q5 e Q4 stanno in 12 GB, e a Q4 i dettagli fini iniziano a soffrire. Il nostro template Flux include già Flux.1 schnell in FP8.
  2. Sostituisci VAE Decode con VAE Decode (Tiled). La decodifica finale è un classico punto di crash, soprattutto con immagini grandi e video. La decodifica a tile lavora a pezzi, al costo di qualche secondo.
  3. Abbassa il batch size in Empty Latent Image. Quattro immagini una dopo l’altra chiedono circa un quarto della memoria per le attivazioni rispetto a un batch da quattro.
  4. Abbassa la risoluzione, poi fai l’upscale. La memoria per le attivazioni cresce con il numero di pixel: 2048×2048 ha quattro volte i pixel di 1024×1024. Genera alla dimensione nativa e usa un upscaler a tile.
  5. Per il video, taglia frame o risoluzione. Con Wan 2.2, passare da 81 a 49 frame o da 720p a 480p fa risparmiare diversi gigabyte. Il modello 14B è fatto di due esperti da 14B che ComfyUI carica a turno; sulle schede da 24–32 GB, il block swap dei nodi wrapper per Wan video sposta parte del modello nella RAM di sistema.
  6. L’offload come ultima risorsa. Quando avvii ComfyUI da te, --lowvram tiene parti del modello nella RAM di sistema e --reserve-vram 2 lascia 2 GB liberi per altro. Aspettati una generazione parecchie volte più lenta.

I pulsanti del Manager per scaricare i modelli e liberare la cache servono quando cambi workflow nella stessa sessione, per esempio da Flux a Wan, perché il vecchio modello può restare caricato.

LLM: pesi più KV cache

Un modello linguistico ha bisogno di memoria per tre cose: i pesi, la KV cache e 1–2 GB di overhead di lavoro.

I pesi sono parametri per byte per parametro:

Modello 16 bit FP8 / 8 bit 4 bit
8B ~16 GB ~8 GB ~5 GB
32B ~66 GB ~33 GB ~20 GB
70B ~141 GB ~71 GB ~43 GB

La KV cache cresce a ogni token del contesto, per tutte le richieste contemporanee. A 16 bit occupa circa 128 KB per token con Llama 3.1 8B, 256 KB con Qwen3 32B e 320 KB con Llama 3.x 70B. Una singola conversazione da 32.000 token su Qwen3 32B chiede circa 8 GB di cache; una da 128.000 token su un modello da 70B circa 42 GB, quanto i pesi a 4 bit. Ecco perché un modello si carica bene e poi fallisce in una chat lunga, o quando arrivano dieci utenti insieme.

Le soluzioni per strumento:

  • Ollama. Imposta un contesto più breve con num_ctx o OLLAMA_CONTEXT_LENGTH, e dimezza la cache con OLLAMA_FLASH_ATTENTION=1 e OLLAMA_KV_CACHE_TYPE=q8_0. Ollama va in crash di rado; piuttosto sposta dei layer sulla CPU. Se ollama ps mostra qualcosa come 35%/65% CPU/GPU, il modello non ci sta e gira parecchie volte più lento.
  • llama.cpp. Limita il contesto con -c 16384, scegli quanti layer restano sulla GPU con -ngl e quantizza la cache con --cache-type-k q8_0 --cache-type-v q8_0. Una cache V quantizzata richiede flash attention: le build attuali la attivano da sole dove la GPU la supporta, e -fa on la forza.
  • vLLM. --gpu-memory-utilization (default 0.9) è la quota della memoria totale della scheda che vLLM si prende all’avvio per pesi e cache. Abbassala a 0.8 se l’avvio fallisce perché qualcos’altro usa la scheda. Se vLLM dice che la max seq len del modello è più grande di quanto la KV cache possa contenere, alzala a 0.95 o imposta --max-model-len 32768. --kv-cache-dtype fp8 dimezza la cache, --max-num-seqs limita le richieste in parallelo, --enforce-eager risparmia la memoria occupata dai CUDA graph a scapito di un po’ di velocità, e --tensor-parallel-size 2 divide il modello su due schede. Il template vLLM ti dà un’API compatibile con OpenAI per provare queste impostazioni.
  • Quantizza i pesi. Un checkpoint AWQ o GPTQ a 4 bit riduce i pesi a circa un quarto rispetto ai 16 bit. L’FP8 li dimezza e gira in modo nativo sulle schede Ada, Hopper e Blackwell.

Training: riduci il batch, le attivazioni e l’ottimizzatore

Un fine-tuning completo con AdamW in mixed precision chiede circa 16 byte per parametro prima delle attivazioni: 2 per i pesi, 2 per i gradienti, 4 per una copia master a 32 bit e 8 per gli stati dell’ottimizzatore. Un modello da 7B sono circa 112 GB, più di qualsiasi singola scheda tranne H200 e B200. Le soluzioni, più o meno in ordine di quanto fanno risparmiare rispetto a quanto costano:

  1. Batch più piccolo, stesso batch effettivo. Un batch size di 2 con 8 step di gradient accumulation addestra come un batch da 16.
  2. Gradient checkpointing. Le attivazioni vengono ricalcolate durante il backward pass invece di essere memorizzate. Fa risparmiare buona parte della memoria delle attivazioni e costa un 20–30% di velocità. In Hugging Face: gradient_checkpointing=True.
  3. Mixed precision BF16 su qualsiasi scheda da Ampere in poi.
  4. Ottimizzatori a 8 bit. AdamW8bit di bitsandbytes (optim="adamw_bnb_8bit") riduce gli stati dell’ottimizzatore da 8 byte per parametro a circa 2.
  5. LoRA o QLoRA invece del fine-tuning completo. Addestri piccoli adapter; la QLoRA carica il modello base a 4 bit. Un modello da 8B si addestra con QLoRA in 10–16 GB, un 70B chiede circa 48 GB e sta comodo su 80 GB.
  6. Sequenze più corte o risoluzione più bassa. Per una LoRA Flux, addestra prima a 512–768 px e metti in cache latenti e text embedding.
  7. Più schede. FSDP o DeepSpeed ZeRO distribuiscono pesi, gradienti e stati dell’ottimizzatore su più GPU.

Se il crash arriva dopo centinaia di step e non subito, cerca un leak: total_loss += loss tiene in vita l’intero grafo, total_loss += loss.item() no. La valutazione senza torch.no_grad() fa lo stesso.

Frammentazione: PYTORCH_CUDA_ALLOC_CONF

Quando l’errore mostra gigabyte “reserved but unallocated”, la memoria c’è ma è spezzata in pezzi troppo piccoli per la richiesta. Succede soprattutto con forme che cambiano: immagini di dimensioni diverse, sequenze di lunghezza variabile o modelli caricati e scaricati a turno. Impostala prima che parta Python:

export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True

In un notebook, metti os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True" nella prima cella, prima di import torch. Le release più recenti di PyTorch leggono la stessa impostazione anche come PYTORCH_ALLOC_CONF. Sulle versioni più vecchie l’alternativa è max_split_size_mb:128. Questa impostazione non crea memoria; ti permette solo di usare quella libera.

Per dimensionare il lavoro, misura il picco invece di tirare a indovinare: torch.cuda.max_memory_allocated() dopo uno step completo o un’immagine, e torch.cuda.memory_summary() per il dettaglio.

Quando smettere di ottimizzare e prendere una scheda più grande

Sali di taglia quando la soluzione ti costa qualità (scendere da FP8 a Q4), velocità (offload sulla CPU) o più tempo tuo di quanto valga la differenza di prezzo. Per confronto, un’ora costa da 0,59 € su una RTX 4090 da 24 GB, da 0,76 € su una RTX 5090 da 32 GB, da 1,06 € su una L40S da 48 GB e da 3,90 € su una H100 da 80 GB.

Cosa continua a finire la memoria Taglia di scheda adatta
Flux dev a 16 bit, o Flux con più LoRA e ControlNet 24–32 GB
Wan 2.2 14B a 720p senza offloading 32 GB per clip brevi, 48 GB per avere margine
Modello da 32B con 32.000+ token di contesto o più utenti 48–80 GB
Modello da 70B a 4 bit 48 GB con contesto normale, 80 GB per contesto lungo o serving
70B in FP8 80–96 GB con contesto breve, 141 GB o due schede per contesto lungo
Fine-tuning completo di un modello da 7–8B 80 GB e oltre, o più schede (la QLoRA sta in 16–24 GB)

Il selettore di VRAM mostra quali schede contengono il tuo modello e quanto costano adesso; la guida su quanta VRAM serve ha la tabella completa per modello. Un test veloce sulla scheda più grande costa appena 15 minuti del suo prezzo, e se salvi /workspace quando fermi il server, viene ripristinata sul server successivo anche se ha un’altra scheda.

Domande sull’argomento

Perché nvidia-smi mostra la memoria quasi piena quando il mio script non sta facendo niente?

PyTorch tiene riservata la memoria liberata per riutilizzarla, quindi risulta ancora occupata. Anche vLLM si prende di proposito circa il 90% della scheda all'avvio. Se nessuna delle due cose lo spiega, un vecchio kernel Jupyter o un processo andato in crash sta ancora tenendo un modello in memoria.

Perché il mio LLM si carica bene e poi va in crash in una conversazione lunga?

La KV cache cresce a ogni token di contesto e a ogni utente in parallelo. Un modello che ci sta con una chat da 4.000 token può esaurire la memoria a 30.000. Limita la lunghezza del contesto o quantizza la cache.

Due GPU risolvono il CUDA out of memory?

Per i modelli linguistici, di solito sì: vLLM, llama.cpp e Ollama possono dividere un modello su più schede. I modelli di immagini e video in ComfyUI in genere devono stare su una sola scheda, quindi per loro la soluzione è una singola scheda con più memoria.

L'offload sulla CPU o --lowvram sono una vera soluzione?

Fanno girare il lavoro, spesso parecchie volte più lentamente. Per una cosa una tantum va bene. Per il lavoro abituale, una scheda in cui il modello ci sta di solito costa meno per risultato.

Trova la GPU in cui entra il tuo modello

Scegli un modello e vedi la scheda più economica con VRAM sufficiente, con fatturazione al minuto in euro.