KV Cache em GPUs de 16 GB: Fazendo o Longo Contexto Caber de Fato

Por que o contexto de 128K falha em 16 GB

Conteúdo da página

Um modelo pode anunciar uma janela de contexto de 128K e ainda assim falhar com 40K tokens em uma GPU de 16 GB. O limite da arquitetura nunca prometeu que os pesos, o cache KV, os buffers de computação e o compositor do ambiente gráfico caberiam na sua placa ao mesmo tempo.

O cache KV é geralmente onde os planos de contexto longo encontram esse limite físico. Ele cresce com cada token e sequência ativa, portanto, uma configuração que parece confortável na inicialização pode desacelerar drasticamente, transbordar para a memória do sistema ou falhar durante um prefill grande.

Orçamento de memória do cache KV em uma GPU de 16 GB

Este guia transforma o problema em um orçamento de VRAM. Ele cobre a fórmula do cache, tabelas reproduzíveis de tamanho de 32K a 128K e configurações funcionais para --cache-type-k e --cache-type-v do llama.cpp, cache paginado e de prefixo do vLLM e controles de contexto do Ollama — além dos forks experimentais de cache adaptativo que merecem interesse, mas não confiança cega. Para o contexto mais amplo de throughput, latência e benchmarks por trás desses números, comece pelo hub de Desempenho de LLMs.

A Resposta Curta para uma GPU de 16 GB

Comece com uma sequência, um contexto máximo realista, Flash Attention e um cache KV de 8 bits. Meça essa configuração antes de tentar cache de 4 bits, offload para CPU, múltiplos slots paralelos ou um fork experimental.

Objetivo Primeira tentativa sensata em 16 GB Principal risco
32K Pesos Q4 ou Q5, KV Q8, uma sequência Os pesos do modelo deixam pouco espaço para buffer
64K Modelo menor ou quantização agressiva de pesos, KV Q8 Latência de prefill e largura de banda do cache
128K Modelo GQA pequeno, KV Q8 ou Q4 testado, uma sequência Apenas o cache pode consumir a maior parte da VRAM
Duas sessões concorrentes de 64K Trate como um orçamento de cache de aproximadamente 128K Capacidade paralela é confundida com throughput gratuito

Minha opinião é simples: uma configuração estável de 64K é geralmente mais útil do que uma configuração nominal de 128K que roda na beira de uma falha de falta de memória. Capacidade de contexto não é um troféu; é uma decisão de latência, qualidade e concorrência.

O Que o Cache KV Armazena

Durante a geração autoregressiva, cada camada de atenção produz tensores de chave e valor para cada token processado. O runtime retém esses tensores para que o próximo token possa atender aos tokens anteriores sem recomputar o prefixo inteiro.

O cache economiza uma enorme quantidade de computação, mas consome memória em proporção à quantidade de tokens retidos. Para um transformador convencional com atenção de consulta agrupada, uma linha de base útil é:

bytes KV = sequências * tokens * camadas * 2 * cabeças KV * dimensão da cabeça * bytes por valor

O fator de dois representa chaves e valores. A atenção multi-cabeça usa tantas cabeças KV quanto cabeças de consulta, a atenção de consulta agrupada usa menos cabeças KV, e a atenção latente multi-cabeça ou arquiteturas recorrentes híbridas necessitam de cálculos diferentes.

Por Que a Contagem de Parâmetros Não é Suficiente

Dois modelos de 8B podem ter custos de cache KV muito diferentes. Um pode usar 32 camadas e oito cabeças KV, enquanto outro pode usar menos cabeças KV, camadas KV compartilhadas, atenção com janela deslizante ou estados latentes comprimidos.

A contagem de parâmetros prediz principalmente a memória dos pesos. A geometria do KV vem da arquitetura de atenção, portanto, leia os metadados do modelo em vez de chutar com base em 8B, 27B ou no tamanho do arquivo GGUF. A ilustração mais clara é o quão longe o design de atenção se moveu além da atenção multi-cabeça pura (MHA):

  • Atenção de Consulta Única (MQA) compartilha uma única cabeça K/V entre todas as cabeças de consulta — economia máxima de cache, mas é o compromisso de qualidade mais agressivo e raramente é usado isoladamente em modelos de fronteira atuais.
  • Atenção de Consulta Agrupada (GQA) agrupa cabeças de consulta em clusters que compartilham uma única cabeça K/V — o compromisso mainstream usado pela maioria dos modelos densos abertos, e a geometria que a fórmula acima pressupõe.
  • Atenção Latente Multi-Cabeça (MLA), introduzida no DeepSeek-V2 e levada para o DeepSeek-V3 e Kimi K2, adota uma abordagem completamente diferente: em vez de compartilhar K/V entre cabeças, projeta chaves e valores para um vetor latente de baixa dimensão comprimido e reconstrói o K/V em resolução total sob demanda no momento da atenção. O DeepSeek relatou uma redução de aproximadamente 93% no cache KV em comparação com um modelo denso MHA de tamanho equivalente, mantendo a qualidade competitiva — e às vezes superior — à da GQA com o mesmo orçamento de memória.

A consequência prática é que um “modelo GQA de 27B” e um “modelo MLA de 27B” podem ter pegadas de cache KV que diferem por uma ordem de grandeza para o mesmo comprimento de contexto. Não assuma que a fórmula acima se aplica a um modelo que se documenta como usando atenção latente, estado no estilo DeltaNet ou camadas de janela deslizante — verifique a seção de arquitetura do cartão do modelo primeiro.

Com o Ollama, ollama show MODEL --verbose expõe metadados do modelo, incluindo contagem de camadas, cabeças de atenção, cabeças KV e comprimento de contexto, onde o formato os fornece. Com o llama.cpp, a saída do carregador de modelo impressa na inicialização geralmente inclui metadados GGUF equivalentes e a alocação real de cache do runtime.

Limite do Modelo, Contexto Alocado e Contexto Usado

Estes são três números separados. O limite do modelo é o máximo suportado pelo seu treinamento e codificação posicional, o contexto alocado é o que o runtime reserva ou permite, e o contexto usado é os tokens atualmente retidos para uma sequência.

Aumentar uma flag do motor não pode estender com segurança um modelo além do seu esquema de posições suportado. A escalonamento RoPE pode estender algumas arquiteturas, mas é um experimento de qualidade do modelo, não uma otimização de memória KV.

Tabela de Tamanho do Cache KV: Orçamentos de Contexto de 32K, 64K e 128K

Considere um modelo GQA representativo com 32 camadas, oito cabeças KV e uma dimensão de cabeça de 128. Essas dimensões produzem 65.536 elementos de chave e valor por token antes de multiplicar pelo tamanho de armazenamento de cada elemento.

A tabela usa GiB binários e os tamanhos de blocos físicos comumente associados a f16, q8_0 e q4_0 do llama.cpp. É um cálculo de linha de base, não uma promessa sobre a memória total do processo; alinhamento, metadados, camadas híbridas e espaços de trabalho do backend adicionam sobrecarga.

Tipo de cache Bytes aprox. por valor armazenado Contexto 32K Contexto 64K Contexto 128K
F16 2.0000 4.00 GiB 8.00 GiB 16.00 GiB
Q8_0 1.0625 2.13 GiB 4.25 GiB 8.50 GiB
Q4_0 0.5625 1.13 GiB 2.25 GiB 4.50 GiB
K Q8_0 + V Q4_0 Misto 1.63 GiB 3.25 GiB 6.50 GiB

Agora dobre a contagem de camadas para 64, mantendo as outras dimensões inalteradas. O cache FP16 se torna 8 GiB em 32K, 16 GiB em 64K e 32 GiB em 128K, o que demonstra por que uma única recomendação de contexto não pode cobrir todos os modelos.

A Equação Real de 16 GB

Um orçamento prático é mais amplo do que a fórmula do KV:

VRAM utilizável = VRAM total - reserva da área de trabalho e driver

orçamento KV = VRAM utilizável
          - pesos do modelo residentes na GPU
          - buffers de grafo e ativação
          - espaço de trabalho do runtime
          - estado de decodificação especulativa
          - margem de segurança

Em um cartão de 16 GB conectado a uma área de trabalho, não planeje considerando que todos os 16 GiB estejam disponíveis. Reserve pelo menos algumas centenas de MiB para a área de trabalho e o driver, e depois deixe outra margem para buffers dependentes da carga de trabalho; uma folga total de 1.0 a 1.5 GiB é uma premissa de partida razoável, mas seus logs são a autoridade.

Suponha que um modelo GGUF ocupe 10.8 GiB na GPU e a sobrecarga do runtime atinja um pico próximo a 1.2 GiB. Após uma margem de segurança de 1 GiB, sobra apenas cerca de 3 GiB para KV, portanto, o modelo representativo cabe em aproximadamente 45K tokens com Q8_0 ou 87K com Q4_0, antes da sobrecarga específica do motor.

Isso não torna automaticamente o Q4_0 a escolha certa. Se a precisão de contexto longo diminuir na sua carga de trabalho, um modelo menor ou mais agressivamente quantizado com um cache Q8_0 pode ser melhor do que pesos maiores combinados com um cache frágil. Âncoras medidas para exatamente essa aritmética estão nas tabelas de benchmark do llama.cpp com VRAM de 16 GB, onde a VRAM por modelo é registrada em contextos de 19K, 32K e 64K. Para uma pesquisa mais ampla de quais tamanhos de modelo e níveis de quantização se comportam bem sob Ollama na mesma classe de cartão, veja Comparando o desempenho de LLMs no Ollama em GPU com VRAM de 16GB.

Calcule o Orçamento do Cache KV Para o Seu Modelo

O seguinte trecho de Python estima um cache GQA de atenção completa convencional. Substitua a geometria pelos valores da configuração do modelo ou dos metadados GGUF.

def kv_gib(tokens, layers, kv_heads, head_dim, bytes_per_value, sequences=1):
    total = (
        sequences
        * tokens
        * layers
        * 2
        * kv_heads
        * head_dim
        * bytes_per_value
    )
    return total / (1024 ** 3)


model = {
    "layers": 32,
    "kv_heads": 8,
    "head_dim": 128,
}

types = {
    "f16": 2.0,
    "q8_0": 34 / 32,
    "q4_0": 18 / 32,
}

for tokens in (32768, 65536, 131072):
    row = {
        name: round(kv_gib(tokens=tokens, bytes_per_value=size, **model), 2)
        for name, size in types.items()
    }
    print(tokens, row)

As proporções Q8_0 e Q4_0 incluem metadados de bloco simples, que é por que elas são ligeiramente maiores que exatamente um byte e meio byte por valor. O relatório de inicialização do runtime permanece mais preciso porque conhece os layouts de cache específicos do modelo.

Quando Esta Fórmula está Errada: Arquiteturas Híbridas e de Janela Deslizante

Não force arquiteturas híbridas na equação GQA convencional. Camadas de janela deslizante retêm apenas uma janela recente, camadas KV compartilhadas reduzem duplicação, camadas recorrentes podem carregar estado de tamanho fixo, e a atenção latente multi-cabeça armazena uma representação comprimida em vez de tensores K/V por cabeça — o caso da MLA acima sendo o exemplo mais dramático.

Os motores modernos gerenciam cada vez mais esses layouts mistos explicitamente. Use a fórmula para explicar os termos dominantes, e depois confirme a alocação relatada pela construção exata do motor e o backend que você planeja implantar.

llama.cpp: Controle Direto da Precisão de K e V

O llama.cpp expõe opções separadas --cache-type-k e --cache-type-v em seu analisador de argumentos atual. Esta é a interface de inferência local mais útil quando você precisa trocar a precisão do cache pela capacidade de contexto, em vez de aceitar um preset global. Se você precisar primeiro da configuração de instalação e serviço ao redor, o guia do llama.cpp cobre llama-cli, llama-server e as flags principais de VRAM.

Uma configuração conservadora de 64K para usuário único parece com esta:

./llama-server \
  --model /models/model.gguf \
  --n-gpu-layers 999 \
  --ctx-size 65536 \
  --parallel 1 \
  --flash-attn on \
  --cache-type-k q8_0 \
  --cache-type-v q8_0 \
  --batch-size 1024 \
  --ubatch-size 256

A sintaxe das flags e o suporte do backend mudam rapidamente, portanto, execute llama-server --help para a construção instalada. Mais importante, inspecione o log de inicialização: ele deve mostrar o contexto pretendido, os tipos de cache, o offload para GPU e os buffers K e V alocados.

Quais Tipos de Cache do llama.cpp Tentar

Comece com Q8_0 para ambos K e V. Isso reduz aproximadamente pela metade a memória do KV em relação ao F16, e testes independentes de perplexidade em modelos de 20B ou mais (Qwen3.6-27B, Nemotron-30B) mostram que a diferença agregada de qualidade em relação ao F16 está dentro do ruído de medição — uma aposta muito menos dramática do que ir diretamente para Q4_0, que os mesmos testes mostraram colapsando a velocidade de decodificação e a precisão em contexto longo em modelos menores.

Se o Q8_0 não couber, teste chaves Q8_0 com valores Q4_0 antes de quantizar ambos os lados para Q4_0. Esta ordem tem suporte de pesquisa, não apenas folclore: estudos controlados de alocação de bits em checkpoints de Llama, Phi-4, Qwen3 e Mistral encontraram que os tensores de chave são consistentemente de dois a dez vezes mais sensíveis ao erro de quantização do que os tensores de valor, e que dar às chaves o maior orçamento de bits (por exemplo, chaves de 4 bits com valores de 2 bits) recupera até 94–98% da precisão em ponto flutuante total — enquanto a divisão invertida (chaves de 2 bits, valores de 4 bits) pode perder 30 pontos percentuais em tarefas como GSM8K. As chaves determinam quais tokens anteriores a atenção realmente corresponde, portanto, protegê-las primeiro é a escolha arquitetonicamente sólida, não apenas a que soa mais segura.

Configuração Memória Risco de qualidade Recomendação
K e V F16 Mais alta Mais baixa Linha de base quando couber
K e V Q8_0 Cerca da metade do F16 Baixa mas não zero Ponto de partida padrão para 16 GB
K Q8_0, V Q4_0 Entre Q8 e Q4 Moderado Segundo passo útil
K e V Q4_0 Cerca de um quarto do F16 Mais alta Valide na profundidade alvo

Um aviso importante para internalizar: “risco de qualidade baixo” em benchmarks agregados não significa risco zero no nível do token. Um teste controlado que manteve o Flash Attention constante e alterou apenas a precisão do KV sob decodificação gananciosa (determinística) encontrou que o cache Q8_0 alterou o texto gerado exato na grande maioria dos prompts, e o Q4_0 o alterou em praticamente todos — uma vez que um token muda, o restante da continuação pode divergir. Perplexidade e pontuações de tarefas downstream podem parecer boas em média, enquanto saídas individuais ainda diferem da linha de base F16. Se a sua aplicação precisa de reproduibilidade byte a byte (testes de regressão, respostas em cache, agentes determinísticos), trate qualquer quantização de KV como uma mudança comportamental, não apenas uma otimização de memória, e valide contra o seu próprio conjunto de prompts fixos.

O cache V quantizado pode requerer Flash Attention ou um caminho de backend compatível. Um servidor que retorna silenciosamente para outro tipo invalida o experimento, que é por que os logs de inicialização importam mais do que linhas de comando copiadas.

Contexto, Slots Paralelos e Cache Unificado

--ctx-size descreve uma capacidade do motor, não uma garantia de que cada slot paralelo receba esse número de tokens independentemente. O gerenciamento de cache evoluiu no llama.cpp, incluindo comportamento de cache unificado, portanto, teste a construção exata em vez de confiar em uma regra mais antiga que simplesmente divide o contexto pelo número de slots.

A equação de capacidade ainda sobrevive a mudanças de implementação: tokens únicos simultâneos precisam de armazenamento em algum lugar. Se duas sessões de agentes podem cada uma atingir 48K, orce para perto de 96K tokens vivos, a menos que a carga de trabalho compartilhe prefixos ou tolere evicção e recomputação.

Tamanho do Lote Não Diminui o KV Armazenado

--batch-size e --ubatch-size afetam o processamento de prompt e a memória temporária. Reduzi-los pode salvar um prefill grande de um pico de memória de ativação, mas não altera os bytes persistentes necessários para cada token retido.

Essa distinção explica um padrão de falha comum: o modelo inicia e uma requisição vazia funciona, mas um prompt de 60K falha durante a ingestão. Reduza o microlote para diagnosticar o pico transitório; reduza contexto, precisão do cache, paralelismo ou residência de pesos para alterar a capacidade persistente.

vLLM: Capacidade Paginada Ainda é Capacidade

O vLLM aborda o problema como um motor de serviço. Ele perfila a memória disponível, reserva um pool de cache KV e aloca o cache em blocos para que sequências concorrentes não necessitem cada uma de uma grande região contígua. Se você está decidindo se deve migrar para o vLLM em primeiro lugar, o guia de migração de Ollama para vLLM cobre os sinais de carga de trabalho; aqui a questão é puramente quanta capacidade o pool pode manter, e o quickstart do vLLM cobre instalação e flags gerais de serviço além das alavancas de capacidade abaixo.

O PagedAttention reduz fragmentação e desperdício em torno de comprimentos de sequência variáveis — a alocação paginada remove fragmentação, não o custo de armazenamento por token, portanto, uma única requisição única de 128K ainda precisa de blocos suficientes para seu estado KV.

O guia oficial de conservação de memória do vLLM recomenda limitar max_model_len e max_num_seqs quando a memória estiver apertada, e observa que gráficos CUDA consomem memória adicional da GPU. Em um cartão de 16 GB, ambas as configurações devem ser intencionais, não herdadas da configuração máxima do modelo.

Um servidor focado em sequência única pode começar aqui:

vllm serve MODEL_ID \
  --max-model-len 65536 \
  --max-num-seqs 1 \
  --gpu-memory-utilization 0.90 \
  --kv-cache-dtype fp8 \
  --enable-prefix-caching

Nem toda GPU de 16 GB, modelo, método de quantização ou backend de atenção suporta essa combinação exata. Trate-a como uma forma de configuração: restrinja comprimento e concorrência, reserve folga, selecione um dtype de cache suportado e valide o relatório de inicialização.

Cache KV FP8 no vLLM

A documentação atual de cache KV quantizado do vLLM suporta formatos de cache FP8 em caminhos CUDA e ROCm compatíveis. O FP8 reduz aproximadamente pela metade o armazenamento bruto do cache em relação ao BF16 ou FP16 e pode, portanto, aumentar a capacidade de tokens ou a concorrência.

A escala importa. A documentação distingue escalas padrão, cálculo de aquecimento e calibração de dataset, e recomenda calibração baseada em dataset para a maior precisão; simplesmente definir FP8 com escala 1.0 é conveniente, mas não é automaticamente a escolha de qualidade mais confiável.

Cache de Prefixo é uma Otimização de Reutilização

O cache de prefixo automático permite que uma nova requisição reutilize blocos KV para um prefixo em cache idêntico. É excelente para consultas repetidas sobre o mesmo documento longo, prompts de sistema compartilhados e conversas de múltiplas rodadas, pois evita recomputar o prefill correspondente.

Não torna uma requisição longa única menor e não acelera a geração de novos tokens. A documentação de cache de prefixo do vLLM limita explicitamente o benefício ao trabalho de prefill de prefixo compartilhado.

Utilização de Memória da GPU Não é Memória Gratuita

Aumentar --gpu-memory-utilization dá ao vLLM um alvo de reserva maior, mas não cria VRAM. Empurrá-lo muito perto de 1.0 pode deixar espaço insuficiente para a área de trabalho, outro processo, picos de ativação variáveis ou alocações não-PyTorch.

Comece em torno de 0.88 a 0.92 em uma GPU dedicada de 16 GB, inspecione o perfil e aumente apenas se a carga de trabalho permanecer estável. Se a inicialização tiver sucesso, mas prompts reais falharem, reduza tokens loteados, concorrência de sequência, captura de grafo CUDA ou o contexto máximo antes de assumir que o alocador está quebrado.

Ollama: Controles Mais Fáceis, Diagnóstico Menos Granular

O Ollama fornece deliberadamente uma superfície operacional menor. Sua documentação atual de comprimento de contexto define GPUs abaixo de 24 GiB para contexto 4K por padrão, recomenda pelo menos 64K para cargas de trabalho de agentes e codificação, e avisa que contexto maior consome mais memória.

Defina o padrão global do servidor e confirme o modelo carregado desta forma:

OLLAMA_CONTEXT_LENGTH=65536 ollama serve

ollama ps

Você também pode definir num_ctx por requisição ou modelo. ollama ps é importante porque suas colunas PROCESSOR e CONTEXT revelam se o modelo permaneceu totalmente na GPU e se o contexto solicitado foi realmente alocado. Fique ciente de que o comportamento de agendamento por trás desses números mudou entre versões do Ollama; minha comparação da alocação de memória no Ollama v0.12.1 mostra o novo agendador empurrando alguns modelos mais para a CPU em um cartão de 16 GB, portanto, fixe a versão que você mediu.

Cache KV Quantizado no Ollama

O Ollama expõe OLLAMA_KV_CACHE_TYPE com escolhas f16, q8_0 e q4_0 em sua FAQ atual. KV quantizado requer Flash Attention, que o Ollama usa automaticamente em backends suportados ou pode ser solicitado com OLLAMA_FLASH_ATTENTION=1.

Um serviço de contexto longo de 16 GB pode, portanto, ser iniciado como:

OLLAMA_CONTEXT_LENGTH=65536 \
OLLAMA_FLASH_ATTENTION=1 \
OLLAMA_KV_CACHE_TYPE=q8_0 \
OLLAMA_NUM_PARALLEL=1 \
ollama serve

Q8_0 é a alternativa recomendada pelo Ollama ao F16. A FAQ avisa que Q4_0 pode produzir uma perda de qualidade mais perceptível, especialmente em contexto mais alto, portanto, deve ser um fallback medido, não um preset automático de 16 GB.

Paralelismo do Ollama Multiplica o Orçamento de Contexto

O Ollama documenta uma regra particularmente clara: a memória necessária escala com OLLAMA_NUM_PARALLEL * OLLAMA_CONTEXT_LENGTH. Quatro requisições paralelas com configuração de 32K podem implicar uma alocação agregada de contexto de 128K para esse modelo.

Para um agente pessoal em 16 GB, mantenha OLLAMA_NUM_PARALLEL=1 até que uma sessão longa esteja estável. Colocar uma segunda requisição na fila é geralmente preferível a empurrar o primeiro modelo parcialmente para a CPU e tornar ambas as requisições lentas. As mecânicas de fila, 503 e descarregamento de modelo por trás dessa escolha estão documentadas em como o Ollama lida com requisições paralelas.

Offload para CPU: Uma Saída Válida com um Preço

Mover algumas camadas do modelo ou estado KV para a memória do sistema pode transformar uma falha de alocação em um processo funcional. Isso também coloca a largura de banda PCIe e a latência da memória do host no caminho de decodificação, onde cada token gerado pode pagar o custo. A evidência de pista e geração de quando o PCIe realmente afeta está em Desempenho de LLM e Pistas PCIe.

Offload pode ser sensato para trabalho de lote ocasional, mas raramente é o melhor padrão para um agente de codificação interativo. Primeiro compare uma quantização de pesos menor, KV Q8, concorrência reduzida e um limite de contexto realista; use offload quando a capacidade importar mais do que a latência.

Fique atento ao penhasco, não à média. Um servidor pode decodificar rapidamente em 8K, e depois desacelerar severamente após parte do conjunto de trabalho transbordar, portanto, faça benchmarks em 32K, 64K e o máximo pretendido, em vez de relatar apenas uma taxa de tokens de contexto vazio.

Caches de Janela Deslizante e KV Adaptativo

A atenção de janela deslizante altera o orçamento retendo apenas uma janela recente para camadas selecionadas. Modelos híbridos podem combinar essas camadas com atenção global ocasional ou estado recorrente, tornando um cálculo plano de contexto completo uma superestimativa substancial ou mal posicionado de memória.

A otimização é parte da arquitetura do modelo, não um interruptor genérico que pode ser aplicado sem consequências. Um motor deve entender o padrão de camadas, regras de evicção, posições e quaisquer tokens globais corretamente.

O Que o KV Adaptativo Tenta Melhorar

Forks experimentais vão além escolhendo a precisão ou layout do cache por camadas e profundidade de contexto. O objetivo é atraente: preservar maior precisão onde importa, comprimir camadas menos sensíveis e mudar a mistura antes que a pressão de VRAM cause um transbordo duro — a mesma descoberta de sensibilidade da chave sobre a do valor descrita acima é exatamente o tipo de sinal que um alocador adaptativo gostaria de explorar automaticamente, em vez de deixar para o ajuste manual --cache-type-k/--cache-type-v.

Um projeto downstream de agosto de 2026, llama.cpp-adaptive-turboquant, relata um seletor automático para vários modos de camadas adaptativas e publica testes de profundidade longa em uma RTX 5080 16 GB. Esses números são resultados reportados pelo autor de um fork especializado, não evidência de que o llama.cpp upstream se comporte da mesma forma.

Por Que Ainda é Experimental

O fork combina tipos de cache personalizados, kernels CUDA, caminhos específicos do modelo e restrições de toolchain. Isso é muito mais código em que confiar do que alternar o armazenamento de cache upstream de F16 para Q8_0.

Use um fork assim apenas quando o upstream não puder atender a um requisito real e você puder reproduzir qualidade, estabilidade e velocidade no seu modelo. Registre o commit e a versão CUDA, porque um resultado ligado apenas a um nome de projeto não é reproduzível.

Um Teste Justo de Cache Adaptativo

Compare o fork contra uma linha de base Q8_0 upstream com o mesmo GGUF, prompt, sampler, profundidade de contexto e comprimento de saída. Meça a VRAM de inicialização, VRAM de pico de prefill, velocidade de processamento de prompt, velocidade de decodificação e uma tarefa de qualidade que realmente exija evidência da parte mais antiga do contexto.

Não aceite uma alocação bem-sucedida como um resultado completo. Um cache pode caber 128K e ainda assim perder fatos iniciais, corromper a saída no final da sequência ou decodificar lentamente demais para ser útil.

Um Procedimento de Ajuste de 16 GB Trabalho: Uma Variável de Cada Vez

O caminho mais rápido para uma configuração estável é alterar uma dimensão de memória de cada vez. Alterar aleatoriamente tipo de cache, tamanho do lote, offload de camadas, paralelismo e contexto juntos produz um comando funcional sem explicação.

flowchart TD A["Passo 1: carregar em contexto 8K, uma sequência, registrar VRAM de aquecimento"] --> B{"Pesos + runtime abaixo de aprox. 14.5 GiB?"} B -- "não" --> C["Escolher uma quant ou modelo menor, repetir Passo 1"] C --> A B -- "sim" --> D["Passo 2: linha de base de qualidade KV F16/BF16, salvar saídas de tarefas"] D --> E["Passo 3: alternar para KV Q8_0 / FP8 com Flash Attention"] E --> F["Passo 4: aumentar contexto em estágios - 32K, 64K, 96K, 128K"] F --> G{"Falha durante prefill?"} G -- "sim" --> H["Passo 5: reduzir tamanho do lote / microlote"] H --> F G -- "não" --> I{"Falha apenas com requisições concorrentes?"} I -- "sim" --> J["Passo 5: reduzir slots paralelos / max-num-seqs"] J --> F I -- "não" --> K["Apenas agora: misto Q8/Q4, Q4 completo, offload, fork adaptativo"]

Passo 1: Estabelecer o Piso de Pesos

Carregue o modelo em contexto 8K, uma sequência e o offload de GPU pretendido. Registre a VRAM do processo após o aquecimento e verifique se nenhuma camada se moveu inesperadamente para a CPU.

Se os pesos e o runtime já consumirem mais de aproximadamente 14.5 a 15 GiB, o contexto longo não tem margem saudável. Escolha uma quant ou modelo de pesos menor antes de ajustar o cache.

Passo 2: Medir KV F16 ou BF16 Como Linha de Base de Qualidade

Execute o menor contexto que suporte seu teste e retenha o cache de alta precisão padrão. Salve saídas de tarefas de recuperação, edição de código, seleção de ferramentas e instruções longas.

Esta linha de base diz se erros posteriores vêm da quantização do cache. Sem ela, um problema de template de chat ou um modelo fraco pode ser facilmente culpado pelo KV Q4.

Passo 3: Passar para Q8 ou FP8

Ative Flash Attention onde necessário, selecione Q8_0 no llama.cpp ou Ollama, ou um modo FP8 suportado no vLLM. Repita os mesmos prompts nas mesmas profundidades de tokens e confirme que o log mostra o tipo de cache pretendido.

Para muitos deploys de 16 GB, este é o ponto de parada útil. Ele dobra aproximadamente a capacidade bruta do KV sem tornar a compressão do cache a quantização mais agressiva no stack.

Passo 4: Aumentar Contexto em Estágios

Teste 32K, 64K, 96K e 128K em vez de pular diretamente para o máximo anunciado. Em cada estágio, registre tokens por segundo de processamento de prompt, tokens por segundo de decodificação, VRAM de pico e se evidência perto do início ainda pode ser recuperada.

A decodificação de contexto longo frequentemente desacelera mesmo após a memória caber, porque a atenção lê mais estado em cache. Capacidade e desempenho são eixos separados.

Passo 5: Ajustar Memória Transitória

Se a falha ocorrer durante o prefill em vez da inicialização, reduza o microlote ou os tokens máximo loteados. Se a falha ocorrer apenas com requisições simultâneas, reduza a concorrência de sequência ou slots paralelos.

Apenas depois que esses controles forem compreendidos, você deve tentar cache misto Q8/Q4, cache Q4 completo, offload para CPU ou um fork adaptativo. Mantenha a execução Q8 upstream como a linha de base de comparação. Se você adicionar decodificação especulativa ou MTP depois, lembre-se de que seus buffers de rascunho são outra linha na equação de orçamento, não velocidade gratuita — o guia de decodificação especulativa cobre as mecânicas e seu custo de VRAM, e meu benchmark de Qwen 3.6 27B e 35B MTP vs Padrão mostra exatamente quanta capacidade o estado extra de uma cabeça MTP pode custar em um cartão de 16 GB.

O Que Registrar em um Benchmark de Contexto Longo

Uma única figura de tokens/s esconde exatamente o problema que este artigo está tentando resolver. Testes de contexto longo devem preservar detalhes suficientes para que outro operador possa reproduzir a fronteira de memória.

Campo Por que importa
GPU e VRAM utilizável Uso de área de trabalho e outros processos mudam o orçamento
Versão ou commit do motor Comportamento do cache e flags evoluem rapidamente
Versão do driver, CUDA, ROCm ou Vulkan Determina comportamento do backend e kernel
Modelo exato e quant de pesos Define residência de pesos e arquitetura
Tipos de cache K e V Define tamanho do cache persistente e risco de qualidade
Capacidade de contexto e profundidade do prompt Alocação não é a mesma que profundidade real
Sequências paralelas Multiplica ou compartilha demanda de cache
Lote e microlote Afeta picos de prefill e velocidade
Velocidade de processamento de prompt Expõe usabilidade de prefill longo
Velocidade de decodificação em cada profundidade Expõe desaceleração de largura de banda do cache
VRAM de pico e offload para CPU Distingue encaixe de transbordo
Resultado de qualidade de contexto longo Detecta falhas de compressão ou posição

Use amostragem de nvidia-smi ou a ferramenta equivalente do fabricante durante o prefill e a decodificação. O relatório de alocação do motor é necessário, mas a memória de pico do dispositivo durante um prompt real é o número que decide a estabilidade.

Erros Comuns de Cache KV em GPUs de 16 GB

Tratando Suporte a 128K Como uma Promessa de Hardware

O campo de contexto em uma configuração de modelo é um limite arquitetônico. Não diz nada sobre a memória restante após carregar uma quantização particular em um motor particular.

Calcule o cache e verifique o runtime. Contexto de tamanho de marketing sem um orçamento de VRAM é meramente um OOM adiado até o primeiro prompt sério.

Quantizando Pesos Mas Esquecendo KV

Um GGUF de 4 bits reduz pesos do modelo, não um cache KV F16. Em contexto longo, o cache pode apagar toda a economia e eventualmente exceder a pegada dos pesos.

Relate ambas as quantizações. Modelo Q4_K_M, KV Q8_0 é significativo; modelo de 4 bits está incompleto.

Assumindo que Atenção Paginada Comprime Tokens

A paginação melhora o comportamento de alocação e compartilhamento. Não altera a precisão do tensor nem remove o estado KV necessário por uma sequência única.

Use alocação paginada para servir cargas de trabalho variáveis eficientemente. Use precisão do cache, arquitetura do modelo, limites de contexto e limites de concorrência para controlar a capacidade.

Assumindo que Cache de Prefixo Ajuda Todo Prompt Longo

O cache de prefixo economiza computação de prefill repetida quando requisições compartilham um prefixo exato. Um despejo de repositório único de 100K não recebe um desconto mágico de memória apenas porque o cache de prefixo está habilitado.

É uma otimização de carga de trabalho, não um substituto para a equação de orçamento. Meça a taxa de acerto e a pressão do cache retido no serviço multiusuário.

Usando KV Q4 Sem um Teste de Qualidade

Cache de baixo bit pode falhar sutilmente. O modelo ainda escreve texto fluente, mas a atenção sobre evidência distante, nomes exatos, argumentos de ferramentas ou dependências de código pode se degradar — e como a pesquisa de divergência de tokens acima mostra, mesmo a configuração “segura” Q8_0 não é garantida a reproduzir a saída exata F16 sob decodificação determinística, apenas a preservar precisão em agregado.

Teste a tarefa alvo na profundidade alvo. Benchmarks de chat curtos são quase inúteis para validar um cache de contexto longo.

Deixando Paralelismo em Automático

Um motor pode escolher concorrência que é sensata para throughput, mas impossível para seu alvo de contexto longo. Em 16 GB, uma sequência profunda e várias sequências curtas são cargas de trabalho fundamentalmente diferentes.

Defina o limite explicitamente, e depois aumente com tráfego medido. Caso contrário, uma segunda requisição pode transformar uma configuração estável de 64K em uma surpresa de alocação ou latência.

Perfis Recomendados de 16 GB

Esses perfis são posições de partida, não presets universais. Um modelo com geometria KV incomum — em particular, um design MLA ou híbrido de janela deslizante — pode ser muito mais barato ou mais caro do que o exemplo GQA convencional.

Agente de Codificação Interativo

Use uma sequência, contexto de 48K a 64K, cache Q8, Flash Attention e residência total de pesos na GPU, se possível. Este perfil favorece latência previsível e boa precisão de cache em vez de um máximo impressionante, mas raramente útil.

Ative reutilização de prefixo quando o motor suportar, porque voltas de codificação frequentemente compartilham um prefixo grande de repositório ou conversa. Ainda assim, compacte saída de ferramentas e transcrições antigas; engenharia de cache não torna tokens irrelevantes valiosos.

Análise de Documento Longo

Use um modelo menor com capacidade de 64K a 128K, cache Q8 ou FP8 calibrado e cache de prefixo repetido quando múltiplas perguntas alvejarem o mesmo documento. Meça o tempo para o primeiro token, porque o prefill pode dominar mesmo quando a decodificação permanece aceitável.

Se apenas uma pergunta será feita, recuperação ou sumário em blocos pode ser mais rápido e confiável do que forçar o corpus inteiro através de um cartão de 16 GB. Contexto longo é uma ferramenta, não um substituto para a arquitetura de informação.

Pequeno Servidor Multiusuário

Limite o contexto por requisição e as sequências ativas totais, em vez de anunciar o máximo do modelo para cada cliente. A alocação paginada do vLLM é útil aqui, enquanto Ollama e llama.cpp também requerem atenção explícita aos tokens vivos agregados.

Prefira fila a transbordo descontrolado. Uma política de admissão mais lenta é menos danosa do que todas as requisições cruzarem o PCIe durante a decodificação de repente.

Recomendação Final para Contexto Longo em 16 GB

Para contexto longo em 16 GB, KV Q8 e uma sequência ativa são a linha de base certa. Eles expõem o limite real sem fazer qualidade de cache de baixo bit, alocação paralela e latência de offload falharem ao mesmo tempo.

Calcule a partir da geometria de atenção, subtraia pesos e sobrecarga de runtime, e depois confirme o resultado nos logs do motor e medições de memória de pico. Se 128K ainda não couber, um modelo menor é frequentemente a otimização mais limpa; se couber, mas rastejar, reduzir o contexto é frequentemente a opção honesta.

Atenção paginada, cache de prefixo, janelas deslizantes e precisão adaptativa resolvem problemas úteis, mas diferentes. A configuração vencedora é aquela que permanece na GPU, recupera evidência antiga corretamente e sustenta velocidade de decodificação aceitável na profundidade de contexto que você realmente usa.

Referências

Subscrever

Receba novos artigos sobre sistemas, infraestrutura e engenharia de IA.