Alternar tema

Agendamento de GPU e gestão de recursos no Ollama: otimização de VRAM e balanceamento entre várias GPUs

Easton editorial illustration: agentic retrieval reasoning table

Com uma placa de 8 GB de VRAM, você finalmente consegue carregar um modelo 13B. Depois de algumas inferências, porém, tudo trava de repente e a tela mostra o erro “CUDA out of memory”.

Ou você investe em duas GPUs, esperando enfim executar modelos maiores, abre o nvidia-smi e descobre que apenas uma placa está trabalhando enquanto a outra continua ociosa.

Encontrei todos esses problemas quando comecei a usar o Ollama: VRAM insuficiente, várias GPUs sem uso e uma velocidade de inferência que oscilava sem explicação. Aos poucos, entendi a lógica de agendamento de GPU por trás do Ollama. Muita coisa não funciona só porque foi configurada; é preciso compreender o que os parâmetros realmente controlam.

Reuni aqui o que aprendi para resolver alguns problemas práticos:

  • Como executar um modelo 13B com estabilidade em 8 GB de VRAM, sem OOM inesperado
  • Como configurar várias GPUs para que elas sejam realmente utilizadas, com uma solução completa de balanceamento de carga
  • Quais parâmetros ajustar quando falta VRAM, em ordem de prioridade
  • O que é GPU offloading e como ele funciona internamente no llama.cpp

Um aviso antes de começar: o conteúdo é um pouco mais avançado e pressupõe alguma familiaridade com GPU, CUDA e as operações básicas do Ollama. Se você começou a usar o Ollama agora, vale ler primeiro os textos anteriores da série, especialmente o sexto, sobre fundamentos de otimização de desempenho. Com esse contexto, esta explicação fica bem mais fácil de acompanhar.

1. Como funciona a gestão de memória da GPU: todos os parâmetros

O agendamento de GPU do Ollama depende principalmente de alguns parâmetros que controlam como as camadas do modelo são distribuídas entre GPU e CPU. Entender esses parâmetros ajuda a explicar por que às vezes ocorre um erro mesmo quando a VRAM parece suficiente, ou por que a inferência fica lenta sem um motivo aparente.

1.1 Os principais parâmetros em detalhes

Estes são os parâmetros mais importantes, organizados em uma tabela para facilitar a consulta:

ParâmetroFunçãoValor padrãoQuando ajustar
num_gpuQuantas camadas do modelo são executadas na GPUDetecção automáticaReduza quando faltar VRAM
main_gpuNúmero da GPU principal0Defina qual placa usar em uma configuração com várias GPUs
low_vramAtiva o modo de pouca VRAMfalseRecomendado para placas com menos de 8 GB
num_batchTamanho do lote512Reduza para 256 quando a VRAM estiver no limite
num_ctxTamanho do contexto4096Em conversas curtas, 2048 economiza bastante VRAM

Entre esses parâmetros, num_gpu é o que mais causa confusão. Ele não representa a quantidade de GPUs instaladas, mas quantas camadas do modelo serão processadas na GPU.

Por exemplo, o Llama 2 7B tem 32 camadas. Ao configurar num_gpu: 32, todas as 32 camadas são executadas na GPU. Se a VRAM não for suficiente, você pode usar num_gpu: 20: 20 camadas ficam na GPU e as outras 12 são calculadas pela CPU, o que naturalmente reduz a velocidade.

O parâmetro low_vram é interessante. Quando ele está ativo, o Ollama usa algumas técnicas para economizar VRAM, como colocar o KV cache na memória da CPU em vez de mantê-lo na GPU. A inferência fica um pouco mais lenta, mas pelo menos o processo não trava.

1.2 Fluxo de alocação da VRAM

Quando o Ollama carrega um modelo, a alocação de VRAM segue este fluxo:

  1. Detecção da VRAM: primeiro, verifica quanta memória livre existe na GPU
  2. Cálculo das camadas: com base no tamanho do modelo e na VRAM disponível, calcula quantas camadas cabem na GPU
  3. Alocação do KV cache: reserva espaço para o cache usado na inferência, que também ocupa VRAM
  4. Início da inferência: o uso dinâmico da VRAM varia durante a execução

O ponto crítico está na segunda etapa: o Ollama calcula automaticamente a melhor distribuição de camadas. Esse cálculo nem sempre é preciso, principalmente quando a VRAM fica exatamente no limite, como ao executar um modelo 13B em uma placa de 8 GB. Nesses casos, é necessário definir num_gpu manualmente.

Para descobrir quantas camadas do modelo atual usam GPU offloading, execute:

ollama run llama3 --verbose

Na saída, você verá uma linha parecida com llama_model_load: model loaded - layers: 40/40 on GPU, indicando que todas as 40 camadas estão na GPU.

1.3 Como funciona o backend llama.cpp

O Ollama usa o llama.cpp como mecanismo de inferência. Ao entender a lógica de GPU offloading do llama.cpp, fica mais claro por que alguns ajustes de parâmetros produzem pouco efeito.

Decisão de GPU offloading

O llama.cpp faz aproximadamente este cálculo:

VRAM disponível = VRAM total da GPU - reserva do sistema (algumas centenas de MB)
Tamanho por camada = parâmetros do modelo / número de camadas
Camadas que cabem = min(total de camadas, VRAM disponível / tamanho por camada)

Há uma armadilha nesse cálculo: ele considera apenas a VRAM ocupada pelo modelo e não inclui o KV cache. Esse cache é usado durante a inferência e aumenta conforme a conversa fica mais longa. Por isso, às vezes o modelo carrega corretamente, mas, após algumas inferências, o KV cache esgota a VRAM e o processo trava.

75%
Economia de VRAM com quantização Q4
Source: Comparação prática: FP16 → Q4_K_M

Arquitetura de cálculo híbrido

GPU e CPU não trabalham de forma totalmente separada. Em linhas gerais:

  • A GPU cuida de: cálculos de matrizes e operações de attention, que exigem mais processamento
  • A CPU cuida de: operações de embedding e normalization, que exigem menos processamento
  • Transferência de dados: os dados precisam circular entre GPU e CPU, o que gera custo adicional

Quando apenas parte das camadas fica na GPU, o custo da transferência aumenta. Ao terminar uma camada, se a próxima estiver no outro dispositivo, o resultado precisa ser transferido antes que o cálculo continue. É por isso que o GPU offloading parcial pode reduzir bastante a velocidade de inferência.

Mapeamento de memória com mmap

Por padrão, o llama.cpp usa mmap para carregar o arquivo do modelo. As vantagens são:

  • Não é necessário ler o modelo inteiro para a memória; o sistema operacional carrega os dados sob demanda
  • Vários processos podem compartilhar a mesma região de memória
  • O consumo de memória é menor

Se você quiser desativar o mmap, pois ele pode causar problemas em algumas situações, configure no Modelfile:

PARAMETER use_mmap false

2. Configuração com várias GPUs: arquitetura completa de balanceamento de carga

Para quem tem duas ou mais GPUs, a principal dúvida é: como fazer o Ollama usar todas elas?

Primeiro, uma limitação importante: o Ollama não oferece paralelismo de modelo. Isso significa que não é possível dividir um modelo em duas partes, processando uma na GPU 0 e outra na GPU 1. Cada modelo só pode ser vinculado a uma GPU.

Então, para que servem várias GPUs? Há duas possibilidades:

  1. Executar instâncias de modelos diferentes: a GPU 0 executa o llama3 e a GPU 1 executa o mistral
  2. Executar várias instâncias do mesmo modelo: útil para balancear a carga e aumentar a vazão

2.1 Uma instância com várias GPUs: limitações e configuração

Se você só precisa que o Ollama reconheça várias GPUs, a forma mais simples é usar a variável de ambiente CUDA_VISIBLE_DEVICES:

# Permitir que o Ollama use apenas a GPU 0 e a GPU 1
CUDA_VISIBLE_DEVICES=0,1 ollama serve

Essa configuração, porém, tem um problema: por padrão, o Ollama coloca o modelo apenas na GPU 0, enquanto a GPU 1 continua ociosa. Você pode indicar a GPU principal com o parâmetro main_gpu:

# Modelfile
FROM llama3
PARAMETER main_gpu 1  # Definir a GPU 1 como principal

Na prática, essa solução tem utilidade limitada: você apenas trocou a placa que executa o modelo, sem aproveitar de fato a capacidade das duas GPUs.

2.2 Balanceamento entre várias instâncias: solução recomendada

A forma de realmente aproveitar várias GPUs é executar várias instâncias do Ollama, vincular cada uma a uma placa e distribuir as solicitações com um balanceador de carga.

A arquitetura fica assim:

┌─────────┐
│ Cliente │  Envia solicitações de inferência
└────┬────┘

┌────▼────────────────────┐
│ Nginx (balanceador)      │  Estratégia least_conn
│ Porta: 8080             │
└────┬─────────┬──────────┘
     │         │
┌────▼───┐ ┌──▼────┐
│Ollama 1│ │Ollama 2│
│GPU 0   │ │GPU 1   │  Cada instância usa uma GPU exclusiva
│Porta   │ │Porta   │
│11434   │ │11435   │
└────────┘ └────────┘

Etapa 1: iniciar várias instâncias do Ollama

# Instância 1: vinculada à GPU 0, porta 11434
CUDA_VISIBLE_DEVICES=0 OLLAMA_HOST=127.0.0.1:11434 ollama serve &

# Instância 2: vinculada à GPU 1, porta 11435
CUDA_VISIBLE_DEVICES=1 OLLAMA_HOST=127.0.0.1:11435 ollama serve &

Observação: o diretório de dados padrão do Ollama é ~/.ollama, e as duas instâncias compartilham o mesmo armazenamento de modelos. Isso não é um problema, pois o mapeamento de memória com mmap permite que vários processos compartilhem o mesmo arquivo do modelo.

Etapa 2: configurar o balanceamento de carga no Nginx

# /etc/nginx/conf.d/ollama.conf
upstream ollama_cluster {
    least_conn;  # Priorizar a instância com menos conexões
    server 127.0.0.1:11434;
    server 127.0.0.1:11435;
}

server {
    listen 8080;

    location / {
        proxy_pass http://ollama_cluster;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;

        # Suporte a respostas em streaming
        proxy_buffering off;
        proxy_cache off;
    }
}

A estratégia least_conn envia cada nova solicitação para a instância com o menor número de conexões ativas. Assim, a carga fica mais bem distribuída entre as duas GPUs.

Etapa 3: chamar pelo cliente

O cliente precisa se conectar apenas à porta do Nginx:

# Chamada pelo Nginx, distribuída automaticamente para uma das instâncias
curl http://localhost:8080/api/generate -d '{
  "model": "llama3",
  "prompt": "Olá"
}'

Outra opção é alterar o endereço padrão do cliente Ollama:

export OLLAMA_HOST=http://localhost:8080
ollama run llama3

2.3 Comparação das estratégias de balanceamento

O Nginx oferece diferentes estratégias de balanceamento de carga, cada uma adequada a um cenário:

EstratégiaComo funcionaCenário indicado
Round-robin (padrão)Envia solicitações alternadamente para cada instânciaCenários simples, com modelos do mesmo tamanho
Menos conexões (least_conn)Envia para a instância menos ocupadaRecomendado para serviços de inferência
IP HashUm mesmo IP sempre é enviado à mesma instânciaCenários que exigem persistência de sessão

As solicitações de um serviço de inferência têm duração imprevisível: algumas retornam em segundos, enquanto outras levam vários minutos. Com round-robin, uma instância pode ficar sobrecarregada enquanto outra permanece ociosa. least_conn evita esse desequilíbrio.

Se você quiser distribuir as solicitações de maneira uniforme e permitir uma troca automática quando uma instância falhar, adicione uma verificação de integridade:

upstream ollama_cluster {
    least_conn;
    server 127.0.0.1:11434 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:11435 max_fails=3 fail_timeout=30s;
}

Depois de três falhas consecutivas, o Nginx retira temporariamente a instância do cluster e tenta usá-la novamente após 30 segundos.

3. Estratégias para otimizar a VRAM: quantização, contexto e processamento em lote

Quando falta VRAM, ajuste os parâmetros nesta ordem: quantização > tamanho do contexto > tamanho do lote > número de camadas na GPU.

Por que essa ordem? Porque a quantização produz o maior impacto. Para o mesmo modelo, Q4 pode usar 75% menos VRAM que FP16. Alterar o número de camadas na GPU apenas transfere o cálculo para a CPU: economiza VRAM, mas reduz a velocidade.

3.1 Escolha do nível de quantização

Quantizar significa armazenar os parâmetros do modelo com menos bits. FP16 usa 16 bits por parâmetro, enquanto Q4 usa apenas 4. A redução de bits naturalmente causa alguma perda de precisão, mas, nos testes práticos, a perda do Q4 fica em torno de 2% a 3%, aceitável para a maioria dos cenários.

Comparação entre níveis de quantização:

QuantizaçãoUso de VRAM em relação ao FP16Perda de precisãoCenário indicado
Q4_K_MCerca de 25%2% a 3%Recomendado: equilíbrio entre desempenho e qualidade
Q5_K_MCerca de 33%1% a 2%Cenários que exigem um pouco mais de precisão
Q8_0Cerca de 50%0,5%Qualidade próxima da precisão original
FP16100%NenhumaPesquisa e testes de benchmark

Dados práticos de referência para o Llama 2 13B:

  • FP16: cerca de 26 GB de VRAM
  • Q4_K_M: cerca de 8 GB de VRAM
  • Q8_0: cerca de 13 GB de VRAM

Portanto, um modelo 13B Q4 cabe por pouco em 8 GB de VRAM. O problema é que o KV cache ainda precisa de espaço e pode esgotar a memória durante a inferência.

Uma recomendação ao escolher o nível de quantização: para uso cotidiano, Q4_K_M é suficiente. Em tarefas que exigem maior precisão, como tradução e geração de código, vale considerar Q5_K_M ou Q8_0.

Por padrão, o Ollama baixa a versão quantizada em Q4. Para usar outra quantização, adicione o sufixo ao nome do modelo:

# Quantização Q4 (padrão)
ollama pull llama3

# Quantização Q8
ollama pull llama3:8b-q8_0

3.2 Otimização do tamanho do contexto

O KV cache armazena o histórico anterior da conversa durante a inferência. Seu uso de VRAM está diretamente ligado ao tamanho do contexto.

Fórmula aproximada e simplificada:

VRAM do KV cache ≈ num_ctx × num_layers × hidden_dim × 2 bytes

Considere o Llama 2 7B como exemplo:

  • num_layers = 32
  • hidden_dim = 4096
  • num_ctx = 4096

O KV cache fica em torno de 2 GB. Se o ctx aumentar para 8192, ele passa a ocupar 4 GB. Ao dobrar o contexto, você também dobra a VRAM do KV cache.

Estratégias de otimização:

  1. Conversas curtas: use num_ctx: 2048

    • Economiza metade da VRAM do KV cache
    • É suficiente para perguntas e respostas cotidianas e tarefas simples
  2. Processamento de documentos longos: não defina diretamente o ctx como 16000 ou mais; use uma estratégia de chunking

    • Divida o documento em trechos menores e processe um de cada vez
    • É mais estável e controlável do que enviar tudo de uma vez

Defina o tamanho do contexto no Modelfile:

FROM llama3
PARAMETER num_ctx 2048  # Reduzir o tamanho do contexto

É importante desfazer um equívoco comum: muita gente acredita que reduzir o ctx prejudica a qualidade da saída. Não é assim. O ctx determina apenas quanto da conversa anterior o modelo consegue “lembrar”. Se a conversa tem poucas rodadas, usar 2048 ou 4096 produz o mesmo resultado.

3.3 Otimização de lotes e concorrência

O parâmetro num_batch controla quantos tokens são processados de uma vez. O valor padrão é 512, ou seja, o Ollama processa 512 tokens de inferência por lote.

Qual é a vantagem de um lote grande? Maior eficiência no processamento paralelo. O custo é um pico mais alto no uso de VRAM.

Quando a memória está no limite, reduzir o lote ajuda a diminuir esse pico:

FROM llama3
PARAMETER num_batch 256  # Reduzir de 512 para 256

Nos testes práticos, reduzir o batch de 512 para 256 diminuiu o pico de VRAM em cerca de 20%. A inferência fica um pouco mais lenta, mas a queda não é tão grande quanto ao reduzir o número de camadas na GPU.

O problema das solicitações simultâneas

Por padrão, o Ollama processa as solicitações em série: termina uma antes de iniciar a próxima. Se várias forem enviadas ao mesmo tempo, elas ficam na fila.

Há duas formas de aumentar a capacidade concorrente:

  1. Implantar várias instâncias: use a solução de balanceamento entre várias GPUs descrita acima; cada instância processa solicitações de forma independente
  2. Usar um sistema de filas: adicione uma fila na camada da aplicação, como Redis Queue, e gerencie a distribuição das solicitações

A segunda opção é mais adequada quando você não tem várias GPUs. No código da aplicação, o fluxo pode ser assim:

import redis
from queue import Queue

# Usar o Redis como fila
r = redis.Redis()
r.lpush('ollama_queue', request_data)

# O worker em segundo plano retira solicitações da fila para processá-las
request = r.rpop('ollama_queue')
ollama.generate(request)

4. Casos práticos: três situações reais

Depois da teoria, estes são alguns problemas reais e as soluções aplicadas.

4.1 Cenário 1: executar um modelo 13B com estabilidade em 8 GB de VRAM

Problema

O usuário tem uma RTX 3060 com 8 GB de VRAM e quer executar o modelo Llama 2 13B Q4. O próprio modelo precisa de cerca de 8 GB e cabe por pouco. Depois de algumas inferências, porém, começam os erros de OOM porque o KV cache esgota a memória.

Solução

A ideia central é reduzir o uso do KV cache e o pico de VRAM.

FROM llama2:13b-q4

PARAMETER num_gpu 30      # O modelo 13B tem 40 camadas; colocar apenas 30 na GPU
PARAMETER low_vram true   # Ativar o modo de pouca VRAM e colocar o KV cache na memória da CPU
PARAMETER num_ctx 2048    # Reduzir o contexto pela metade e também diminuir o KV cache pela metade
PARAMETER num_batch 256   # Reduzir o lote para diminuir o pico de memória

Com esses parâmetros combinados, o uso de VRAM se mantém estável em torno de 6 GB, deixando 2 GB de margem para oscilações.

Resultado

6 GB
Uso estável de VRAM
Source: De 8 GB para 6 GB, sem novos erros de OOM
  • Uso de VRAM: caiu de cerca de 8 GB para aproximadamente 6 GB, com execução estável
  • Velocidade de inferência: cerca de 8 tokens/s, mais lenta que o uso integral da GPU, mas muito mais rápida que apenas CPU
  • Estabilidade: os travamentos por OOM deixaram de ocorrer

O custo é uma inferência um pouco mais lenta. Dez camadas precisam ser processadas pela CPU, e toda passagem entre GPU e CPU gera transferência de dados. Ainda assim, a configuração se torna utilizável e deixa de travar inesperadamente.

4.2 Cenário 2: aumentar a vazão com balanceamento entre duas GPUs

Problema

O usuário tem duas RTX 3090, cada uma com 24 GB de VRAM, e implantou o Ollama como um serviço de API externo. Uma única instância processa apenas uma solicitação por vez, a concorrência é baixa e, nos horários de pico, a fila cresce bastante.

O nvidia-smi mostra uma grande diferença de utilização entre as placas: uma fica constantemente acima de 70%, enquanto a outra permanece perto de 20%.

Solução

Usar várias instâncias com balanceamento de carga no Nginx, como explicado em detalhes na seção 2. Este é o script completo de inicialização:

#!/bin/bash
# start_ollama_cluster.sh

# Instância 1: GPU 0
CUDA_VISIBLE_DEVICES=0 \
OLLAMA_HOST=127.0.0.1:11434 \
OLLAMA_MODELS=/home/user/.ollama \
nohup ollama serve > ollama1.log 2>&1 &

# Instância 2: GPU 1
CUDA_VISIBLE_DEVICES=1 \
OLLAMA_HOST=127.0.0.1:11435 \
OLLAMA_MODELS=/home/user/.ollama \
nohup ollama serve > ollama2.log 2>&1 &

# Pré-carregar o modelo nas duas instâncias
sleep 5
curl http://127.0.0.1:11434/api/pull -d '{"name": "llama3"}'
curl http://127.0.0.1:11435/api/pull -d '{"name": "llama3"}'

echo "Cluster do Ollama iniciado nas portas 11434 e 11435"

A configuração do Nginx usa least_conn para distribuir as solicitações de maneira uniforme.

Resultado

80%
Aumento da vazão
Source: Duas instâncias em paralelo versus uma instância em série
  • Vazão total: aumento de cerca de 80% ao passar de uma instância em série para duas em paralelo
  • Utilização por GPU: passou de uma média de 40% para 80%, com as duas placas trabalhando
  • Latência de resposta: redução de cerca de 50% nos horários de pico, pois as solicitações deixaram de esperar na fila

Nos testes práticos, uma única instância levou cerca de 10 minutos para processar 100 solicitações; com duas instâncias balanceadas, o tempo caiu para pouco mais de 5 minutos.

4.3 Cenário 3: automatizar a alocação dinâmica de VRAM

Problema

O usuário trabalha com vários modelos de tamanhos diferentes e precisa ajustar manualmente a quantidade de camadas na GPU a cada troca. Quando esquece de mudar a configuração, o processo trava. É possível automatizar isso?

Solução

Criar um script que escolha automaticamente a configuração adequada do Modelfile com base na VRAM disponível.

#!/bin/bash
# auto_offload.sh - Configuração automática de GPU offloading

# Obter a VRAM livre atual da GPU, em MB
GPU_MEM_FREE=$(nvidia-smi --query-gpu=memory.free --format=csv,noheader,nounits | head -1)

# Referência de tamanho dos modelos, em MB
declare -A MODEL_SIZES
MODEL_SIZES["llama3:8b-q4"]=5000
MODEL_SIZES["llama3:70b-q4"]=40000
MODEL_SIZES["mistral:7b-q4"]=4500

MODEL_NAME=$1

if [ -z "$MODEL_NAME" ]; then
    echo "Uso: $0 <nome_do_modelo>"
    exit 1
fi

MODEL_SIZE=${MODEL_SIZES[$MODEL_NAME]}

if [ -z "$MODEL_SIZE" ]; then
    echo "Tamanho desconhecido para o modelo $MODEL_NAME"
    exit 1
fi

# Verificar se há VRAM suficiente para usar apenas a GPU
if [ $GPU_MEM_FREE -gt $MODEL_SIZE ]; then
    # Offloading completo para GPU
    echo "Usando offloading completo para GPU (memória suficiente)"
    cat > /tmp/modelfile_temp <<EOF
FROM $MODEL_NAME
PARAMETER num_gpu -1  # -1 indica uso integral da GPU
PARAMETER low_vram false
EOF
else
    # GPU parcial: calcular a proporção adequada de camadas
    OFFLOAD_RATIO=$((GPU_MEM_FREE * 100 / MODEL_SIZE))
    echo "Usando offloading parcial para GPU ($OFFLOAD_RATIO%)"
    cat > /tmp/modelfile_temp <<EOF
FROM $MODEL_NAME
PARAMETER num_gpu $OFFLOAD_RATIO
PARAMETER low_vram true
PARAMETER num_ctx 2048
EOF
fi

# Criar o modelo
ollama create "${MODEL_NAME}-auto" -f /tmp/modelfile_temp
echo "Modelo ${MODEL_NAME}-auto criado com configuração automática"

Como usar:

# Executar o script para criar automaticamente uma configuração adequada
./auto_offload.sh llama3:70b-q4

Resultado

  • Adaptação automática às mudanças na VRAM disponível
  • Menos erros de configuração manual
  • Troca de modelo sem precisar ajustar os parâmetros toda vez

O script ainda pode ser ampliado. É possível adicionar monitoramento para mudar automaticamente para o modo de pouca VRAM quando faltar memória, ou combinar a solução com uma tarefa agendada que pré-carrega o modelo durante a madrugada, quando não há usuários.

5. Boas práticas e monitoramento: configurações recomendadas, ferramentas e problemas comuns

5.1 Configurações recomendadas por quantidade de VRAM

Esta tabela de consulta rápida ajuda a escolher uma configuração adequada ao seu hardware:

VRAMModelo recomendadoQuantizaçãoCamadas na GPUOutros parâmetros
6 GBModelo 7BQ4Parcial, cerca de 50%low_vram=true, ctx=2048
8 GBModelo 7BQ4GPU completactx=2048, por segurança
8 GBModelo 13BQ4Parcial, cerca de 75%low_vram=true, ctx=2048, batch=256
12 GBModelo 13BQ4GPU completaÉ possível usar ctx=4096
16 GBModelo 13BQ8 ou Q5GPU completactx=4096
16 GBModelo 70BQ4Parcial, cerca de 50%low_vram=true
24 GBModelo 70BQ4GPU completaÉ possível usar ctx=4096
48 GB, duas placasModelo 70BQ4GPU completaBalanceamento entre várias instâncias

Observação: estas são estimativas conservadoras. Na prática, também é preciso considerar o KV cache e o espaço reservado pelo sistema. Em conversas longas, com contexto maior, uma configuração mais conservadora tende a ser mais estável.

5.2 Ferramentas para monitorar a VRAM

Monitoramento em tempo real com nvidia-smi

A forma mais simples é:

# Atualizar a cada segundo
nvidia-smi -l 1

# Exibir apenas o uso da VRAM
nvidia-smi --query-gpu=memory.used,memory.free --format=csv -l 1

A saída mostra o uso de memória de cada placa. Ao observar os valores durante a inferência, você consegue identificar como a VRAM aumenta.

Logs detalhados do Ollama

ollama run llama3 --verbose

A saída mostra informações detalhadas sobre o carregamento do modelo, incluindo:

  • Número de camadas com GPU offloading
  • Uso de memória do modelo
  • Se o mmap está ativo
  • Alocação do KV cache

Ao ver uma mensagem como GPU offloading: 40/40 layers, você sabe que todas as camadas do modelo estão na GPU.

Exemplo de script de monitoramento

Para acompanhar o uso da VRAM por períodos mais longos, você pode criar um script que registre os dados em um log:

#!/bin/bash
# monitor_gpu.sh

LOG_FILE="gpu_memory.log"

while true; do
    TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
    GPU_MEM=$(nvidia-smi --query-gpu=memory.used,memory.free --format=csv,noheader)
    echo "$TIMESTAMP $GPU_MEM" >> $LOG_FILE
    sleep 5
done

Deixe o script rodando em segundo plano para consultar os dados históricos quando necessário.

5.3 Solução de problemas comuns

Problema 1: OOM durante a inferência

Siga estas etapas:

  1. Verifique o nvidia-smi para confirmar que a VRAM realmente acabou
  2. Confira a configuração atual:
    • A quantização é Q4? Se não for, mude para Q4
    • O tamanho do contexto está grande demais? Reduza para 2048
    • O lote está grande demais? Reduza para 256
    • Todas as camadas estão na GPU? Retire algumas delas
  3. Se os ajustes anteriores não resolverem, ative low_vram=true

Ordem de prioridade: quantização > ctx > batch > número de camadas na GPU > low_vram

Problema 2: inferência lenta

Primeiro, confirme se o número de camadas com GPU offloading é insuficiente:

ollama run your_model --verbose | grep "GPU offloading"

Se a saída mostrar GPU offloading: 20/40 layers, metade das camadas está sendo calculada pela CPU, e a lentidão é esperada.

Solução: reduza o nível de quantização, passando de Q4 para Q8, ou use uma placa com mais VRAM. Se nenhuma opção for possível, será necessário aceitar essa velocidade.

Problema 3: a VRAM oscila muito e fica instável

A principal causa das oscilações é o KV cache. Quanto mais longa a conversa, maior o cache.

Solução: limite o tamanho do contexto ou controle o histórico na camada da aplicação, mantendo, por exemplo, apenas as dez rodadas mais recentes.

Problema 4: depois de configurar várias GPUs, apenas uma é usada

Verifique se a configuração do Nginx entrou em vigor:

curl http://localhost:8080/api/tags

Se a resposta mostrar apenas um modelo, isso indica que as solicitações estão sendo distribuídas.

Se houver grande diferença de utilização entre as duas placas, as causas possíveis são:

  • A estratégia least_conn não foi configurada
  • Uma das instâncias apresentou problema; verifique os logs
  • O modelo foi carregado em apenas uma das instâncias

Resumo

Depois de todos esses detalhes, os pontos principais são:

  1. Quando faltar VRAM, ajuste primeiro a quantização: Q4 usa 75% menos memória que FP16 e causa pouca perda de qualidade
  2. Observe o uso do KV cache: o tamanho do contexto afeta diretamente o cache, e conversas longas pressionam mais a VRAM
  3. Use balanceamento para várias GPUs: uma única instância tem utilidade limitada; várias instâncias com Nginx são a solução adequada
  4. Entenda o funcionamento do llama.cpp: GPU offloading não é mágica, mas um cálculo dividido por camadas que gera custo de transferência de dados

Estas são algumas configurações prontas para uso:

Configuração estável para 8 GB de VRAM:

PARAMETER num_gpu 30
PARAMETER low_vram true
PARAMETER num_ctx 2048
PARAMETER num_batch 256

Inicialização com balanceamento entre duas GPUs:

CUDA_VISIBLE_DEVICES=0 OLLAMA_HOST=127.0.0.1:11434 ollama serve &
CUDA_VISIBLE_DEVICES=1 OLLAMA_HOST=127.0.0.1:11435 ollama serve &

Se este conteúdo foi útil, confira os outros textos da série. O sexto explica os fundamentos de quantização e processamento em lote; este aprofunda a parte de GPU. O oitavo aborda a implantação paralela de vários modelos e aplica a configuração com várias GPUs a cenários mais complexos.

Em caso de dúvida, procure no GitHub Discussions do Ollama, onde a comunidade já discutiu muitos problemas práticos. Você também pode deixar um comentário; responderei quando vir.


FAQ

O Ollama consegue dividir um modelo entre várias GPUs para fazer cálculos em paralelo?
Não. O Ollama não oferece paralelismo de modelo (Tensor Parallelism), e cada instância do modelo só pode ser vinculada a uma GPU. Para aproveitar várias GPUs, é necessário executar várias instâncias e usar o Nginx para balancear a carga.
Por que o modelo carrega corretamente, mas apresenta OOM depois de algumas inferências?
Por causa do KV cache. O carregamento considera apenas a VRAM ocupada pelo próprio modelo, mas, durante a inferência, o KV cache cresce conforme o tamanho da conversa. Recomendações:

• Reduza o tamanho do contexto (num_ctx)
• Ative o modo low_vram
• Encurte o histórico da conversa
Qual parâmetro devo ajustar primeiro quando falta VRAM?
A prioridade é: quantização > tamanho do contexto > processamento em lote > número de camadas na GPU > low_vram. A quantização tem o maior impacto: Q4 pode economizar 75% da VRAM com perda de qualidade de apenas 2% a 3%.
O parâmetro num_gpu indica quantas GPUs eu tenho?
Não. num_gpu indica quantas camadas do modelo serão processadas na GPU. Em um modelo de 32 camadas, por exemplo, num_gpu=32 coloca todas elas na GPU; num_gpu=20 deixa 20 camadas na GPU e 12 na CPU.
Qual estratégia usar para balancear a carga entre várias GPUs?
Recomendo least_conn (prioridade para o menor número de conexões). Como a duração das solicitações de inferência é imprevisível, o round-robin pode sobrecarregar uma instância enquanto outra fica ociosa. least_conn envia a solicitação para a instância menos ocupada naquele momento.
Que tamanho de modelo cabe em 8 GB de VRAM?
Configuração conservadora:

• 7B Q4: tudo na GPU, ctx=2048
• 13B Q4: GPU parcial (cerca de 75%), com low_vram + ctx=2048 + batch=256
• Modelos maiores exigem mais VRAM ou offloading para CPU

21 min de leitura · Publicado em: 11 abr 2026 · Atualizado em: 4 set 2026

Comentários

Entre com GitHub para comentar

Easton BlogEaston Blog