11 novembre 2026 · Team VRAMfarm
CUDA out of memory: come risolverlo in ComfyUI, con gli LLM e nel training
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:
- 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. - 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
killdel PID. Se il tuo script muore ogni volta che parte un’altra app sulla scheda, il motivo è questo: si contendono la stessa memoria. - 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:
- 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.
- 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.
- 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.
- 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.
- 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.
- L’offload come ultima risorsa. Quando avvii ComfyUI da te,
--lowvramtiene parti del modello nella RAM di sistema e--reserve-vram 2lascia 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_ctxoOLLAMA_CONTEXT_LENGTH, e dimezza la cache conOLLAMA_FLASH_ATTENTION=1eOLLAMA_KV_CACHE_TYPE=q8_0. Ollama va in crash di rado; piuttosto sposta dei layer sulla CPU. Seollama psmostra qualcosa come35%/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-ngle 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 onla 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 fp8dimezza la cache,--max-num-seqslimita le richieste in parallelo,--enforce-eagerrisparmia la memoria occupata dai CUDA graph a scapito di un po’ di velocità, e--tensor-parallel-size 2divide 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:
- Batch più piccolo, stesso batch effettivo. Un batch size di 2 con 8 step di gradient accumulation addestra come un batch da 16.
- 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. - Mixed precision BF16 su qualsiasi scheda da Ampere in poi.
- Ottimizzatori a 8 bit. AdamW8bit di bitsandbytes (
optim="adamw_bnb_8bit") riduce gli stati dell’ottimizzatore da 8 byte per parametro a circa 2. - 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.
- 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.
- 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.