Olá a todos!
Prefácio: este texto nasce de uma pergunta que me fazem em quase todas as conversas sobre infraestrutura on-premise, normalmente numa chamada teams e normalmente antes de o projeto estar fechado. “Um 70B cabe numa placa de 24 GB?” A resposta honesta é que a pergunta está mal formulada, e este post é a minha tentativa de a reformular de uma vez por todas, com contas em vez de opiniões. No fim há uma calculadora que fizeram para isto, mas o objectivo é que consigam fazer a conta de cabeça mesmo sem ela.
Há um padrão que se repete com uma regularidade quase cómica. Alguém decide montar IA dentro de casa, vai ao Hugging Face, vê que o ficheiro do modelo que quer diz 16 GB, olha para a caixa da placa gráfica que diz 24 GB, faz a subtracção mental, sorri, e encomenda. Depois instala, corre, e a coisa até arranca. E um mês mais tarde, quando três pessoas começam a usar aquilo ao mesmo tempo com documentos grandes colados no contexto, o servidor entra em colapso, a geração passa a três tokens por segundo, e alguém me liga a perguntar se o modelo “está mal instalado”.
Não está mal instalado. Está mal dimensionado, que é uma doença diferente e bastante mais cara.
O erro está em comparar duas grandezas que parecem comparáveis e não são: o tamanho do ficheiro do modelo e o número impresso na caixa da placa. Nenhum dos dois é o que vocês pensam que é, e entre um e outro há pelo menos três compartimentos de memória que ninguém contou.>

O modelo mental que resolve isto
Antes das contas, o esqueleto. Uso este há uns anos e nunca me deixou ficar mal:
capacidade utilizável = VRAM anunciada x 0,90
total necessário = pesos do modelo + KV cache + workspace e folga
E a regra de ouro: compara-se o total necessário com a capacidade utilizável, nunca com o número da caixa.
Aquele 0,90 não é uma constante da física. É um ponto de partida conservador que uso para planear antes de ter hardware para medir. Numa máquina Linux headless, dedicada, sem ambiente gráfico e sem mais nada a competir pela placa, é capaz de conseguirem 0,94 ou 0,95. Numa estação Windows onde a mesma placa também está a desenhar dois monitores 4K, pode não passar dos 0,85. O valor certo é o que a vossa máquina vos disser depois de estar a correr. O 0,90 serve para fazer a encomenda.
Compartimento um: o piso exacto dos pesos
Comecemos pela parte que é aritmética pura e que, por isso, é a única em que não há discussão possível.
Um peso em quatro bits ocupa meio byte. Multiplica-se o número de parâmetros por 0,5 e está feito. Para os tamanhos que aparecem em noventa por cento das conversas:
| Modelo | Piso em 4 bits |
|---|---|
| 7B | 3,26 GiB |
| 13B | 6,05 GiB |
| 32B | 14,90 GiB |
| 70B | 32,60 GiB |
Reparem numa coisa antes de irem mais longe, porque é uma armadilha que apanha muito boa gente. Um 7B em quatro bits são 3 500 000 000 bytes. Isso são 3,5 GB se contarmos em unidades decimais, que é o que o Hugging Face mostra ao lado do ficheiro, e são 3,26 GiB se contarmos em unidades binárias, que é o que o nvidia-smi vos devolve em MiB. É o mesmo ficheiro. É a mesma memória. São dois números diferentes no ecrã, com uma diferença de sete por cento entre eles, e vi mais do que uma pessoa perder uma tarde a tentar perceber para onde é que os megabytes tinham fugido.
Escolham uma unidade e fiquem com ela até ao fim da conta. Eu conto tudo em GiB, porque é a unidade em que a placa reporta, e é a placa que decide se aquilo cabe ou não.
Porque é que o ficheiro real é sempre maior do que o piso
Aquela tabela é um piso teórico, e faço questão de lhe chamar piso e não estimativa. Nenhum ficheiro que vocês descarreguem vai ter exactamente aquele tamanho, e vai ser sempre para cima.
O motivo é que “quatro bits” nunca é quatro bits em todo o lado. Um GGUF Q4_K_M não guarda os pesos todos com a mesma precisão: guarda blocos de pesos em quatro bits com escalas e offsets associados, mantém algumas camadas em precisão superior porque quantizá-las destrói a qualidade, e ainda leva metadados, o tokenizador e a tabela de embeddings pelo caminho. Na prática, um Q4_K_M anda entre os 4,5 e os 5 bits por peso, o que quer dizer que um 7B que devia pesar 3,26 GiB aterra à volta dos 3,8 GiB. Quinze a vinte por cento acima do piso.
Nos formatos GPTQ e AWQ a mecânica é diferente mas a conclusão é a mesma: há sempre escalas, há sempre zero-points, há sempre alguma coisa que não é peso e que ocupa lugar. Não é desperdício, é o preço de a quantização não destruir o modelo.
Daqui sai a primeira regra prática, e é a mais importante deste post inteiro:
Comecem pelo ficheiro concreto que vão correr, não pela contagem de parâmetros. Vão ao repositório, vejam o tamanho real daquele quant específico, e usem esse número. A tabela de cima serve para saber se vale a pena continuar a conversa; não serve para encomendar hardware.
E já agora, uma nota sobre modelos MoE, porque estão em todo o lado e confundem toda a gente. Num modelo de arquitectura mista com trinta e cinco mil milhões de parâmetros totais e três mil milhões activos por token, a memória que ele ocupa é a dos parâmetros totais. Os trinta e cinco. A velocidade é que se aproxima da dos três. É uma arquitectura excelente e é exactamente por isso que a uso no meu router, mas quem lê “3B activos” e encomenda uma placa a pensar num modelo de 3B vai ter uma surpresa desagradável no dia da instalação.
Compartimento dois: a capacidade que a placa realmente vos dá
Aplicando os noventa por cento aos rótulos que se compram em Portugal hoje:
| Rótulo da placa | Planeamento realista |
|---|---|
| 8 GB | 7,2 GiB |
| 12 GB | 10,8 GiB |
| 16 GB | 14,4 GiB |
| 24 GB | 21,6 GiB |
| 32 GB | 28,8 GiB |
Aqueles dez por cento que desaparecem não são uma margem de segurança inventada por precaução. São coisas concretas, e vale a pena saber quais são, porque algumas conseguem-se recuperar e outras não.
O contexto de CUDA, que se cria quando o primeiro processo toca na placa, come algumas centenas de megabytes e come-os por processo. Se estão a correr o Ollama e um serviço de embeddings separado na mesma placa, pagam duas vezes. A fragmentação do alocador de memória rouba mais um bocado, e piora à medida que o processo vai vivendo: uma placa que aceita um pedido às nove da manhã pode recusar o mesmo pedido às seis da tarde sem que nada tenha mudado no pedido. O ambiente gráfico, se existir, leva entre meio giga e um giga e meio, e num servidor a sério isto resolve-se não instalando ambiente gráfico nenhum.
E depois há a parte de que quase ninguém se lembra e que num sistema on-premise a sério é decisiva: o modelo de linguagem raramente está sozinho na placa. Quem monta RAG tem um modelo de embeddings residente, que ocupa um a dois gigas. Quem quer resultados decentes tem também um reranker. Quem faz transcrição tem um Whisper. Quem tem um classificador à entrada, como eu tenho, tem mais um modelo pequeno permanentemente carregado. Somem isso tudo antes de decidir quanto sobra para o modelo principal, porque somar depois é como descobrir que a mobília não cabe depois de assinar a escritura.
Compartimento três: o KV cache, que é onde a conta rebenta
Esta é a parte que separa quem já pôs isto em produção de quem só correu um modelo no portátil.
Os pesos são estáticos. Carregam-se uma vez, ocupam sempre o mesmo, e é por isso que são fáceis de planear. O KV cache é dinâmico: cresce com o comprimento das conversas e multiplica-se pelo número de pessoas a falar ao mesmo tempo. É a única coisa na placa que responde ao sucesso do projecto crescendo, o que faz dele o candidato número um a fazer explodir um sistema que funcionava perfeitamente na demonstração.
A equação de partida é esta:
2 x camadas x cabeças KV x dimensão da cabeça x tokens em cache x bytes por elemento x sequências concorrentes
O 2 é porque se guardam duas coisas por cada posição, as chaves e os valores. O resto lê-se na configuração do modelo, no config.json ou nos metadados do GGUF. Não é preciso adivinhar nada.
Vamos a um caso concreto, porque em abstracto isto não convence ninguém. Suponham um modelo com 64 camadas, 8 cabeças KV, dimensão de cabeça 128, com o cache em 16 bits:
- 8 000 tokens de contexto, uma pessoa a falar: 2 GiB
- 32 000 tokens, uma pessoa: 8 GiB
- 32 000 tokens, quatro pessoas em simultâneo: 32 GiB
Leiam a última linha outra vez. Trinta e dois gigabytes só de cache, sem um único peso de modelo lá dentro. Numa placa de 24 GB, o cache sozinho já não cabia.
Existem truques para fazer offload para a memoria RAM normal do computador de alguns dos conteúdos, mas com obvia penalização na velocidade.
É este o número que ninguém põe na folha de cálculo, e é este o número que decide se o vosso sistema aguenta a equipa toda ou só a demonstração ao chefe. Duplicar o contexto duplica o cache. Duplicar os pedidos concorrentes em contexto cheio duplica outra vez. É crescimento multiplicativo em cima de uma quantidade fixa de memória, e o resultado é que a coisa passa de folgada a impossível muito mais depressa do que a intuição sugere.
Há três formas de atacar isto, e todas têm custo.
A primeira é escolher modelos com GQA agressivo. Aquele exemplo tinha 8 cabeças KV; um modelo com 64 cabeças KV e tudo o resto igual precisaria de oito vezes mais cache. Esta característica não aparece em nenhuma tabela comparativa de benchmarks e é, para quem corre com concorrência, mais determinante do que metade das métricas que aparecem.
A segunda é quantizar o próprio cache. Passar de 16 para 8 bits corta a memória do cache ao meio e é suportado pelos runtimes mais comuns. Custa alguma qualidade, sobretudo em contextos muito longos e em tarefas de recuperação de detalhe. Meçam antes de assumir que é grátis, e meçam com o vosso trabalho real e não com perguntas de brincadeira.
A terceira, e é a que recomendo primeiro, é ser honesto sobre o contexto de que precisam. Muita gente configura 128k porque estava lá disponível, e depois nunca ultrapassa os 6k em utilização real. Estão a pagar memória por um contexto imaginário. Vejam os logs, vejam a distribuição real dos comprimentos das vossas conversas, e configurem para o percentil 95 do que acontece de facto, não para o máximo do que é possível.
Ah, e confirmem qual é o valor por omissão do vosso runtime. É quase de certeza mais baixo do que aquilo que vão usar em produção, o que significa que o benchmark simpático que fizeram na semana passada foi feito com um cache minúsculo e não vos diz nada sobre o dia em que alguém colar um contrato de quarenta páginas no chat.
Compartimento quatro: workspace e folga
Falta o resto. Buffers de activação durante a inferência, o grafo de computação que o runtime constrói, os tensores temporários do processamento em lote do prompt, e a folga que impede que uma variação normal de utilização vos atire uma excepção de memória à cara.
Este compartimento é o mais difícil de calcular antecipadamente com precisão, porque depende do runtime, da versão do runtime, e do tamanho do lote. Por isso não o calculo: reservo. Um a dois gigabytes em placas pequenas, dois a três em placas grandes ou com concorrência, e ajusta-se depois de medir.
Quem achar que é margem a mais, faça as contas ao custo de um processo de inferência morrer a meio da tarde num sistema que trinta pessoas estão a usar, e compare com o custo de dois gigabytes de VRAM.
A minha ordem de operações
Junto tudo isto numa sequência que sigo sempre pela mesma ordem, e a ordem importa:
- Partir do checkpoint exacto, não da contagem de parâmetros. O ficheiro concreto, daquele quant, daquele repositório, com o tamanho que lá está escrito.
- Somar o KV cache para o contexto e a concorrência que vão mesmo ter, não para os que soam bem numa apresentação.
- Somar workspace e folga.
- Comparar o total com a capacidade utilizável, e não com o rótulo da caixa.
- Medir, assim que houver máquina: pico de memória, tempo até ao primeiro token, e tokens por segundo.
Os quatro primeiros passos fazem-se numa folha de cálculo antes de gastar um euro. O quinto é o único que vos diz a verdade, e é por isso que os outros quatro existem: para chegarem ao quinto com uma máquina que tem hipótese de passar.
Caber não é correr
Chegámos ao ponto que mais custa a aceitar, e que é a razão pela qual eu insisto tanto no passo 5.
Um modelo que cabe em memória não é um modelo que serve. São coisas independentes.
Os runtimes modernos são simpáticos de mais nesta parte. Quando o modelo não cabe todo na placa, não recusam: partem-no, deixam algumas camadas na VRAM e empurram as outras para a RAM do sistema, e anunciam que carregou. Tecnicamente carregou. Só que a partir daí, cada token gerado obriga a fazer viajar dados pelo PCIe, e o PCIe é uma ordem de grandeza mais lento do que a memória da placa. O que era uma resposta em dois segundos passa a ser uma resposta em quarenta, e a diferença entre um sistema que as pessoas adoptam e um sistema que as pessoas contornam está algures nesse intervalo.
O offload para CPU não é um erro do software. É uma decisão de engenharia razoável, tomada por omissão, que serve muito bem para experimentar e serve muito mal para produção. Quem monta on-premise deve saber quando ela está a acontecer, e a única forma de saber é olhar para quantas camadas foram efectivamente para a placa e comparar com o total.
Por isso as três métricas do passo 5, e por isso três e não uma:
O pico de memória diz-vos se cabe, medido durante um pedido em contexto cheio e não com o modelo parado a seguir ao arranque.
O tempo até ao primeiro token é a métrica da experiência de utilização. É o silêncio entre carregar em enter e ver alguma coisa acontecer, e é o número que decide se as pessoas voltam à ferramenta amanhã.
Os tokens por segundo dizem-vos se a resposta chega em tempo útil, e devem ser medidos com a concorrência real. Um sistema que faz 40 tokens por segundo para um utilizador e 6 para cinco utilizadores em simultâneo tem um problema que a medição individual nunca revelaria.
Uma nota sobre duas placas, que é o caminho por onde muita gente tenta fugir a estas contas. Duas placas de 24 GB não são uma placa de 48 GB. São 48 GB com uma ligação lá pelo meio, e o proveito que tiram disso depende do runtime, do modelo, e de as camadas dividirem bem. Às vezes resulta muito bem. Às vezes ficam com o dobro do consumo eléctrico e um sistema mais lento do que tinham antes. Se a alternativa for uma placa única com a memória necessária, e se o orçamento a permitir, a placa única é quase sempre a decisão certa.
O que isto muda na hora de comprar
A conclusão prática de tudo isto não é um número. É uma mudança de sequência.
A forma habitual de decidir é escolher a placa e depois ver que modelos lá cabem. A forma que funciona é a inversa: decidir que trabalho é que o sistema tem de fazer, que modelos servem esse trabalho, com que contexto e com quantas pessoas em simultâneo, somar os quatro compartimentos, e só então ir ver que placa é que tem essa memória.
E há uma decisão de gestão escondida aqui que vale mais do que qualquer optimização técnica. Se as contas derem que o modelo grande não cabe com a concorrência que precisam, a resposta correcta muitas vezes não é comprar uma placa maior. É correr três modelos pequenos especializados na placa que já têm, cada um bom no seu domínio, todos residentes ao mesmo tempo, sem trocas de modelo e sem esperas. Já escrevi aqui sobre isso e continuo a achar que é o caminho que a maior parte das organizações devia seguir antes de assinar uma factura de cinco dígitos.
A calculadora
Como faço estas contas com demasiada frequência, e como escrever a mesma folha de cálculo pela décima vez é a definição de trabalho que devia estar automatizado, procurei e achei isto numa página no ResearchAudio.
É gratuita, corre inteiramente no browser, e não envia os vossos dados para lado nenhum. Isso importa mais do que parece: quem está a dimensionar infraestrutura está a escrever números que descrevem a arquitectura interna da organização, e essa informação não tem que passear por servidores de terceiros só para fazer uma multiplicação.
https://tools.researchaudio.io/llm-gpu-memory-calculator/
Usem-na como aquilo que ela é: uma folha de planeamento que vos dá um número defensável para levar a uma reunião de orçamento, e não um substituto de medir o sistema depois de o ter montado.
E agora um pedido
Esta é a parte em quem faz essa calculadora precisa de vocês.
A folha está calibrada com as minhas medições e com as de alguns clientes, o que é uma amostra pequena e enviesada para o tipo de hardware e de trabalho que eu vejo. Se já mediram pico de memória real para uma combinação concreta de modelo, quantização, contexto e placa, mandem-me. Interessa-lhes o quarteto todo, e interessa-lhes sobretudo onde é que a previsão falhou: as combinações em que a folha diz que cabe e não cabe, ou em que diz que não cabe e afinal sobrava espaço. Os casos em que acertamos não ensinam nada a ninguém. Interessam onde houve um desvio do padrão.
Se aparecerem dados suficientes, eles publicarão a comparação entre o previsto e o medido, com os desvios à vista e sem os esconder. Uma folha de cálculo que ninguém confrontou com a realidade é uma opinião com formatação bonita.
Meçam a vossa. Depois digam-me se bate certo.
Até à próxima. Nuno
PS: as contas deste post foram todas verificadas à mão antes de publicadas, e o texto foi escrito com o auxílio de modelos LLM privados 🙂
As imagens presentes neste artigo são propriedade dos respetivos detentores de direitos e são aqui reproduzidas em baixa resolução, ao abrigo do direito de citação para fins de crítica, comentário e informação, nos termos do artigo 75.º, n.º 2, do Código do Direito de Autor e dos Direitos Conexos. Não se pretende com esta utilização causar qualquer prejuízo à exploração normal da obra. Caso seja detentor dos direitos e pretenda a remoção ou a correção da atribuição, contacte-nos através de [email protected] e o pedido será atendido assim que possível.