KV Cache em GPUs de 16 GB: Tornando o Contexto Longo Realmente Viável

Por que a contextualização 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 arquitetural nunca prometeu que pesos, cache de KV, buffers de computação e o compositor do ambiente gráfico caberiam no seu cartão ao mesmo tempo.

O cache de KV é geralmente onde os planos de longo contexto encontram esse limite físico. Ele cresce com cada token ativo e sequência, de modo que uma configuração que parece confortável na inicialização pode se tornar abruptamente lenta, 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 reprodutíveis de tamanhos de 32K a 128K e configurações funcionais para --cache-type-k e --cache-type-v do llama.cpp, cache paginada e de prefixo do vLLM e controles de contexto do Ollama — além dos forks experimentais de cache adaptativa que merecem interesse, mas não confiança cega. Para o contexto mais amplo de vazão, 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.

Alvo 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 de 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 Somente o cache pode consumir a maior parte da VRAM
Duas sessões concorrentes de 64K Tratar como um orçamento de cache de cerca de 128K Capacidade paralela é confundida com vazão gratuita

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 memória insuficiente. 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 (key) e valor (value) para cada token processado. O runtime retém esses tensores para que o próximo token possa prestar atenção aos tokens anteriores sem recomputar o prefixo inteiro.

O cache economiza 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 (grouped-query attention), uma linha de base útil é:

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

O fator dois representa chaves e valores. A atenção multi-cabeça usa tantas cabeças de KV quanto cabeças de consulta, a atenção de consulta agrupada usa menos cabeças de 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 de KV, enquanto outro pode usar menos cabeças de KV, camadas de KV compartilhadas, atenção com janela deslizante ou estados latentes comprimidos.

A contagem de parâmetros prevê principalmente a memória de 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 é de quão longe o design de atenção avançou além da atenção multi-cabeça pura (MHA):

  • Atenção de Consulta Única (MQA) compartilha uma única cabeça de 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 nos modelos de fronteira atuais.
  • Atenção de Consulta Agrupada (GQA) agrupa cabeças de consulta em clusters que cada um compartilha uma cabeça de 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, toma uma abordagem inteiramente diferente: em vez de compartilhar K/V entre cabeças, projeta chaves e valores em um vetor latente de baixo rango comprimido e reconstrói o K/V de resolução completa sob demanda no momento da atenção. O DeepSeek relatou uma redução de cache KV de aproximadamente 93% em comparação com um modelo MHA denso de tamanho equivalente, mantendo a qualidade competitiva com — às vezes à frente de — a GQA no 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 em uma ordem de magnitude 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 com 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 de KV e comprimento de contexto, quando 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

Esses 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 são os tokens atualmente retidos para uma sequência.

Elevar uma flag do engine não pode estender com segurança um modelo além do seu esquema de posição suportado. O escalonamento de 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 32K, 64K e 128K

Considere um modelo GQA representativo com 32 camadas, oito cabeças de 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ário e os tamanhos de bloco físico 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 Aprox. bytes 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
Q8_0 K e Q4_0 V 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 para desktop e driver

orçamento de 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 um display, não planeje considerando que todos os 16 GiB estejam disponíveis. Reserve pelo menos algumas centenas de MiB para o desktop e o driver, e então deixe outra margem para buffers dependentes da carga de trabalho; 1,0 a 1,5 GiB de folga total é uma suposição inicial 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, de modo que o modelo representativo cabe em aproximadamente 45K tokens com Q8_0 ou 87K com Q4_0, antes da sobrecarga específica do engine.

Isso não faz automaticamente Q4_0 a escolha certa. Se a precisão em contexto longo cair na sua carga de trabalho, um modelo menor ou mais agressivamente quantizado com cache Q8_0 pode ser melhor do que pesos maiores pareados com um cache frágil. Âncoras medidas para exatamente essa aritmética estão nas tabelas de benchmark do llama.cpp para 16 GB VRAM, onde a VRAM por modelo é registrada em 19K, 32K e 64K de contexto. Para uma investigação 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 16GB de VRAM.

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

O trecho Python a seguir estima um cache GQA de atenção completa convencional. Substitua a geometria por valores da configuração do modelo ou 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 de Q8_0 e Q4_0 incluem metadados de bloco simples, motivo pelo qual elas são ligeiramente maiores do 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 para a equação GQA convencional. Camadas com janela deslizante retêm apenas uma janela recente, camadas de 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 — sendo o caso de MLA acima o exemplo mais dramático.

Engines modernos gerenciam cada vez mais esses layouts mistos explicitamente. Use a fórmula para explicar os termos dominantes, e então confirme a alocação relatada pela build e backend exatos do engine 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 parser de argumentos atual. Esta é a interface local de inferência mais útil quando você precisa trocar a precisão do cache pela capacidade de contexto, em vez de aceitar um predefinido global. Se você precisar primeiro do setup de instalação e serve circundante, 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 assim:

./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, então execute llama-server --help para a build instalada. Mais importante, inspecione o log de inicialização: ele deve mostrar o contexto pretendido, os tipos de cache, o offload da GPU e os buffers de 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 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, o que os mesmos testes mostraram colapsando a velocidade de decodificação e a precisão em contexto longo em modelos menores.

Se Q8_0 não couber, teste chaves Q8_0 com valores Q4_0 antes de quantizar ambos os lados para Q4_0. Essa ordem tem respaldo de pesquisa, não apenas folclore: estudos controlados de alocação de bits em checkpoints Llama, Phi-4, Qwen3 e Mistral encontraram que os tensores de chave são consistentemente duas a dez vezes mais sensíveis ao erro de quantização do que os tensores de valor, e que dar às chaves o orçamento de bits maior (por exemplo, chaves de 4 bits com valores de 2 bits) recupera de 94–98% da precisão de precisão 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 a quais tokens anteriores a atenção realmente corresponde, então 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 em F16 Maior Menor Linha de base quando couber
K e V em Q8_0 Cerca da metade do F16 Baixo, 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 em Q4_0 Cerca de um quarto do F16 Maior Valide na profundidade alvo

Uma ressalva que vale a pena 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 apenas alterou a precisão do KV sob decodificação gulosa (determinística) encontrou que o cache Q8_0 alterou o texto exato gerado 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. Pontuações de perplexidade e tarefas downstream podem parecer finas em média, enquanto saídas individuais ainda diferem da linha de base F16. Se a sua aplicação precisar de reprodutibilidade 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 fixo.

Cache V quantizado pode requerer Flash Attention ou um caminho de backend compatível. Um servidor que silenciosamente faz fallback para outro tipo invalida o experimento, motivo pelo qual os logs de inicialização importam mais do que linhas de comando copiadas.

Contexto, Slots Paralelos e Cache Unificada

--ctx-size descreve uma capacidade do engine, não uma garantia de que cada slot paralelo receba esse número de tokens independentemente. A gerência de cache evoluiu no llama.cpp, incluindo comportamento de cache unificada, então teste a build 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 agente podem cada atingir 48K, faça um orçamento para perto de 96K tokens ao vivo, a menos que a carga de trabalho compartilhe prefixos ou tolere evicção e recomputação.

Tamanho de Lote Não Reduz o KV Armazenado

--batch-size e --ubatch-size afetam o processamento do prompt e a memória temporária. Reduzi-los pode salvar um grande prefill 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 micro-lote 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 engine de serve. Ele perfila a memória disponível, reserva um pool de cache KV e aloca cache em blocos, para que sequências concorrentes não precisem 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 de quanto cache o pool pode segurar, e o início rápido do vLLM cobre instalação e flags gerais de serve além das alavancas de capacidade abaixo.

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, então uma requisição única de 128K ainda precisa de blocos suficientes para seu estado KV.

O guia oficial de economia de memória do vLLM recomenda limitar max_model_len e max_num_seqs quando a memória está apertada, e observa que grafos CUDA consomem memória GPU adicional. Em um cartão de 16 GB, ambos os ajustes devem ser intencionais em vez de herdados da configuração máxima de um 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 isso 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. FP8 reduz aproximadamente pela metade o armazenamento bruto de cache em relação ao BF16 ou FP16 e portanto pode aumentar a capacidade de tokens ou a concorrência.

Escalonamento importa. A documentação distingue escalas padrão, cálculo de aquecimento e calibração baseada em conjunto de dados, e recomenda calibração baseada em conjunto de dados 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 Reuso

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

Ela 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 GPU Não é Memória Gratuita

Elevando --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 o display, outro processo, picos de ativação variáveis ou alocações não-PyTorch.

Comece ao redor 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 tem sucesso, mas prompts reais falham, reduza tokens loteados, concorrência de sequência, captura de grafo CUDA ou o contexto máximo antes de assumir que 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 com 4K de contexto por padrão, recomenda pelo menos 64K para cargas de trabalho de agente e codificação, e adverte que contexto maior consome mais memória.

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

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. Esteja ciente de que o comportamento de agendamento por trás desses números mudou entre versões do Ollama; minha comparação de alocação de memória do Ollama v0.12.1 mostra o novo agendador empurrando alguns modelos mais para a CPU em um cartão de 16 GB, então fixe a versão que você mediu.

Cache KV Quantizada no Ollama

O Ollama expõe OLLAMA_KV_CACHE_TYPE com escolhas f16, q8_0 e q4_0 em sua FAQ atual. KV quantizada 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 do Ollama ao F16. A FAQ adverte que Q4_0 pode produzir uma perda de qualidade mais notável, especialmente em contexto mais alto, então deve ser um fallback medido em vez de 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 em uma configuração de 32K podem implicar uma alocação agregada de contexto de 128K para aquele 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. A mecânica de filas, 503 e descarregamento de modelo por trás dessa escolha está documentada em como o Ollama lida com requisições paralelas.

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

Mover algumas camadas do modelo ou estado KV para a RAM 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. O corredor e as evidências de geração de quando o PCIe realmente incomoda estão em Desempenho de LLMs e Linhas PCIe.

Offload pode ser sensato para trabalho em 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.

Observe o penhasco em vez da média. Um servidor pode decodificar rapidamente em 8K, e então retardar severamente após parte do conjunto de trabalho transbordar, então faça benchmark em 32K, 64K e o máximo pretendido, em vez de relatar apenas uma taxa de tokens com contexto vazio.

Caches KV de Janela Deslizante e Adaptativa

A atenção com janela deslizante altera o orçamento retendo apenas uma janela recente para camadas selecionadas. Modelos híbridos podem combinar essas camadas com atenção ocasional global ou estado recorrente, fazendo com que um cálculo plano de contexto completo superestime substancialmente ou coloque a memória no lugar errado.

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

O Que Caches KV Adaptativas Tentam Melhorar

Forks experimentais vão mais longe, escolhendo precisão ou layout de cache por camada e profundidade de contexto. O objetivo é atraente: preservar maior precisão onde importa, comprimir camadas menos sensíveis e alterar a mistura antes que a pressão de VRAM cause um transbordo duro — a mesma descoberta de sensibilidade de chaves sobre sensibilidade de valores descrita acima é exatamente o tipo de sinal que um alocador adaptativo gostaria de explorar automaticamente em vez de deixá-lo para ajuste manual de --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 relatados 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 de modelo e restrições de ferramenta. Isso é muito mais código para confiar do que trocar o armazenamento de cache upstream de F16 para Q8_0.

Use tal fork 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 do CUDA, porque um resultado associado apenas a um nome de projeto não é reprodutível.

Um Teste Justo de Cache Adaptativa

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

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

Um Procedimento de Ajuste de 16 GB Trabalhado: Uma Variável por Vez

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

flowchart TD A["Passo 1: carregar em 8K de contexto, uma sequência, registrar VRAM de aquecimento"] --> B{"Pesos + runtime abaixo de cerca de 14,5 GiB?"} B -- "não" --> C["Escolha uma quant ou modelo menor, repita o 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: mudar para KV Q8_0 / FP8 com Flash Attention"] E --> F["Passo 4: estagiar contexto para cima - 32K, 64K, 96K, 128K"] F --> G{"Falha durante prefill?"} G -- "sim" --> H["Passo 5: reduzir tamanho de lote / ubatch"] 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 8K de contexto, uma sequência e o offload para 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á consomem mais do que cerca de 14,5 a 15 GiB, o contexto longo não tem margem saudável. Escolha uma quant de pesos ou modelo 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 ao KV Q4.

Passo 3: Mover 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 deployments de 16 GB, este é o ponto de parada útil. Ele praticamente dobra a capacidade bruta de KV sem tornar a compressão do cache a quantização mais agressiva na pilha.

Passo 4: Elevar 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 retarda 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 micro-lote ou os tokens loteados máximos. 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 entendidos, você deve tentar cache mista Q8/Q4, cache Q4 completa, offload para CPU ou um fork adaptativo. Mantenha a execução Q8 upstream como linha de base de comparação. Se você adicionar posteriormente decodificação especulativa ou MTP, 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 MTP vs Padrão para Qwen 3.6 27B e 35B mostra exatamente quanto contexto 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

Um único número 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 o limite de memória.

Campo Por que importa
GPU e VRAM utilizável Uso de display e outros processos alteram o orçamento
Versão ou commit do engine Comportamento de cache e flags evoluem rapidamente
Versão do driver, CUDA, ROCm ou Vulkan Determina backend e comportamento de 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 coisa que profundidade real
Sequências paralelas Multiplica ou compartilha demanda de cache
Lote e micro-lote Afeta picos de prefill e velocidade
Velocidade de processamento de prompt Expõe utilidade de prefill longo
Velocidade de decodificação em cada profundidade Expõe lentidão de largura de banda de cache
VRAM de pico e offload para CPU Distingue cabimento 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 vendor durante ambos prefill e decodificação. O relatório de alocação do engine é necessário, mas a memória de dispositivo de pico durante um prompt real é o número que decide a estabilidade.

Erros Comuns de Cache KV em GPUs de 16 GB

Tratar Suporte a 128K como Promessa de Hardware

O campo de contexto em uma configuração de modelo é um teto arquitetural. Ele não diz nada sobre a memória restante após carregar uma quantização particular em um engine particular.

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

Quantizar Pesos mas Esquecer 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 de pesos.

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

Assumir que Atenção Paginada Comprime Tokens

Paginação melhora comportamento de alocação e compartilhamento. Não altera a precisão dos tensores 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 de cache, arquitetura do modelo, limites de contexto e limites de concorrência para controlar a capacidade.

Assumir que Cache de Prefixo Ajuda Todo Prompt Longo

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

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

Usar KV Q4 Sem 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 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.

Deixar Paralelismo em Automático

Um engine pode escolher uma concorrência sensata para vazão, 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 então aumente-o 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 para 16 GB

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

Agente de Codificação Interativo

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

Ative reuso de prefixo quando o engine o suportar, porque turnos de codificação frequentemente compartilham um grande prefixo 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 visam o mesmo documento. Meça tempo até 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 sumarização em blocos pode ser mais rápida 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 arquitetura de informação.

Pequeno Servidor Multiusuário

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

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

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 com que qualidade de cache de baixo bit, alocação paralela e latência de offload falhem ao mesmo tempo.

Calcule a partir da geometria de atenção, subtraia pesos e sobrecarga de runtime, e então confirme o resultado nos logs do engine 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 rasteja, reduzir o contexto é frequentemente a escolha honesta.

Atenção paginada, cache de prefixo, janelas deslizantes e precisão adaptativa resolvem problemas úteis, mas diferentes. O setup vencedor é o 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.