Quarto post da série. No anterior, você viu quais VMs GPU provisionar e como conectar elas. Agora a ideia é olhar dentro da GPU pra entender o que acontece no silício. Não pra escrever CUDA kernel, mas pra ser um troubleshooter melhor e conversar com o time de ML sem ficar no modo adivinhação.

tl;dr: O tamanho do checkpoint não diz quanta VRAM o treino vai consumir. Em GPU, pesos são só o começo. Gradientes, ativações e estado do otimizador é que costumam explodir a conta.

O ticket das 2 da manhã

Slack toca às 2 da manhã. O training job do time de ML caiu de novo. O erro é uma linha:

CUDA out of memory. Tried to allocate 2.00 GiB

O lead de data science está irritado: “O modelo tem 7 bilhões de parâmetros em FP16. Isso é só 14 GB. A A100 tem 40 GB de memória. Deveria sobrar bastante. O que está acontecendo?”

Você entra por SSH, roda nvidia-smi e vê memory usage em 100%. A conta não fecha se você olhar só pros pesos. O problema é que pesos são só uma parte da história. Gradientes, estados do otimizador, ativações e, em muitos casos, uma cópia FP32 dos pesos brigam pelo mesmo espaço.

Esse post existe pra resolver exatamente esse tipo de conversa.

Arquitetura GPU pra engenheiros de infra

Você não precisa projetar circuito. Mas precisa de um modelo mental do que está dentro da caixa, porque isso explica por que certos workloads andam bem, por que outros engasgam e por que alguns SKUs rendem muito mais do que outros no mesmo código.

Streaming Multiprocessors (SMs)

Uma GPU é construída em cima de unidades repetidas chamadas Streaming Multiprocessors (SMs). Cada SM é um processador independente com seus próprios cores, cache e hardware de scheduling. A A100 tem 108 SMs. A H100 tem 132.

Pense em cada SM como um pequeno andar de fábrica. Ele tem seus operários, seu armazenamento local e seu próprio capataz decidindo a ordem do trabalho.

CUDA Cores vs. Tensor Cores

Dentro de cada SM:

  • CUDA Cores: processadores paralelos de propósito geral. A100 = 6.912, H100 = 16.896. Lidam com math de ponto flutuante e inteiros.
  • Tensor Cores: unidades especializadas em matrix-multiply-and-accumulate. A100 = 432, H100 = 528. São elas que puxam boa parte do desempenho em AI.

Tradução infra ↔ AI: GPU é como uma rodovia enorme com dezenas de faixas fazendo a mesma operação ao mesmo tempo. CPU é uma rodovia bem menor, mas muito melhor em curvas, desvios e decisões. Pra multiplicação de matrizes, a rodovia larga ganha.

Quando você tem múltiplas GPUs no mesmo nó, elas precisam trocar dados. PCIe resolve o básico, mas training multi-GPU sério quase sempre quer NVLink:

GPUNVLink bandwidth (bidirecional)
A100600 GB/s
H100900 GB/s
B2001.8 TB/s

Verifique NVLink com:

nvidia-smi topo -m

Se aparecer PIX ou PHB entre GPUs em vez de NV#, você está olhando pra um caminho via PCIe, não pra NVLink. Nessa hora vale conferir se o SKU escolhido realmente entrega a topologia que o time acha que comprou.

Memória GPU: o recurso que você mais vai gerenciar

Se você só guardar uma seção desse post, guarda essa. GPU memory, e principalmente ficar sem ela, é o problema mais comum em infra de AI.

Hierarquia de memória

CamadaA100 SpecH100 SpecAnalogia
HBM (High Bandwidth Memory)40 GB: ~1.6 TB/s, 80 GB: ~2.0 TB/s80 GB, 3.35 TB/sRAM do sistema
L2 Cache40 MB50 MBCPU L3 cache
Shared Memory / L1Até 164 KB por SMAté 256 KB por SMCPU L1/L2 cache
Registers256 KB por SM256 KB por SMRegistradores CPU

HBM é o que o nvidia-smi mostra. É a memória principal da GPU, onde vivem pesos do modelo, batches, ativações e resultados intermediários.

O que preenche a memória durante training

Cinco consumidores principais:

1. Model Parameters (os pesos) Tamanho direto: parâmetros × bytes por parâmetro. 7B params em FP16 = ~14 GB.

2. Gradients Um gradiente por parâmetro durante backpropagation. 7B × 2 bytes = mais ~14 GB.

3. Master Weights Em muitos pipelines de mixed precision, o framework mantém uma cópia FP32 dos pesos. 7B × 4 bytes = ~28 GB.

4. Optimizer States Adam ou AdamW mantêm dois estados por parâmetro, geralmente em FP32. 7B × 4 bytes × 2 = ~56 GB.

5. Activations Resultados intermediários salvos no forward pass e reaproveitados no backward. Essa parte varia com arquitetura, sequência e batch size.

A matemática que salva seu fim de semana

Total GPU Memory ≈ Parameters + Gradients + Master Weights + Optimizer States + Activations

Pro modelo de 7B do ticket:

ComponenteCálculoMemória
Parameters (FP16)7B × 2 bytes~14 GB
Gradients (FP16)7B × 2 bytes~14 GB
Master Weights (FP32)7B × 4 bytes~28 GB
Optimizer States (FP32, AdamW)7B × 4 bytes × 2~56 GB
Activations (varia)Depende do batch size~8-20 GB
Total~120-132 GB

Um “modelo de 14 GB” não pede 14 GB pra treinar. Nesse cenário ele pede mais de 120 GB. A100-40GB está fora do jogo desde o começo. Mesmo A100-80GB fica apertada.

Regra prática: quando um ML engineer diz “o modelo tem X gigabytes”, quase sempre ele está falando do checkpoint. Pra training com Adam ou AdamW, eu começo chutando algo entre 6x e 8x o tamanho dos pesos e depois ajusto pelo batch size e pelas otimizações do framework.

Gradient checkpointing troca compute por memória. Em vez de guardar todas as ativações do forward, o framework salva menos coisa e recomputa parte do caminho no backward. Isso costuma reduzir memória de ativações bastante, com uma penalidade de tempo de treino que varia conforme o modelo.

Precisão: trocando memória e throughput por fidelidade numérica

FormatoBitsBytes/paramRangeUse case
FP32324±3.4 × 10³⁸Full-precision training, master weights
TF3219*4 (armazenado)= FP32Default em A100+ pra matmul
BF16162±3.4 × 10³⁸Preferido pra training
FP16162±65.504Training com loss scaling, inference
INT881-128 a 127Inference quantizada
INT440.5-8 a 7Inference agressivamente quantizada

BF16 virou o ponto de equilíbrio mais comum no training moderno. Ele mantém o range do FP32 e corta a memória pela metade.

INT8 e INT4 entram mais na fase de inference. Você treina em BF16 ou FP16, depois quantiza pra reduzir memória e aumentar throughput, aceitando alguma perda de qualidade.

Tradução infra ↔ AI: pensa em precisão como formato de imagem. FP32 é o RAW. BF16 é um JPEG de alta qualidade. INT4 já é thumbnail. Em cada etapa você decide se quer mais fidelidade ou mais velocidade.

Estratégias multi-GPU

Quando o modelo não cabe numa GPU, ou quando o training levaria dias demais, entra o mundo multi-GPU.

Data Parallelism (DP)

Você replica o modelo inteiro em cada GPU. Cada GPU processa um batch diferente. Depois de cada step, as GPUs sincronizam gradientes via all-reduce. Em workload bem balanceado, o ganho pode chegar perto do linear.

O lado ruim é óbvio: cada GPU precisa segurar o modelo inteiro, os gradientes e os estados do otimizador.

DeepSpeed ZeRO: o destruidor de limites de memória

StageO que é particionadoEfeito prático
ZeRO-1Optimizer states e, em muitos casos, master weightsCorta a parte mais cara da memória persistente
ZeRO-2Optimizer states + gradientsReduz também a fatia de gradientes
ZeRO-3Optimizer states + gradients + parametersSharda praticamente tudo que era replicado

Voltando ao exemplo de 7B: training com AdamW numa GPU pede mais de 120 GB. Com 8 GPUs e ZeRO-3, a parte persistente cai pra algo perto de 14 GB por GPU. Some ativações e você ainda fica, dependendo do batch, na faixa de 22 a 34 GB por GPU. Agora uma A100-40GB já passa a fazer sentido.

FSDP (Fully Sharded Data Parallel)

FSDP é a resposta nativa do PyTorch pro mesmo problema. A lógica é parecida com ZeRO-3: particionar parâmetros, gradientes e estados do otimizador. Do ponto de vista de infra, os requisitos são muito próximos.

Pipeline Parallelism (PP)

Aqui você divide o modelo por camadas: GPU 0 fica com um pedaço, GPU 1 com outro, e assim por diante. Cada GPU segura só uma fração dos pesos. O problema é que aparecem pipeline bubbles, quando parte das GPUs fica esperando dado passar pela fila.

Tensor Parallelism (TP)

Esse é o nível mais granular. Você divide a própria camada entre GPUs. Funciona muito bem quando a interconexão é rápida. NVLink aqui deixa de ser luxo e vira requisito prático.

3D Parallelism (modelos 100B+)

Combina os três:

  • TP dentro do nó
  • PP entre poucos nós
  • DP com ZeRO entre muitos nós

É o tipo de composição que aparece em LLMs gigantes.

Model SizeEstratégiaGPUsRede necessária
< 1B paramsGPU única ou DP1-8PCIe OK
1-10B paramsDP + ZeRO-24-16NVLink preferido
10-70B paramsZeRO-3 / FSDP8-64NVLink + InfiniBand
70-200B+ params3D Parallelism64-512+NVLink + InfiniBand obrigatórios

A stack de software NVIDIA

Boa parte das sessões de debugging de GPU termina em compatibilidade de software. A stack é em camadas, e cada camada depende da de baixo:

Código do modelo (script de training)
Framework (PyTorch 2.x, TensorFlow, JAX)
cuDNN (primitivas DL otimizadas) + NCCL (multi-GPU)
CUDA Toolkit (libraries, runtime, compiler)
NVIDIA Driver (kernel module -> hardware GPU)
Hardware GPU (A100, H100, etc.)

Se alguma peça sai do lugar, você vê desde mensagem críptica até crash silencioso.

O escape hatch dos containers: imagens NVIDIA NGC, como nvcr.io/nvidia/pytorch, já empacotam uma combinação testada de driver API, CUDA, cuDNN, NCCL e framework:

# Pull container oficial NVIDIA PyTorch (releases mensais)
docker pull nvcr.io/nvidia/pytorch:24.05-py3

# Rodar com acesso GPU
docker run --gpus all -it nvcr.io/nvidia/pytorch:24.05-py3

Dica de troubleshooting: as três versões que eu sempre coleto primeiro são estas:

# Versão do driver + max CUDA suportada
nvidia-smi

# CUDA Toolkit instalado
nvcc --version

# CUDA que o PyTorch foi compilado contra
python -c "import torch; print(torch.version.cuda)"

Se elas não combinam, você já tem um suspeito forte.

Lendo nvidia-smi como um pro

nvidia-smi é o top do mundo GPU. Saída típica:

+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 535.161.08    Driver Version: 535.161.08    CUDA Version: 12.2               |
|-----------------------------------------+------------------------+----------------------|
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |          Memory-Usage  | GPU-Util  Compute M. |
|=========================================+========================+======================|
|   0  NVIDIA A100-SXM4-80GB         On   | 00000001:00:00.0  Off  |                    0 |
| N/A   42C    P0              72W / 400W |  71458MiB / 81920MiB   |     94%      Default |
+-----------------------------------------+------------------------+----------------------+

Campos que importam

CampoO que significaSaudável (training)Problemático
GPU-Util% de compute ativo85-100%Abaixo de 50%
Memory-UsageHBM usada70-95%100% com OOM ou muito baixa com GPU ociosa
TempTemperatura °C35-75°CAcima de 83°C
Pwr:Usage/CapConsumo vs. limite60-90% do capMuito baixo com job ativo
PerfPerformance stateP0P2+ durante job ativo
ECC ErrorsErros de memória0Qualquer valor > 0
Persistence-MDriver persistenteOnOff em hosts dedicados a GPU

Comandos essenciais

# Snapshot básico (90% do uso)
nvidia-smi

# Monitoramento contínuo (refresh a cada 5s)
nvidia-smi -l 5

# Output CSV pra scripting e dashboards
nvidia-smi --query-gpu=name,temperature.gpu,utilization.gpu,utilization.memory,memory.total,memory.used --format=csv

# Monitoramento compacto em tempo real
nvidia-smi dmon -s u

# Topologia GPU: verificar NVLink
nvidia-smi topo -m

# Checar erros ECC (saúde do hardware)
nvidia-smi --query-gpu=ecc.errors.uncorrected.volatile.total --format=csv

# Listar processos GPU
nvidia-smi pmon -s u -c 1

Os 7 problemas de GPU que você vai encontrar

1. CUDA Out of Memory (OOM)

RuntimeError: CUDA out of memory. Tried to allocate 2.00 GiB

Fixes, em ordem prática: reduzir batch size -> habilitar gradient checkpointing -> ZeRO-2 ou ZeRO-3 -> BF16 / mixed precision -> GPU maior.

2. CUDA Version Mismatch

CUDA error: no kernel image is available for execution on the device

Fix: conferir driver, toolkit e framework. Se a combinação estiver torta, use um container NGC conhecido.

3. GPU Not Found

NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver.

Fix: verifique SKU da VM, status da VM Extension, reboot e, se necessário, reinstalação do driver.

4. ECC Errors (falha de hardware)

nvidia-smi --query-gpu=ecc.errors.uncorrected.volatile.total --format=csv
# Se retornar > 0: abrir ticket no Azure para replacement

Em cloud isso costuma terminar em manutenção ou substituição do host. Conte com downtime.

5. Thermal Throttling

Temperatura acima de 83°C, perf state caindo de P0 pra P2 ou P3, throughput indo embora. Em cloud, isso geralmente vira chamado pro provedor.

6. Low GPU Utilization

GPU-Util abaixo de 50% durante training ativo quase sempre aponta pra data starvation. Fixes comuns: aumentar DataLoader.num_workers, usar pin_memory=True, mover cache pra NVMe local e revisar formato do dataset.

nvidia-smi topo -m mostra PHB ou PIX em vez de NV#. Vale revisar se o ambiente está em ND-series mesmo. NC e NV não entregam a mesma topologia.

Gerações de GPU no Azure

GeraçãoGPUAzure VMHBMNVLinkInfiniBand
Volta (2017)V100NC v3 / ND v216/32 GBDepende do SKUDepende do SKU
Ampere (2020)A100ND A100 v4 / NC A100 v440/80 GB600 GB/s200 Gb/s nas famílias ND
Hopper (2022)H100ND H100 v580 GB900 GB/s400 Gb/s
Blackwell (2024)B200 / GB200Novas famílias chegando ao Azure, confirme a regiãoaté 192 GBaté 1.8 TB/s400 Gb/s

Cada geração trouxe mais bandwidth de memória e novos formatos de precisão. Ampere colocou TF32 no jogo. Hopper trouxe FP8 e Transformer Engine. Blackwell empurra isso ainda mais, com mais memória por GPU e foco claro em modelos gigantes.

Fechando o loop

Quando o próximo ticket de OOM aparecer às 2 da manhã, a conta já fica mais simples: você não vai olhar só pro tamanho do modelo. Vai olhar pro batch size, pras ativações, pro otimizador e pra estratégia de paralelismo.

Leitura complementar

No próximo post

Agora que você entende o que acontece dentro da GPU, incluindo arquitetura, memória, stack de software e debugging, faz sentido automatizar o resto. No próximo post eu vou falar sobre Infrastructure as Code pra AI: como templatear clusters GPU, endpoints de inference e pipelines de training de forma reproduzível, versionada e auditável.