Capítulo 8 — Gerenciamento de KV Cache de Próxima Geração
Oitavo post do passeio capítulo a capítulo pelo LLM Primer VI: Escalando Sistemas de IA. O capítulo que traz para dentro do motor de inferência a intuição de paginação do sistema operacional — e transforma o KV cache de uma laje de bytes reservados em um recurso compartilhado, com evicção e cacheamento de prefixo.
Por que este capítulo existe
O Capítulo 7 deixou o batching contínuo carregando uma dívida. Sequências entram e saem a cada iteração; um layout ingênuo dá a cada slot sua própria laje do tamanho máximo, a maior parte da qual é desperdiçada; parte do ganho de throughput do batching é devolvida. A dívida é a fragmentação interna, exatamente o modo de falha que os sistemas operacionais resolveram com paginação nos anos 1960. O Capítulo 8 é sobre aplicar essa solução ao serving de LLM: dividir o KV cache em pequenos blocos físicos, desacoplá-los das posições lógicas de token com uma tabela de páginas, e deixar políticas de evicção e cacheamento reclamarem ou compartilharem blocos entre sequências. O PagedAttention é o movimento fundacional; H2O e InfiniGen são as políticas de evicção; prefix caching é a técnica que permite a um cluster de produção servir milhões de requisições agênticas em uma dúzia de GPUs.
8.1 PagedAttention é memória virtual para o KV cache
O artigo do PagedAttention do vLLM (2023) leva a intuição do sistema operacional diretamente para dentro do motor. O KV cache é dividido em blocos de tamanho fixo — tipicamente 16 tokens cada — mantidos em um pool físico plano. Uma sequência é representada por uma tabela de blocos: um array de ponteiros que mapeia posições lógicas para IDs de blocos físicos. O kernel de atenção recebe a tabela de blocos como entrada adicional e coleta chaves e valores por indireção em vez de por slicing contíguo; na Hopper o L2 absorve o padrão de acesso aleatório bem o suficiente para que o kernel rode a poucos porcento da versão baseada em laje. Os ganhos são grandes. A fragmentação interna cai de 60 a 80% para cerca de 4% (um bloco final parcial por sequência), o que eleva a concorrência disponível 2 a 4 vezes. O compartilhamento de blocos com contagem de referência torna a amostragem complexa quase gratuita — best-of-8 em um prompt de 2.000 tokens cai de 16.800 para 2.800 blocos de token — e é o substrato sobre o qual o prefix caching é construído.
8.2 H2O e InfiniGen expulsam os tokens que não importam
O PagedAttention resolve a fragmentação mas não resolve contextos que crescem além da VRAM em qualquer layout. Um contexto de 200 mil tokens em Llama-3-70B precisa de 60 GB de KV ao lado dos pesos. O H2O ("Heavy Hitter Oracle") observa que os pesos de atenção durante a decodificação se concentram em um pequeno conjunto de posições de origem — tokens de recência, tokens de attention-sink no início e um conjunto esparso de acertos relevantes de conteúdo — enquanto a maioria das posições históricas recebe peso essencialmente zero. O motor mantém um score de atenção acumulado por posição; quando o orçamento de KV da sequência se aproxima do teto, ele evita as posições com menor score, exceto por uma janela garantida de tokens recentes e de sink. A economia é grande; o custo é a permanência — se uma consulta posterior precisar de uma posição que foi despejada, o motor não pode recuperá-la. O InfiniGen refina a troca com seleção dinâmica e recuperável: em vez de descartar tokens completamente, ele descarrega seu KV para a memória da CPU e os traz de volta paginados se a atenção começar a se concentrar neles novamente. A política de evicção certa depende de quanto uma carga de trabalho reconsulta sua própria história longa; cargas agênticas punem a evicção permanente e recompensam a recuperação estilo InfiniGen.
8.3 O prefix caching é a alavanca de maior impacto que o PagedAttention desbloqueia
No tráfego real, os primeiros milhares de tokens da maior parte dos prompts são idênticos entre requisições. Um serviço de chat reutiliza o mesmo system prompt em toda conversa. Um serviço RAG cola as mesmas passagens recuperadas no prompt de cada usuário que fez a mesma busca. Um agente injeta as mesmas descrições de ferramentas e andaimes de raciocínio a cada passo. O PagedAttention torna o compartilhamento mecânico: hashear o prompt em pedaços do tamanho do bloco; se o hash está no cache global de blocos já preenchidos por computação, apontar a tabela de blocos da nova requisição para o bloco cacheado e pular inteiramente o prefill daquele prefixo; se não, o prefill roda e o bloco resultante é registrado. As taxas de acerto em produção são dramáticas — acima de 99% para o system prompt de um serviço de chat, 30 a 60% para os prefixos dependentes de recuperação em RAG, próximas de 1,0 para um andaime agêntico. O RadixAttention do SGLang leva a ideia adiante com uma árvore radix que indexa prefixos compartilhados de qualquer comprimento, não apenas alinhados a bloco. O prefix caching é a única técnica que mais frequentemente vira um cluster de serving fora do orçamento em um cluster dentro do orçamento.
O que o Capítulo 8 prepara
Paginação, evicção e prefix caching encolheram a pegada de KV por token e tornaram o comportamento de memória do motor tratável em alta concorrência. O que nenhuma delas resolve é a dependência sequencial fundamental da decodificação: um token de saída por iteração, por sequência, não importa quantos slots estejam ativos. O Capítulo 9 ataca essa restrição com a decodificação especulativa — a família de técnicas que prevê vários tokens à frente com um rascunho barato e verifica o palpite em um único forward pass caro, quebrando o piso de um-token-por-passo para as sequências que mais importam para a latência percebida pelo usuário.
Próximo — Capítulo 9: Decodificação Especulativa. Rascunhar, verificar e a aritmética de quando a especulação compensa.