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.

TX da app
memcpy, descritor
0.1–1 µs
→TX da NIC
Switch
cut-through
250–600 ns
→RX da NIC
RX da app
descodificar, acordar o consumidor
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 50 acrescenta 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 memcpy de 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.

Busbw de allreduce — clusters H100 / InfiniBand NDR num só rack
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_allreduce ao 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:

  1. ib_send_lat entre 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.
  2. nccl-tests all_reduce_perf de 8 B a 1 GB. Guarde a curva. Compare com a referência publicada para a sua classe de NIC e de switch.
  3. Compare o sockperf ping-pong em TCP com o ib_send_lat em 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).
  4. 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.
  5. 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.
  6. 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.