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:
- Confirme que você não está limitado por entrada (dataloader, pré-processamento).
- Identifique os principais kernels e se são limitados por computação ou por largura de banda.
- Verifique sobreposição de comunicação e eficiência de all-reduce.
- 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:
- Treinar modelos grandes se liga de perto a Treinamento Distribuído, Otimização, e Arquitetura Transformer.
- Inferência eficiente influencia a viabilidade de produto para Modelos de Linguagem Grandes e sistemas interativos.
- Aplicações agentivas (Agentes & Planejamento) são especialmente sensíveis à latência de inferência, comportamento de cauda e confiabilidade de serving porque envolvem muitas chamadas sequenciais ao modelo e interações com ferramentas.
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.