Afinidade NUMA: eliminando a latência oculta em storage NVMe multi-socket

      Roberto Lemos 10 min de leitura
      Afinidade NUMA: eliminando a latência oculta em storage NVMe multi-socket

      Descubra como o desalinhamento NUMA mata a performance de arrays NVMe em servidores multi-socket e aprenda a configurar a afinidade de CPU e IRQ corretamente.

      Compartilhar:

      Você investiu pesado em infraestrutura. Comprou servidores dual-socket de última geração, populou os bays com drives NVMe Gen4 ou Gen5 e configurou um array RAID ou ZFS robusto. A expectativa era performance linear, mas a realidade do monitoramento mostra outra coisa: latência de cauda (tail latency) errática e picos inexplicáveis de wait time, mesmo quando o throughput total está longe do limite teórico.

      Se você já verificou o wear leveling dos SSDs, revisou as configurações de queue depth e nada mudou, o problema provavelmente não está no disco. O problema é a física da sua placa-mãe. Bem-vindo ao inferno silencioso da topologia NUMA (Non-Uniform Memory Access) mal configurada em ambientes de storage de alta performance.

      Para um arquivo de vídeo sendo transmitido sequencialmente, alguns microssegundos não importam. O buffer resolve. Para um banco de dados transacional processando milhares de operações aleatórias de 4K por segundo, atravessar a ponte entre processadores é a diferença entre um SLA cumprido e uma aplicação engasgada.

      Resumo em 30 segundos

      • Geografia importa: Em servidores multi-socket, os slots PCIe (e seus NVMes) são conectados fisicamente a apenas um processador. Acessá-los pelo processador "vizinho" custa caro.
      • O pedágio da interconexão: Cruzar o barramento entre CPUs (UPI/Infinity Fabric) aumenta a latência e polui o cache, criando gargalos invisíveis em benchmarks sintéticos simples.
      • O padrão é inimigo: O agendador padrão do sistema operacional (scheduler) prioriza o balanceamento de carga entre cores, ignorando frequentemente a localidade do dado, o que destrói a performance de IOPS.

      A arquitetura física: entendendo o mapa do território

      Para resolver problemas de latência em storage moderno, precisamos parar de tratar o servidor como uma caixa preta unificada. Um servidor com dois soquetes de processador (Dual Socket) não é um computador grande; são, na prática, dois computadores menores conectados por um cabo de rede de altíssima velocidade soldado na placa-mãe.

      Cada processador possui suas próprias pistas PCIe (PCIe lanes). Quando você espeta um drive NVMe no chassi, ele não está conectado "ao servidor". Ele está conectado eletricamente ao Root Complex de um processador específico (CPU 0 ou CPU 1).

      Esquema de topologia NUMA mostrando o caminho físico que os dados percorrem em acessos locais versus acessos remotos via interconexão. Figura: Esquema de topologia NUMA mostrando o caminho físico que os dados percorrem em acessos locais versus acessos remotos via interconexão.

      Se o seu banco de dados (PostgreSQL, MySQL, Oracle) tem uma thread rodando na CPU 1, mas precisa ler um bloco de dados de um NVMe conectado à CPU 0, essa solicitação precisa viajar pelo barramento de interconexão (Intel UPI ou AMD Infinity Fabric).

      O custo do "salto" (hop)

      Em discos mecânicos (HDD) ou mesmo SSDs SATA antigos, a latência da mídia (milissegundos) era tão alta que o tempo de trânsito na placa-mãe era irrelevante. Era como reclamar do tempo para abrir a porta da garagem antes de uma viagem de 1000 km.

      Com NVMe Gen4 e Gen5, a latência da mídia caiu para a casa dos microssegundos (frequentemente abaixo de 10µs para leituras). De repente, o tempo para atravessar o link QPI/UPI (que pode adicionar de 100 a 300 nanossegundos, mais a contaminação de cache) tornou-se uma fatia percentual significativa da transação total.

      💡 Dica Pro: Em cargas de trabalho mistas, o acesso remoto NUMA não afeta apenas a latência. Ele consome largura de banda da interconexão (Interconnect Bandwidth), que também é usada para manter a coerência de cache entre as CPUs. Storage mal configurado pode deixar sua CPU mais lenta para processamento puramente computacional.


      O agendador do SO e a ilusão de equilíbrio

      Sistemas operacionais modernos, como o Linux, são projetados para multitarefa de propósito geral. O scheduler (agendador) do kernel tem como missão principal manter todos os núcleos da CPU ocupados de forma igualitária.

      Se a CPU 0 está sobrecarregada com interrupções de rede, o kernel pode decidir mover sua thread de banco de dados para a CPU 1, que está ociosa. Do ponto de vista de CPU, isso é ótimo. Do ponto de vista de Storage I/O, isso pode ser catastrófico se os discos estiverem na CPU 0.

      O fenômeno da latência de cauda (Tail Latency)

      A latência média pode parecer boa, mas a latência de cauda (o percentil 99 ou p99) dispara. Isso acontece porque o acesso remoto não é apenas "mais longe"; ele sofre de contenção. Se o link entre as CPUs estiver ocupado sincronizando memória RAM, seu pedido de I/O entra numa fila de espera.

      Isso gera o comportamento que todo DBA teme: a query que geralmente leva 2ms, subitamente leva 50ms sem motivo aparente, travando a aplicação.

      Comparativo de consistência de latência: a curva Figura: Comparativo de consistência de latência: a curva "NUMA Aware" demonstra previsibilidade, enquanto a "NUMA Ignorant" revela a perigosa latência de cauda.


      Diagnóstico: mapeando a topologia real

      Antes de sair fixando processos, você precisa saber onde seus discos "moram". Não confie na documentação do chassi; verifique como o kernel enxerga o hardware.

      A ferramenta lstopo (parte do pacote hwloc) é sua melhor amiga aqui. Ela desenha um mapa visual da sua arquitetura. Porém, para scripts e verificações rápidas, o sistema de arquivos /sys é a fonte da verdade.

      Para verificar a qual nó NUMA um dispositivo NVMe pertence:

      cat /sys/class/block/nvme0n1/device/numa_node
      

      Se o resultado for 0, o disco está ligado ao primeiro socket. Se for 1, ao segundo. Se for -1, o sistema não conseguiu determinar (comum em virtualização sem passthrough correto ou BIOS mal configurada).

      Verificando a penalidade

      Você pode testar a diferença de performance usando ferramentas de benchmark como o fio, forçando a afinidade para o nó local e depois para o nó remoto.

      Exemplo conceitual de teste:

      1. Teste Local: Rodar fio preso à CPU 0 acessando disco na CPU 0.

      2. Teste Remoto: Rodar fio preso à CPU 1 acessando disco na CPU 0.

      A diferença no throughput máximo (IOPS) e, principalmente, na latência média e p99, revelará o "custo do pedágio" na sua arquitetura específica.


      Alinhamento vertical: a estratégia de mitigação

      A solução para eliminar essa latência oculta é o Alinhamento Vertical. O objetivo é garantir que todo o fluxo de dados, desde a interrupção de hardware até a thread da aplicação, ocorra dentro do mesmo domínio NUMA.

      1. Afinidade de Interrupções (IRQ Affinity)

      Quando um NVMe completa uma operação, ele envia uma interrupção para a CPU. Por padrão, o serviço irqbalance no Linux tenta distribuir essas interrupções entre todos os núcleos disponíveis.

      Para storage de ultra-performance, o irqbalance é frequentemente um vilão. Ele pode jogar a interrupção de conclusão de I/O para uma CPU do outro lado da placa-mãe, forçando uma troca de contexto cara e desnecessária.

      ⚠️ Perigo: Desativar o irqbalance sem configurar manualmente a afinidade das interrupções pode fazer com que a CPU 0 (core 0) receba 100% das interrupções e trave o sistema. A configuração manual deve ser feita com scripts que mapeiam as filas do NVMe (queues) para os cores do mesmo socket NUMA.

      2. Pinagem de Processos (Process Pinning)

      Para bancos de dados críticos, o uso de numactl é mandatório. Você deve instruir o banco de dados a preferir alocar memória e rodar threads no nó NUMA onde o storage reside.

      Se você tem um servidor com dois sockets e dois arrays de disco independentes, uma arquitetura comum é rodar duas instâncias do banco de dados (sharding), cada uma "presa" (pinned) a um socket e seus respectivos discos.

      Tabela Comparativa: Acesso Local vs. Remoto

      Característica Acesso Local (NUMA Local) Acesso Remoto (NUMA Remote)
      Latência Base Nativa do dispositivo NVMe Nativa + Latência do Interconnect (100-300ns+)
      Estabilidade (Jitter) Baixa variabilidade Alta variabilidade (depende do tráfego do link)
      Uso de CPU Eficiente (Cache hit rate alto) Ineficiente (Cache pollution, coerência cara)
      Throughput Máximo Limitado pelo PCIe/Drive Limitado pela largura de banda do Interconnect (UPI/IF)
      Impacto no Vizinho Isolado Afeta performance de memória do outro socket

      Visualização do impacto no cache: o acesso local preserva a eficiência dos ciclos de CPU, enquanto o acesso remoto introduz latência e contenção. Figura: Visualização do impacto no cache: o acesso local preserva a eficiência dos ciclos de CPU, enquanto o acesso remoto introduz latência e contenção.


      O futuro: CXL e a desagregação

      Estamos caminhando para um futuro onde o protocolo CXL (Compute Express Link) promete mitigar parte desses problemas ao permitir coerência de memória e cache mais eficiente entre dispositivos. No entanto, a física da distância permanece. Mesmo com CXL, a localidade dos dados continuará sendo o fator determinante para a performance máxima.

      Dispositivos no formato E1.S e E3 (EDSFF) estão trazendo densidades que saturam ainda mais os barramentos. Um único drive PCIe Gen5 x4 pode entregar 14 GB/s. Num servidor com 24 drives, a largura de banda total do storage excede em muito a capacidade de qualquer link de interconexão entre CPUs. Isso torna a afinidade NUMA não mais uma "otimização de luxo", mas um requisito funcional para não desperdiçar o hardware adquirido.


      Recomendação do Arquiteto

      Não trate a afinidade NUMA como um ajuste de "tuning fino" para ganhar 1% de performance. Em cargas de trabalho de storage NVMe modernas, ignorar a topologia física pode custar 30% a 40% do desempenho pelo qual você pagou, além de introduzir uma imprevisibilidade inaceitável para aplicações de tempo real.

      Minha recomendação direta: audite seus servidores de banco de dados hoje. Use lstopo. Se você encontrar seus processos de DB rodando na CPU 0 e seus discos NVMe na CPU 1, você acabou de encontrar a fonte daquela lentidão que ninguém conseguia explicar. Realinhe suas cargas, configure a afinidade de IRQ e deixe a física trabalhar a seu favor, não contra você.


      Referências & Leitura Complementar

      1. ACPI Specification Version 6.5 (2022): System Resource Affinity Table (SRAT) definition. Define como o firmware comunica a topologia NUMA ao SO.

      2. Intel Xeon Scalable Processor Datasheets: Detalhes sobre a arquitetura Mesh e latências de Ultra Path Interconnect (UPI).

      3. ScyllaDB Engineering Blog: "Seastar: The future of C++ frameworks". Discussão aprofundada sobre arquitetura shared-nothing e per-core design para evitar overhead NUMA.

      4. Linux Kernel Documentation: Documentation/admin-guide/mm/numa_memory_policy.rst. Guia oficial sobre políticas de memória e agendamento no kernel Linux.


      Perguntas Frequentes (FAQ)

      O que é afinidade NUMA no contexto de storage? É a prática técnica de garantir que o processo que solicita o dado (como um banco de dados) esteja sendo executado no mesmo socket de CPU onde o disco NVMe está fisicamente conectado. Isso evita que os dados tenham que trafegar pelo barramento de interconexão entre processadores, reduzindo a latência.
      Por que o NUMA afeta mais NVMe do que SSDs SATA? É uma questão de proporção. Discos SATA e HDDs são lentos o suficiente (latência em milissegundos) para mascarar o tempo extra da interconexão entre CPUs. Já os drives NVMe são extremamente rápidos (microssegundos), fazendo com que o tempo de trânsito entre sockets se torne um gargalo percentual significativo e perceptível na latência total.
      Como verifico a topologia NUMA dos meus discos no Linux? A maneira mais visual é utilizar a ferramenta `lstopo` (do pacote hwloc). Para uma verificação rápida via terminal, você pode ler o arquivo `/sys/class/block/nvmeXn1/device/numa_node`. Se o resultado for 0, pertence ao primeiro nó; se for 1, ao segundo.
      #NUMA #NVMe #Latência de Storage #Performance Tuning #Linux Kernel #Multi-socket #Afinidade de CPU
      Roberto Lemos
      Assinatura Técnica

      Roberto Lemos

      Arquiteto de Workloads

      "Projeto infraestrutura onde o perfil de I/O dita as regras. Sei que a latência do acesso aleatório de um banco difere da vazão sequencial de vídeos. Mapeio o hardware exato para cada aplicação."