Quanta VRAM um LLM realmente ocupa
A conta de cabeça — parâmetros vezes bits — erra por vários gigabytes, porque ignora o KV cache. Aqui está a conta completa, com a fórmula, os números e como conferir na sua GPU.
Com pressa? Peça o TL;DR ao Claude — ele lê a página e resume.
A pergunta que mais recebo sobre rodar modelo local é sempre a mesma: cabe na minha placa?
E quase todo mundo faz a mesma conta errada. Pega o número de parâmetros, multiplica pelos bits da quantização, divide por oito e conclui que um modelo de 32B em Q4 ocupa 20 GB — logo, cabe folgado numa placa de 24 GB.
Aí carrega o modelo e a GPU estoura com 8 mil tokens de contexto.
O problema não é a conta. É que ela responde só um terço da pergunta. A memória que um modelo ocupa tem três parcelas, e a segunda cresce com o tamanho da conversa.
Parcela 1: os pesos
Essa é a parte fácil, e é a única que a maioria calcula. Cada parâmetro ocupa um número de bits que depende da quantização:
| Quantização | bits/peso | bytes/peso |
|---|---|---|
| FP16 (sem quantizar) | 16,0 | 2,00 |
| Q8_0 | 8,5 | 1,06 |
| Q6_K | 6,6 | 0,82 |
| Q5_K_M | 5,7 | 0,71 |
| Q4_K_M | 4,9 | 0,61 |
Repare que Q4_K_M não dá 4 bits por peso, e sim ~4,9. As K-quants guardam as camadas sensíveis (atenção, embeddings) com mais precisão do que o nome sugere. Quem calcula com 4,0 subestima o modelo em quase 20%.
Multiplicando pelos tamanhos mais comuns:
| Modelo | FP16 | Q8_0 | Q6_K | Q4_K_M |
|---|---|---|---|---|
| 8B | 16,1 | 8,5 | 6,6 | 4,9 |
| 12B | 24,4 | 13,0 | 10,1 | 7,5 |
| 32B | 65,6 | 34,9 | 27,1 | 20,1 |
| 70B | 141,2 | 75,0 | 58,2 | 43,2 |
É daqui que sai o número que todo mundo repete: 32B em Q4 = 20 GB. Está certo. E está incompleto.
Parcela 2: o KV cache — a que ninguém calcula
Todo token que entra na conversa deixa um rastro na memória. O modelo guarda os vetores de chave e valor de cada token, em cada camada, para não ter que recalcular a atenção do zero a cada palavra gerada. Isso é o KV cache, e ele cresce linearmente com o contexto.
A fórmula:
bytes por token = 2 × camadas × cabeças_kv × dim_cabeça × bytes_por_elemento
O 2 é porque são duas matrizes: chave e valor. Os outros três números você lê direto do config.json do modelo — num_hidden_layers, num_key_value_heads e head_dim (ou hidden_size / num_attention_heads, quando head_dim não está declarado).
Para o Llama 3 8B — 32 camadas, 8 cabeças KV, dimensão 128, em FP16:
2 × 32 × 8 × 128 × 2 bytes = 131.072 bytes = 128 KiB por token
128 KiB por token parece pouco. Multiplique por uma janela de contexto de verdade:
| Camadas (modelo típico) | KiB/token | 4k | 8k | 32k | 128k |
|---|---|---|---|---|---|
| 32 (≈8B) | 128 | 0,54 | 1,07 | 4,29 | 17,18 |
| 40 (≈12B) | 160 | 0,67 | 1,34 | 5,37 | 21,47 |
| 64 (≈32B) | 256 | 1,07 | 2,15 | 8,59 | 34,36 |
| 80 (≈70B) | 320 | 1,34 | 2,68 | 10,74 | 42,95 |
Um 32B com 32k de contexto reserva 8,6 GB só de cache — quase metade do que os pesos ocupam. Em 128k, o cache passa dos 34 GB e vira o item mais caro da conta, maior que o próprio modelo.
Parcela 3: o overhead do runtime
Sobra a parcela chata de estimar, porque depende do runtime, do driver e de quanto o kernel de atenção precisa de espaço temporário:
- Contexto de CUDA: entre 300 e 600 MB, só por existir um processo usando a GPU.
- Buffers de compute: o espaço temporário da atenção e do feed-forward, tipicamente algumas centenas de MB, proporcional ao batch e ao contexto.
- Fragmentação do alocador: a memória disponível nunca é 100% aproveitável.
Na prática, 1 GB é uma reserva honesta para uma inferência de usuário único no llama.cpp ou Ollama. Servidores com vLLM ou TensorRT-LLM reservam bem mais, porque pré-alocam o cache inteiro na subida.
A conta fechada
Juntando as três parcelas para o caso que me interessa — um 32B em Q4_K_M com 32k de contexto:
32B · Q4_K_M · 32k de contexto
- Pesos
- 20,1 GB
- KV cache
- 8,6 GB
- Overhead
- 1,0 GB
- Total
- 29,7 GB (27,6 GiB)
- Disponível
- 32 GiB (34,4 GB)
- Folga
- 4,4 GiB
Cabe — mas com 4 GiB de folga, não com os 12 GB que a conta de cabeça prometia. E se você subir o contexto para 64k, não cabe mais.
Como conferir de verdade
Toda essa aritmética serve para prever. Para saber, meça — antes e depois de carregar o modelo, com a mesma janela de contexto que você vai usar de fato:
# Antes de carregar: quanto já está ocupado (compositor, navegador, etc.)
nvidia-smi --query-gpu=memory.used,memory.total --format=csv
# Carrega com a janela de contexto real, nao com a padrao
OLLAMA_CONTEXT_LENGTH=32768 ollama run qwen3:32b "ok"
# Quanto o modelo reservou de fato, e se sobrou algo na CPU
ollama ps
O ollama ps mostra uma coluna PROCESSOR. Se ela disser algo como 70%/30% CPU/GPU, parte do modelo foi para a RAM do sistema — e aí o throughput despenca por um fator de dez, porque cada token precisa atravessar o barramento PCIe.
Quando não cabe
Em ordem de custo-benefício, do que menos dói para o que mais dói:
- Reduza o contexto. É a alavanca mais barata e a mais ignorada. Ninguém precisa de 128k para o que faz 90% do tempo, e cada corte pela metade devolve metade do cache.
- Quantize o KV cache.
--cache-type-k q8_0 --cache-type-v q8_0no llama.cpp corta o cache pela metade com perda de qualidade que raramente aparece em uso normal. É a melhor troca da lista. - Desça uma quantização. De Q6_K para Q4_K_M devolve uns 25% dos pesos. A degradação é real, mas costuma ser menor do que a intuição sugere.
- Faça offload de camadas para a CPU. Funciona, cabe quase qualquer coisa — e é dez vezes mais lento. Último recurso.
- Troque de modelo. Um 12B bem escolhido rodando inteiro na GPU entrega mais valor por segundo do que um 32B metade na CPU.
A ordem importa: as duas primeiras opções custam quase nada em qualidade e resolvem a maioria dos casos. A quarta é a que todo mundo tenta primeiro, e é a pior.
No próximo post eu meço o que essa conta prevê, na bancada, com número de tokens por segundo para cada configuração. A aritmética diz o que cabe; só a medição diz o que vale a pena.