Computação e Sistemas

Visão geral

“Compute & Systems” aborda as ideias de hardware e de nível de sistema que determinam quão rápido um modelo de IA executa, quanto ele custa e quais trade-offs de engenharia você enfrenta no treinamento e na implantação. Para o aprendizado profundo (deep learning) moderno—especialmente modelos de Arquitetura Transformer—os gargalos raramente são “apenas mais FLOPs”. O desempenho real vem de equilibrar:

  • Vazão de computação (quantas operações por segundo seu acelerador consegue executar)
  • Capacidade de memória (o modelo e suas ativações cabem?)
  • Largura de banda de memória (quão rápido os dados se movem entre memória e unidades de computação)
  • Largura de banda/latência do interconector (quão rápido múltiplos dispositivos se comunicam)
  • Eficiência de software (kernels, compiladores, estratégias de paralelismo, stack de serving)
  • Utilização (manter hardware caro ocupado em vez de esperando)

Essas restrições moldam tanto o treinamento (dias–semanas de grandes clusters) quanto a inferência (metas de latência/vazão, custo por requisição, latência de cauda, e confiabilidade).

Conceitos centrais de desempenho

FLOPs, vazão e utilização

  • FLOPs: total de operações de ponto flutuante exigidas por uma carga de trabalho.
  • FLOP/s: a taxa com que o hardware pode executar FLOPs (por exemplo, “TFLOP/s”).
  • Utilização: fração do pico teórico que você atinge na prática.

Em aprendizado profundo, chegar ao pico de computação é difícil porque kernels podem ser limitados por largura de banda de memória, sincronização ou formatos (shapes) ruins de tensores.

Regra prática: se sua GPU está com 30% de utilização, com frequência você está limitado pelo sistema (pipeline de dados, tamanhos de batch pequenos, kernels ineficientes, sobrecarga de comunicação), e não “limitado por computação”.

Hierarquia de memória e a parede de largura de banda

Aceleradores têm uma hierarquia:

  • Registradores / SRAM (muito rápido, minúsculo)
  • Caches on-chip / memória compartilhada
  • HBM / GDDR (memória do dispositivo) (grande, rápida, mas bem mais lenta do que on-chip)
  • RAM do host
  • Armazenamento

O desempenho em aprendizado profundo frequentemente encontra a parede de largura de banda: as unidades de computação ficam ociosas esperando dados vindos da memória.

Um modelo mental útil é o modelo roofline (roofline model):

  • O desempenho atingível é limitado por:
    • Pico de computação
    • Pico de largura de banda de memória × intensidade aritmética

Intensidade aritmética (arithmetic intensity) ≈ FLOPs por byte carregado. Operações como multiplicações de matrizes grandes (GEMMs) são favoráveis à computação; operações elemento a elemento, multiplicações de matrizes pequenas e muitos padrões de atenção podem ser limitados por largura de banda.

Latência vs vazão (especialmente em inferência)

  • Latência: tempo para atender uma requisição (importante para apps interativos e agentes).
  • Vazão: requisições/seg ou tokens/seg (importante para processamento em lote e custo).

Muitos sistemas de inferência trocam latência por vazão usando agrupamento em lote (batching). Mas o batching pode aumentar a latência de cauda e pode ser difícil com sequências de comprimento variável.

Isso importa muito para Agentes & Planejamento, onde modelos frequentemente fazem muitas chamadas curtas de “raciocínio” e etapas de uso de ferramentas; baixa latência e comportamento de cauda previsível podem ser mais valiosos do que vazão de pico.

Blocos de construção de hardware para IA

CPUs: orquestração, pré-processamento e controle sensível à latência

CPUs se destacam em:

  • Carregamento e pré-processamento de dados
  • Executar código não tensor (tokenização, lógica de negócio)
  • Agendamento e orquestração (lançar kernels na GPU, gerenciar I/O de rede)
  • Caminhos de controle sensíveis à latência (comuns em pipelines de agente/ferramenta)

Mas CPUs normalmente são ineficientes para treinamento de grandes redes neurais em comparação com GPUs/TPUs.

GPUs: o “burro de carga” de propósito geral

GPUs modernas para IA são otimizadas para álgebra linear densa:

  • Tensor cores / engines de matriz aceleram GEMM e convolução.
  • Memória de alta largura de banda (HBM) fornece grande largura de banda.
  • Ecossistemas de software maduros (CUDA, ROCm, bibliotecas do fornecedor).

Boas práticas típicas em GPUs:

  • Usar multiplicações de matrizes grandes e bem “formatadas”
  • Preferir kernels fundidos (fused kernels) (reduz leituras/escritas na memória)
  • Usar precisão mista (mixed precision) (BF16/FP16/FP8) quando estável

TPUs e outros aceleradores: co-design estreito de HW/SW

Aceleradores especializados (por exemplo, TPUs) buscam:

  • Multiplicações de matrizes eficientes
  • Alta vazão com menor consumo de energia
  • Forte integração com compiladores (por exemplo, grafos no estilo XLA)

O conceito-chave de sistema é o co-design (co-design): o modelo de programação, compilador, interconector e hardware são projetados juntos para reduzir sobrecargas.

Capacidade de memória: parâmetros, ativações e estado do otimizador

O uso de memória no treinamento inclui:

  • Parâmetros (pesos do modelo)
  • Gradientes
  • Estado do otimizador (por exemplo, Adam mantém estimativas adicionais de momentos)
  • Ativações (tensores intermediários para backprop)
  • Cache KV (KV cache) (na inferência autorregressiva)

Para modelos grandes, o estado do otimizador e as ativações frequentemente dominam. Por isso técnicas como checkpointing de ativações, otimizadores particionados e precisão mista importam.

Formatos numéricos e precisão mista

Por que menor precisão ajuda

Formatos de menor precisão reduzem:

  • Pegada de memória (mais cabe em HBM)
  • Demanda de largura de banda de memória
  • Às vezes aumentam a vazão de computação (tensor cores)

Formatos comuns:

  • FP32: estável, caro
  • BF16: bom alcance dinâmico, popular para treinamento
  • FP16: mais rápido, mas com faixa de expoente mais estreita; muitas vezes precisa de escalonamento de perda (loss scaling)
  • FP8: cada vez mais usado para treinamento/inferência em hardware recente
  • INT8/INT4: comum para inferência via Quantização

Precisão mista na prática

A precisão mista tipicamente mantém:

  • Algumas acumulações em FP32
  • Pesos mestre às vezes em FP32
  • A maioria das multiplicações de matrizes em BF16/FP16/FP8

Exemplo (PyTorch AMP):

import torch
from torch.cuda.amp import autocast, GradScaler

model = model.cuda()
opt = torch.optim.AdamW(model.parameters(), lr=3e-4)
scaler = GradScaler()

for x, y in loader:
    x, y = x.cuda(), y.cuda()
    opt.zero_grad(set_to_none=True)

    with autocast(dtype=torch.bfloat16):
        loss = model(x, y)

    scaler.scale(loss).backward()
    scaler.step(opt)
    scaler.update()

AMP pode melhorar significativamente a vazão, mas você ainda precisa observar:

  • Instabilidades (NaNs)
  • Divergência vs baselines em FP32
  • Sensibilidade em camadas de normalização ou softmax

Paralelismo e sistemas distribuídos

Treinamento em larga escala é, fundamentalmente, um problema de sistemas distribuídos.

Paralelismo de dados (data parallelism)

Cada GPU mantém uma réplica completa do modelo; batches diferentes são processados em paralelo. Gradientes são sincronizados (por exemplo, all-reduce) a cada passo.

Prós:

  • Simples
  • Eficiente quando o modelo cabe por dispositivo

Contras:

  • Limitado pela memória por dispositivo
  • A sobrecarga de comunicação cresce com o tamanho do modelo

Esboço de PyTorch DDP:

import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel as DDP

dist.init_process_group("nccl")
torch.cuda.set_device(local_rank)

model = model.cuda()
model = DDP(model, device_ids=[local_rank])

Paralelismo de modelo (tensor, pipeline, expert)

Quando o modelo não cabe em um único dispositivo, você o particiona.

  • Paralelismo de tensor (tensor parallelism): divide grandes multiplicações de matrizes entre dispositivos.
  • Paralelismo de pipeline (pipeline parallelism): divide camadas em estágios; usa microbatch para manter estágios ocupados.
  • Paralelismo de especialistas (expert parallelism) (MoE): roteia tokens para “especialistas”, adicionando padrões de comunicação.

Esses métodos reduzem a pressão de memória por dispositivo, mas adicionam:

  • Mais comunicação
  • Agendamento mais complexo
  • Possíveis bolhas (tempo ocioso do pipeline)

Este tópico se conecta de perto a Treinamento Distribuído e Paralelismo de Modelo.

Primitivas de comunicação e interconectores

O desempenho distribuído frequentemente é limitado por:

  • Largura de banda: quantos GB/s podem ser movidos
  • Latência: quanto tempo cada operação coletiva leva
  • Topologia: NVLink vs PCIe dentro de um nó; InfiniBand/Ethernet entre nós

Coletivos comuns:

  • All-reduce: agrega gradientes no paralelismo de dados
  • All-gather / reduce-scatter: usados em estados de otimizador particionados e no paralelismo de tensor
  • Broadcast: distribui parâmetros ou ativações

Implicação prática: dois clusters com a mesma contagem de GPUs podem diferir dramaticamente na velocidade de treinamento se o interconector for diferente.

Leis de escala encontram limites de sistemas

Mesmo que um modelo se beneficie de mais computação, a escala pode atingir tetos do sistema:

  • O pipeline de entrada não consegue alimentar as GPUs rápido o suficiente
  • O tempo de all-reduce domina o tempo do passo
  • Limites de memória forçam batches menores, reduzindo eficiência
  • Stragglers causam esperas de sincronização

Na prática, você frequentemente otimiza tempo até treinar (time-to-train) equilibrando:

  • Tamanho de batch global + acumulação de gradiente
  • Estratégia de paralelismo (dados vs tensor vs pipeline)
  • Sobreposição de comunicação (pipeline de compute/comm)
  • Frequência de checkpointing e tolerância a falhas

Técnicas eficientes de treinamento (centradas em sistemas)

Checkpointing de ativações

Em vez de armazenar todas as ativações para o backprop, você recomputa algumas durante o backward.

  • Economiza memória
  • Custa computação extra
  • Muitas vezes vale a pena quando memória é a restrição

Otimizadores particionados (sharded optimizers) (ideias no estilo ZeRO)

O estado do otimizador e os gradientes podem ser particionados entre ranks do paralelismo de dados.

Efeito:

  • Reduz memória por GPU
  • Aumenta a complexidade de comunicação

Acumulação de gradiente (gradient accumulation)

Acumula gradientes ao longo de múltiplos microbatches antes de sincronizar ou atualizar.

Útil quando:

  • Você quer um batch efetivo grande
  • Você está limitado por memória por GPU

Custo:

  • Mais passes forward/backward por passo do otimizador
  • Pode afetar a dinâmica de otimização (embora frequentemente seja administrável)

Otimização do pipeline de entrada

Alimentar aceleradores é um problema de sistemas:

  • Use dataloaders paralelos
  • Use prefetching e memória pinned
  • Evite gargalos de CPU por passo (tokenização, augmentação)
  • Considere cache e particionamento (sharding) de datasets

Um modo de falha comum: “GPUs são lentas”, mas o problema real é pré-processamento na CPU ou armazenamento lento.

Sistemas de inferência: servir modelos com eficiência

Inferência parece “apenas forward pass”, mas restrições de sistema dominam em escala.

Prefill vs decode em modelos autorregressivos

Para muitos Modelos de Linguagem Grandes, a inferência tem duas fases:

  • Prefill: processa o prompt (paralelizável, alta intensidade aritmética)
  • Decode: gera tokens um por vez (sensível à latência, sequencial)

Decode frequentemente é o gargalo. Técnicas que ajudam:

  • Cache KV para evitar recomputar atenção sobre tokens anteriores
  • Decodificação especulativa (speculative decoding) (modelo rascunho propõe tokens, modelo grande verifica)
  • Kernels de atenção eficientes e bom gerenciamento de memória

Cache KV e pressão de memória

O cache KV cresce com:

  • Tamanho do batch
  • Comprimento da sequência
  • Número de camadas e heads
  • Precisão (FP16/BF16 vs INT8)

Por isso servir modelos de contexto longo pode ser limitado por memória, mesmo que haja computação disponível.

Batching e agendamento

Sistemas de serving tipicamente implementam:

  • Batching dinâmico (dynamic batching): agrupa requisições que chegam próximas no tempo
  • Batching contínuo (continuous batching): admite novas requisições entre passos de decode
  • Agendamento por prioridade (priority scheduling): protege requisições sensíveis à latência

Trade-off:

  • Batches maiores melhoram a vazão, mas podem piorar a latência de cauda.

Quantização para inferência

A quantização reduz memória e aumenta a vazão, frequentemente com perda mínima de qualidade quando feita com cuidado. Abordagens comuns:

  • INT8 pós-treinamento
  • Quantização apenas de pesos (por exemplo, pesos INT4, ativações FP16)
  • Treinamento com consciência de quantização (quantization-aware training) para melhor fidelidade

Isso se conecta a Quantização.

Exemplo: por que “tokens/seg” depende de escolhas de sistema

Suponha que você sirva um modelo de chat e veja poucos tokens/seg. Possíveis causas de sistema:

  • Tamanho de batch pequeno demais (subutilização)
  • Kernel de atenção não otimizado para sua GPU
  • Cache KV saturando a largura de banda de memória
  • Tokenização na CPU no caminho crítico
  • Sobrecarga de rede se o modelo estiver particionado entre nós

Correções possíveis incluem:

  • Tirar a tokenização do caminho da requisição (ou usar tokenizers mais rápidos)
  • Usar batching contínuo
  • Usar um runtime otimizado (TensorRT-LLM / estilo vLLM)
  • Usar quantização ou cache KV em menor precisão
  • Manter o sharding dentro de um nó (NVLink) quando possível

Stack de software: kernels, compiladores e captura de grafo

Eficiência de kernel e fusão

Muitos modelos contêm sequências como:

  • linear → bias → activation → dropout → residual → layer norm

Se cada etapa for um kernel separado, você paga leituras/escritas extras na memória e sobrecarga de lançamento. Fusão de kernels (kernel fusion) reduz esses custos.

Stacks modernos alcançam fusão via:

  • Compiladores de grafo
  • Compilação JIT
  • Kernels fundidos otimizados à mão em bibliotecas

Execução em grafo vs eager

  • Modo eager (eager mode): flexível, mais fácil de depurar; pode ter sobrecarga de muitas operações pequenas.
  • Captura/compilação de grafo (graph capture/compilation): habilita fusão e agendamento; pode ser mais rápido, mas menos flexível.

Na prática, stacks de treinamento frequentemente misturam:

  • Python eager para orquestração
  • Regiões compiladas para caminhos quentes (hot paths)

Profiling: meça antes de otimizar

Sinais úteis:

  • Utilização da GPU e FLOP/s atingidos
  • Breakdown de tempo por kernel
  • Utilização de largura de banda de HBM
  • Tempo de dataloader na CPU
  • Tempo de comunicação em coletivos

Metodologia comum:

  1. Confirme que você não está limitado por entrada (dataloader, pré-processamento).
  2. Identifique os principais kernels e se são limitados por computação ou por largura de banda.
  3. Verifique sobreposição de comunicação e eficiência de all-reduce.
  4. Valide eficiência de escala ao adicionar mais GPUs/nós.

Confiabilidade, orquestração e custo

Agendamento e multi-tenancy

Em clusters compartilhados, você se importa com:

  • Agendamento justo (prioridade, cotas)
  • Isolamento (um job não deve degradar outro)
  • Eficiência de empacotamento (evitar deixar “fragmentos de GPU” ociosos)

Isso se torna um fator-chave de custo: aceleradores ociosos são caros.

Tolerância a falhas

Grandes execuções de treinamento devem tolerar:

  • Preempções (instâncias spot)
  • Falhas de nó
  • Instabilidades de rede

Abordagens:

  • Checkpoints periódicos (mas checkpointar com muita frequência desperdiça tempo)
  • Checkpoints incrementais
  • Workers sem estado (stateless) com estado recuperável

Modelo de custo: o que realmente determina o gasto

O custo total é moldado por:

  • Preço por hora do hardware × tempo de relógio (wall-clock)
  • Eficiência de escala (8 GPUs deixam você 8× mais rápido, ou só 4×?)
  • Utilização (GPUs esperando por I/O ou comunicação?)
  • Escolhas de precisão/quantização (pegada de memória → menos GPUs)
  • Eficiência de serving (batching, cache, memória KV)

Um enquadramento prático é custo por execução de treinamento e custo por 1M tokens servidos, em vez de apenas especificações de hardware.

Energia e térmicas

Energia e refrigeração importam operacionalmente:

  • GPUs/TPUs consomem energia significativa em alta utilização
  • Throttling térmico pode reduzir desempenho silenciosamente
  • Restrições de data center podem limitar o tamanho do cluster tanto quanto o orçamento

Eficiência energética está cada vez mais sendo uma preocupação de primeira ordem, não apenas um detalhe.

Exemplos práticos e regras gerais

Exemplo 1: Quando GPUs mais rápidas não tornam o treinamento mais rápido

Você faz upgrade de uma GPU antiga para uma mais nova com TFLOP/s de pico muito maior, mas a velocidade de treinamento quase não melhora. Razões comuns:

  • Sua carga de trabalho é limitada por largura de banda (padrões de atenção, operações pequenas, passos do otimizador).
  • Seu cluster é limitado por comunicação (all-reduce domina).
  • Seu pipeline de entrada não consegue alimentar a GPU (pré-processamento na CPU, armazenamento).

Correção: otimize movimento de memória, funda operações, aumente o tamanho do batch (se estável), sobreponha comunicação/computação, ou mude a estratégia de paralelismo.

Exemplo 2: Um gargalo de serving causado por cache KV

Uma aplicação de chat com contexto longo dá OOM ou fica lenta no horário de pico. Diagnóstico:

  • A memória do cache KV cresce com usuários concorrentes e comprimento da sequência.
  • Mesmo que haja computação disponível, capacidade e largura de banda de memória viram a restrição.

Correções:

  • Reduzir o contexto máximo ou limitar o orçamento por requisição
  • Usar quantização do cache KV
  • Usar melhor batching/agendamento
  • Escalar horizontalmente com mais réplicas, não apenas batches maiores

Exemplo 3: Verificação de escala em treinamento distribuído

Se 1 GPU treina em 1,0 unidade de tempo, 8 GPUs idealmente treinam em 0,125. Na realidade, você pode ver 0,20–0,30 devido a:

  • Sobrecarga de all-reduce
  • Bolhas de pipeline
  • Desbalanceamento de carga (comprimentos de sequência variáveis)
  • Contenção no pipeline de dados

Bom trabalho de sistemas melhora a eficiência de escala (scaling efficiency), não apenas adiciona dispositivos.

Como compute & systems se conecta ao resto da IA

Restrições de computação moldam quais modelos são viáveis, quais experimentos você pode rodar e quais aplicações são econômicas:

Principais conclusões

  • O desempenho em IA é um equilíbrio de computação, memória, largura de banda e comunicação, não um único “número de TFLOPs”.
  • Treinamento moderno é uma carga de trabalho de sistemas distribuídos; interconector e coletivos podem dominar o tempo de execução.
  • Inferência geralmente é limitada por latência, memória do cache KV e batching/agendamento, não apenas por computação bruta.
  • As otimizações mais impactantes frequentemente são de nível de sistema: melhor paralelismo, fusão de kernels, precisão mista, runtimes eficientes de serving e melhor utilização.
  • Custo segue utilização e eficiência: hardware mais rápido só ajuda se seu sistema conseguir mantê-lo ocupado.