Dissecar a latência: para onde vai cada microssegundo na rede de um cluster de IA
Quem dimensiona redes para clusters de IA começa normalmente pela largura de banda — 100, 200, 400 GbE — e depois estranha que o benchmark de allreduce apresente um valor muito abaixo da velocidade nominal da linha. A razão é quase sempre a latência, e o regime de mensagens pequenas em que os gráficos de largura de banda não servem para nada.
Este artigo desmonta um único percurso de ida e volta e contabiliza cada etapa. Os valores abaixo são o intervalo consensual a partir de medições públicas, da documentação NVIDIA/Mellanox e dos nossos próprios testes. Trate-os como um orçamento, não como uma garantia — mudam com o silício da NIC, com o ASIC do switch, com a versão do kernel, com as definições da BIOS e com a paciência que tiver para afinar.
Público-alvo: quem especifica ou diagnostica fabrics de clusters de IA. Não é um livro de receitas de tuning, mas o modelo mental que torna o livro de receitas legível.
O percurso completo, num só diapositivo
Um ping-pong de 64 bytes entre duas GPU em nós diferentes, sobre um fabric comutado de 100 GbE com RDMA, decompõe-se mais ou menos assim:
| Componente | Contribuição típica | Notas |
|---|---|---|
| Publicação / serialização na aplicação | 0.1–1 µs | memcpy, construção do cabeçalho, escrita do descritor |
| Caminho de TX na NIC (verbs RDMA) | 0.3–0.8 µs | abaixo de 1 µs em ConnectX / BlueField modernas |
| Atraso no cabo | ~5 ns/m | 50 ns num DAC de 10 m, negligenciável <100 m |
| Salto de switch (cut-through, moderno) | 250–600 ns | InfiniBand <100 ns; Ethernet 400–600 ns |
| Salto de switch (store-and-forward, 64 B) | 1–3 µs | Acrescente a serialização por byte |
| Por cada salto adicional | +latência do switch | Leaf-spine acrescenta 2 saltos face a um só switch |
| Caminho de RX na NIC | 0.3–0.8 µs | Simétrico com o TX em RDMA |
| Pilha TCP/IP do kernel (um sentido) | 10–30 µs | Caminho de sockets; com interrupções e cópias |
| Kernel bypass (DPDK / verbs RDMA, um sentido) | <1 µs | Polling em espaço de utilizador, zero cópias |
| Receção / desserialização na aplicação | 0.1–1 µs | Acordar o consumidor, descodificar o cabeçalho |
Um RTT RDMA através de um único switch em 100 GbE / InfiniBand NDR fica em ~2 µs para mensagens pequenas. Um leaf-spine de dois saltos acrescenta ~1 µs. O mesmo percurso sobre TCP/IP do kernel fica em 20–60 µs, uma ordem de grandeza acima, e com pior comportamento na cauda. Estes dois números determinam quase todas as decisões de arquitetura descritas a seguir.
0.1–1 µs
250–600 ns
0.1–1 µs
Percurso RDMA num sentido: o salto de switch (250–600 ns em Ethernet, <100 ns em IB) é a variável dominante; o atraso no cabo abaixo de 100 m é negligenciável.
O cabo não é o problema
A luz na fibra viaja a cerca de dois terços de c, pelo que a propagação é de ~5 ns/m. Uma fibra de 30 m ao longo de uma fila de racks acrescenta 150 ns num sentido; mesmo um percurso de campus de 500 m dá um microssegundo. A serialização também é pequena às velocidades atuais: uma trama de 64 bytes a 100 Gb/s demora 5.12 ns; uma trama jumbo de 9000 bytes demora 720 ns. Para mensagens de controlo pequenas, a serialização é negligenciável; para transferências em volume deixa de contar assim que o tubo é suficientemente largo.
A latência vive mesmo é no software, nos ASIC dos switches e no protocolo que escolher.
Fabric de switching: cut-through vs store-and-forward
Um switch cut-through começa a encaminhar assim que lê o cabeçalho de destino. Um switch store-and-forward guarda a trama completa em buffer, valida opcionalmente o CRC e só depois encaminha.
| Classe de switch | Latência por salto |
|---|---|
| Switch InfiniBand NDR/HDR (cut-through) | <100 ns |
| Ethernet cut-through (classe Tomahawk, fabric de IA) | 400–600 ns |
| Ethernet cut-through (mais antigo / genérico) | 600–900 ns |
| Ethernet store-and-forward, 64 B | 1–3 µs |
| Ethernet store-and-forward, 1500 B | 1.5–4 µs |
A vantagem do InfiniBand é real: o silício de HPC anda a otimizar isto há duas décadas. Os switches Ethernet modernos para IA (Tomahawk 5, Jericho3-AI, Spectrum-X) fecham a maior parte da diferença de latência e superam o InfiniBand na profundidade de buffer, o que pesa mais no incast do allreduce.
Num leaf-spine, um fluxo entre racks atravessa leaf → spine → leaf. Os desenhos que minimizam o número de saltos (fat-tree pouco profunda, topologias otimizadas por rail) não procuram poupar largura de banda; procuram poupar 500 ns por salto evitado em cada coletiva.
A pilha de rede do kernel
O TCP/IP simples através do kernel Linux paga, por cada pacote: despacho da interrupção da NIC (ou polling NAPI), alocação do skb e softirq, processamento TCP/IP, uma cópia do buffer de socket entre o kernel e o espaço de utilizador, e uma mudança de contexto para a aplicação.
Para um pacote pequeno em x86 moderno, isto dá 10–30 µs num sentido no melhor dos casos, com picos de cauda acima de 100 µs sob carga. E é limitado pelo CPU — satura um núcleo a cerca de 1 Mpps muito antes de a NIC chegar ao limite. É esse o custo que o resto do artigo procura eliminar.
Kernel bypass: DPDK, RDMA, XDP, AF_XDP
Há quatro formas correntes de tirar o kernel do caminho dos dados, que diferem na radicalidade do bypass e no que deixam por aproveitar:
| Caminho | Latência mínima | Modelo de CPU | Compatibilidade | Uso típico |
|---|---|---|---|---|
| TCP/IP do kernel | 10–30 µs/sentido | Interrupções | Universal | Tudo o que não seja crítico em latência |
| AF_XDP | 6–10 µs/sentido | Híbrido | As ferramentas Linux continuam a funcionar | Meio-termo; programas eBPF |
| DPDK | 1–3 µs/sentido | Busy poll | O kernel não vê nada | Telco, HFT, NFV, pipelines de pacotes à medida |
| Verbs RDMA | <1 µs/sentido | Queue pair / CQE | Exige NIC e fabric RDMA | HPC, treino de IA, redes de armazenamento |
Algumas notas práticas:
- DPDK e RDMA não são intermutáveis. O DPDK corre processamento arbitrário de pacotes em espaço de utilizador à velocidade da linha; o RDMA implementa um protocolo específico de semântica de memória com offload por hardware. As cargas de trabalho de IA querem RDMA — o NCCL e as pilhas de armazenamento falam-no nativamente. O DPDK aparece em proxies de inferência, telemetria e dataplanes à medida.
- O AF_XDP é o meio-termo. Liga-se ao driver na camada eBPF, consegue latências abaixo de 10 µs e, ao contrário do DPDK, não rouba a NIC ao sistema operativo. É a resposta certa para máquinas de uso misto.
- O XDP sem AF_XDP serve para drop/redirect/forward em linha (limpeza de DDoS, balanceadores de carga). Processa os pacotes dentro do kernel, no gancho do driver; não os leva para o espaço de utilizador.
Para um cluster de IA: RDMA sobre InfiniBand ou RoCEv2 para a comunicação GPU-a-GPU, TCP/IP simples para tudo o resto (gestão, telemetria, descarregamento de modelos). Não se deve sobre-engenhar os caminhos lentos.
Coalescência de interrupções, GRO/LRO: a armadilha do débito
O Linux e a maioria dos drivers de NIC vêm afinados para débito, não para latência. Há dois parâmetros que contam:
-
Coalescência de interrupções. A NIC espera uma janela configurada em µs (ou um número de pacotes) antes de gerar uma interrupção. Reduz o custo por pacote; acrescenta latência igual à janela.
rx-usecs 50acrescenta até 50 µs numa ligação com pouco tráfego. - GRO / LRO. O kernel (GRO) ou a NIC (LRO) agrega vários segmentos TCP recebidos num skb maior antes de o entregar acima. Fica mais barato de processar, mas o agregador espera deliberadamente por mais pacotes, acrescentando microssegundos.
O compromisso é honesto e não tem volta: não se pode ter débito máximo e latência mínima na mesma configuração. Num nó GPU, a NIC RDMA que trata do allreduce quer rx-usecs 0 ou 1, coalescência adaptativa desligada e GRO desligado na interface do lado RDMA. Uma NIC de gestão separada fica com as predefinições; não está no caminho crítico.
O corolário: uma NIC usada ao mesmo tempo para coletivas RDMA e para descarregamentos TCP vai dar números medíocres nas duas coisas, a não ser que separe o tráfego por filas ou interfaces. Os nós de IA a sério têm uma ou duas NIC RDMA dedicadas (ConnectX-7/8, BlueField-3) mais uma NIC de gestão separada.
A camada aplicacional também não é grátis
Há dois custos no topo da pilha que raramente chegam aos diagramas de arquitetura:
-
memcpy. Um
memcpyde 4 MB demora ~200 µs num único núcleo a ~20 GB/s. Se a sua coletiva copia os dados do tensor para um buffer intermédio antes de os publicar, já gastou mais tempo do que todo o percurso de ida e volta na rede. O GPUDirect RDMA salta esta etapa — a NIC lê diretamente da memória da GPU. - Serialização. Protobuf, JSON ou um enquadramento feito à mão acrescentam dezenas de µs em payloads não triviais. Aceitável num RPC do plano de controlo; fatal no ciclo interno do allreduce. O NCCL evita-o com descritores binários de tamanho fixo e memória pré-registada.
Se a sua pilha "afinada para RDMA" continua lenta, faça o profiling do espaço de utilizador antes de culpar o fabric. Já vimos coletivas de 30 µs em que 25 µs eram um reshape de tensores em PyTorch e apenas 5 µs eram cabo e switch.
Operações coletivas: latência vs largura de banda, e porque é que o tamanho dos pacotes importa
O NCCL (como qualquer biblioteca de coletivas) escolhe o algoritmo em função do tamanho da mensagem:
- As mensagens pequenas são limitadas pela latência. O NCCL usa algoritmos em árvore e o seu protocolo LL (4 bytes de dados com 4 bytes de flags através de escritas atómicas de 8 bytes, sem barreiras de memória). A busbw é uma fração da velocidade da linha; o que se está a medir é o custo por mensagem × número de mensagens.
- As mensagens grandes são limitadas pela largura de banda. O NCCL passa a algoritmos em anel, aproximando-se da velocidade da ligação mais lenta. A latência por mensagem desaparece sob os megabytes.
A transição situa-se grosso modo entre 64 KB e 1 MB, consoante a topologia e a NIC. Abaixo disso, dominam a latência do switch e da NIC e a pilha de protocolos. Acima, está-se a ler a velocidade do cabo no gráfico.
| Tamanho da mensagem | Algoritmo | BusBw (um nó, 8× NVLink) | BusBw (8 nós, IB NDR) |
|---|---|---|---|
| 1 KB | Árvore / LL | ~5 GB/s | ~2 GB/s |
| 64 KB | Árvore | ~80 GB/s | ~20 GB/s |
| 1 MB | Anel | ~250 GB/s | ~40 GB/s |
| 64 MB | Anel | ~370 GB/s | ~45 GB/s |
| 1 GB | Anel | ~450 GB/s | ~48 GB/s |
O tecto publicado para o NVLink da H100 é de 450 GB/s; chegar lá com mensagens mais pequenas ou entre nós é uma luta de afinação. Para a gama da Kentino — 5090 / 4090 / RTX Pro 6000 Blackwell sobre RoCE a 100 GbE — conte com ~10–12 GB/s de busbw por nó em mensagens grandes a 100 GbE, com o mesmo perfil de algoritmo.
O jitter é pior do que a latência
Se a latência do allreduce em todos os nós for de 30 µs ± 1 µs, a barreira espera 30 µs. Se for de 30 µs em média, mas com um nó a 300 µs uma vez em cada mil iterações, todas as outras GPU ficam paradas 270 µs de cada vez que essa cauda dispara. Ao longo de uma época de 100 mil iterações em 16 nós, isso dá horas de computação perdida.
É por isso que a latência P99 / P999 interessa mais ao treino de IA do que a latência média. Uma coletiva de barreira é uma operação em que vence o mais lento: o cluster anda à velocidade do seu pior nó.
Origens do jitter:
- Congestão de buffer por incast. O AllReduce é muitos-para-um em cada iteração. Os switches com buffers pouco profundos descartam pacotes, entra em ação a pausa PFC e a latência dispara 10×–100×. Os switches de IA com buffers profundos (Jericho3-AI, Spectrum-X) absorvem a rajada.
- Jitter do CPU do anfitrião. Uma tarefa cron, uma thread de kernel sem limites ou uma variação de frequência do CPU produzem um valor atípico de 1 ms. Isole núcleos para a thread de ISR/polling da NIC, desative os C-states nos CPU críticos, fixe os processos.
- Coalescência adaptativa. O driver "ajuda" aumentando a coalescência sob carga; a latência dispara em silêncio. Desligue-a explicitamente na NIC RDMA.
- Assimetria de topologia. Um leaf a 200 GbE, outro a 100 GbE. O mais lento é o seu limite em todas as coletivas.
A latência de cauda, e não a média, é o SLO certo para um fabric de IA.
Medir como deve ser
O ping mede o RTT de ICMP pela pilha do kernel no plano de gestão. É inútil para caracterizar um fabric RDMA. As ferramentas que servem:
-
sockperf ping-pong— latência UDP/TCP com resolução sub-nanossegundo. Bom para linhas de base da pilha do kernel e para detetar regressões. -
ib_send_lat,ib_write_lat(suíte perftest) — latência dos verbs RDMA diretamente. É o número que interessa mesmo em InfiniBand e RoCE. Conte com ~1–2 µs numa ligação através do mesmo switch. -
Micro-benchmarks OSU —
osu_latency,osu_bw,osu_allreduceao nível do MPI. A ferramenta certa para aplicações de HPC, antes de o NCCL entrar em cena. -
nccl-tests—all_reduce_perf,all_gather_perf,reduce_scatter_perf. O único benchmark que conta para treino de IA. Exercita exatamente o caminho de código que o seu treino usa. Varra de 8 B a 1 GB; a curva mostra onde o fabric está partido.
Verificação de sanidade: se a busbw do nccl-tests all_reduce_perf em mensagens grandes ficar bem abaixo de 80% da velocidade nominal da NIC, tem um problema de fabric e não de software. Percorra as camadas deste artigo de cima para baixo.
Hábito útil: guarde o resultado do nccl-tests como parte do comissionamento e volte a executá-lo depois de cada alteração de firmware, driver ou topologia. Uma regressão apanhada no dia em que acontece é um diff de uma linha. Apanhada três semanas depois, é um exercício forense.
Quando a latência conta mesmo — e quando não conta
A latência conta em:
- Allreduce síncrono de gradientes em treino com paralelismo de dados. Em todas as iterações, em todos os nós. Latência de cauda = computação perdida.
- Paralelismo de pipeline de grão fino com micro-lotes pequenos. O tempo de bolha nas fronteiras entre andares tem como piso a latência entre nós.
- Divisões de camadas em paralelismo de tensores que se abrem e voltam a fechar dentro de uma passagem forward/backward — uma cadeia de pequenas coletivas, puro jogo de latência.
- Comunicação de passo com parameter server a taxas de atualização elevadas.
A latência quase não conta em:
- Agrupamento de inferência ao nível do pedido. A latência por pedido é dominada pela computação na GPU (TTFT, descodificação por token). 10 µs de rede são ruído ao lado de 50 ms de descodificação.
- Carregamento em volume do modelo a partir de armazenamento de objetos no arranque do trabalho. Limitado pelo débito.
- Escrita de checkpoints em armazenamento de rede. Escritas sequenciais grandes, afine para largura de banda.
- Shuffle do DataLoader com prefetch nos workers. Em lotes, limitado pelo débito.
A conclusão honesta: um servidor de nó único com 4 ou 8 GPU (a gama Kentino AI, o pão nosso de cada dia da Kentino) faz a comunicação entre GPU por NVLink ou PCIe, não pela rede. A rede serve para armazenamento, telemetria e a experiência ocasional com vários nós. Otimizar o fabric para a latência das coletivas só compensa com treino multi-nó a sério, que a maioria dos clientes da Kentino não faz. Para inferência pura e treino num só nó, 25 GbE com uma pilha de kernel limpa é suficiente.
O que fazer a seguir
Se está a comissionar um fabric novo, siga esta sequência:
-
ib_send_latentre todos os pares de nós. Qualquer valor acima de 1.5× a mediana é um sinal de alerta — cabo mau, ótica suja, topologia mal encaminhada. -
nccl-tests all_reduce_perfde 8 B a 1 GB. Guarde a curva. Compare com a referência publicada para a sua classe de NIC e de switch. -
Compare o
sockperf ping-pongem TCP com oib_send_latem RDMA. A diferença deve ser de 10–30×. Se for de 2×, ou a sua pilha de kernel é invulgarmente rápida (improvável) ou o caminho RDMA está avariado (provável — configuração de PFC errada, DCQCN mal montado, RDMA a cair para o caminho por software). - Volte a correr o nccl-tests sob carga. Force tráfego de armazenamento em simultâneo e observe a degradação da busbw. Clusters saudáveis degradam-se <20% sob carga mista realista; clusters doentes colapsam.
- Afine uma variável de cada vez. Coalescência, encaminhamento adaptativo, limiares de PFC, marcações ECN. Documente cada alteração. Mude três coisas e, se uma ajudar, não vai saber qual foi.
- Alerte com base na latência P99 das coletivas, não na média. A média esconde o problema; a P99 é o que o treino sente de facto.
Os artigos seguintes — o N07 sobre encaminhamento e controlo de congestão (ECMP, DCQCN, ECN), o N08 sobre a montagem de RDMA na prática e o K02 sobre a escolha de algoritmos de treino distribuído — aprofundam decisões específicas que este artigo apenas esboça.
A latência num fabric de IA não está no cabo, está em tudo o que envolve o cabo — e a solução passa quase sempre por encurtar o caminho de software antes de gastar mais em hardware.
Isto faz parte da Kentino Wiki, uma série de referência sobre computação de IA, robótica e os sistemas que os ligam. Comentários e correções são bem-vindos em info@kentino.com.