...

Afinidade IRQ do servidor e otimização da rede multi-core para um desempenho máximo

Optimizo os caminhos de rede de um servidor Afinidade IRQ e mapear filas RX/TX para núcleos para controlar a latência, a taxa de transferência e o jitter p99. Aqueles que usam CPUs com vários núcleos orquestram consistentemente interrupções, SoftIRQs, NAPI e NUMA de forma que os fluxos permaneçam alinhados com o núcleo, as trocas de contexto sejam reduzidas e o aplicativo responda de forma mensurável mais rapidamente.

Pontos centrais

  • Distribuição de IRQ determina quais os núcleos que transportam interrupções de hardware e evita pontos de acesso.
  • Proximidade NUMA reduz o acesso remoto e diminui os picos de latência.
  • SoftIRQs e NAPI controlar o processamento em lote e reduzir a carga nos núcleos.
  • RPS/RFS mantém os fluxos próximos dos segmentos de consumo.
  • Medição e fixação torna o desempenho mais determinístico.

Porque é que a afinidade de IRQ conta no funcionamento do servidor

As altas taxas de pacotes rapidamente colocam uma pressão sobre os núcleos individuais se todas as interrupções caírem em algumas CPUs, então eu distribuo a carga seletivamente para Pontos de acesso para evitar isso. Atribuo filas RX/TX aos núcleos apropriados para manter os caminhos de dados curtos e as caches quentes. Isso reduz as latências p95/p99 porque evito migrações desnecessárias e mantenho as etapas de processamento nos mesmos núcleos. Levo em conta a proximidade física da NIC, dos canais de memória e dos soquetes da CPU para que o caminho do pacote até o trabalhador da aplicação permaneça consistentemente rápido. Esta afinidade de núcleos cria uma estabilidade mensurável durante os picos de tráfego sem ter de atualizar o hardware imediatamente.

Equilíbrio de IRQ vs. afinidade fixa

O serviço standard equilíbrio do Iraque distribui as interrupções automaticamente, mas não conhece a lógica da minha aplicação, os alvos NUMA e os orçamentos de latência. Eu vinculei IRQs de rede críticos a núcleos selecionados, enquanto as interrupções ruidosas ou menos importantes foram movidas para outros núcleos. Esta ligação harmoniza-se com a fixação dos processos da aplicação para que o pipeline por fluxo permaneça consistente. Com tráfego intenso, evito redistribuições que geram sobrecarga adicional e enfraquecem o efeito da cache. Se quiser aprofundar o assunto, pode encontrar informações práticas de fundo neste guia: Equilíbrio de IRQ no centro de dados.

Afinidade de CPU, NUMA e o caminho curto de dados

Prefiro colocar os trabalhadores da aplicação e os IRQs de rede no mesmo NUMA-para que os acessos à memória permaneçam locais. Se uma placa de rede fica pendurada no nó 0, eu também configuro as filas RX associadas lá e vinculo os processos relevantes a esses núcleos. Desta forma, evito os dispendiosos acessos remotos à memória, que têm um grande impacto na latência a altas taxas de pacotes. Também incluo pares de hyper-threading para que as threads irmãs não interfiram umas com as outras. Esse triângulo de fixação de processos, afinidade de IRQ e topologia NUMA torna os caminhos de rede mais previsíveis e aumenta a taxa de transferência.

Compreensão de SoftIRQs, NAPI e conceção de filas

Após a interrupção do hardware, o kernel assume o processamento em SoftIRQs, frequentemente no mesmo núcleo que recebeu o IRQ. Quando a carga é alta, eu distribuo conscientemente a carga do SoftIRQ para aliviar os gargalos sem fragmentar desnecessariamente o caminho dos dados. As placas de rede com várias filas ajudam porque posso atribuir núcleos claramente definidos a cada fila e, assim, obter uma verdadeira paralelização. Eu uso a NAPI para processar pacotes em lotes, de modo que não ocorram tempestades de interrupções e o tempo de CPU seja utilizado de forma eficiente. Este artigo fornece conhecimentos básicos sobre este caminho: SoftIRQ e taxa de transferência da rede.

RPS/RFS e localidade do fluxo

Utilizo o RPS para uma distribuição mais alargada dos pacotes e defino RFS para que os fluxos acabem nas threads consumidoras. Isso mantém os acessos ao cache eficientes e o aplicativo se beneficia de tempos de resposta consistentes. Eu harmonizo a estratégia de hash da NIC, o número de filas e os conjuntos de CPU RPS para que nenhuma fila do kernel transborde. A afinidade de fluxo é particularmente eficaz para muitas solicitações curtas, como as geradas por APIs e microsserviços. Dessa forma, construo um pipeline no qual cada fluxo toca o mesmo núcleo com a maior frequência possível e evita migrações desnecessárias.

RSS, tabela de indirecção e XPS: controlo orientado do hashing

Para garantir que a distribuição comece de forma limpa na NIC, eu ajusto RSS (Receive Side Scaling) e a tabela de indirecção para que as filas de RX sejam atribuídas exatamente aos núcleos que mais tarde transportarão as threads da aplicação. Certifico-me de que o número de filas corresponde ao número de núcleos utilizados e que as chaves de hash permanecem estáveis para que os fluxos não se desviem inesperadamente. Se o algoritmo de hash mudar ou se a tabela de indirecção for substituída dinamicamente, isso destruirá a localidade do fluxo e promoverá falhas na cache.

No percurso TX, ativo adicionalmente XPS (Transmit Packet Steering) para que os pacotes de saída sejam enviados pelo núcleo que está processando a aplicação. Isso também mantém os caches TX perto do trabalhador, e o caminho da fila do soquete para a fila da NIC permanece curto. Eu mantenho os mapeamentos de RX e TX consistentes, documento-os por interface e defino-os em scripts de inicialização para que uma reinicialização não desfaça a arquitetura.

Coalescência de interrupções: ponderação da latência em relação ao rendimento

Com Coalescência Eu resumo as interrupções para reduzir a sobrecarga, mas presto atenção aos limites de latência da minha aplicação. Para streaming e VoIP, tenho tendência para manter os intervalos curtos, enquanto as transferências em massa toleram bem lotes mais longos. Eu testo passo a passo, meço p95/p99 e verifico quedas, retransmissões e utilização da CPU por núcleo. Só então anoto as configurações e as documento para cada host e NIC. Este artigo prático fornece uma visão mais profunda do compromisso: Explicação da coalescência de interrupções.

Dosear corretamente as descargas e a agregação

Eu fixo GRO/LRO para reduzir a sobrecarga da CPU, mas verificar se as minhas cargas de trabalho beneficiam de lotes maiores. As APIs sensíveis à latência geralmente respondem melhor quando o GRO é moderado e o LRO está desligado, porque grandes super-pacotes podem exacerbar os efeitos de bloqueio de cabeça de linha. Para transferências em massa, replicação ou cópias de segurança, utilizo o GRO/GSO/TSO de forma mais agressiva, desde que o lado do recetor permaneça estável e a utilização da CPU diminua.

Descargas de checksum e TSO/GSO reduzir significativamente a carga na CPU, mas certifico-me de que as caixas intermédias, os túneis ou as incompatibilidades de descarregamento (por exemplo, com determinados encapsulamentos) funcionam corretamente. Se ocorrerem anomalias, reduzo gradualmente as descargas individuais e meço os efeitos na taxa de transferência, retransmissões e tempo de CPU. O objetivo é um conjunto que permaneça estável em toda a linha e previsível em horários de pico.

Isolamento da CPU, agendador e estados de energia

Para orçamentos de latência rígidos, isolo núcleos para caminhos de rede e trabalhadores de aplicações. Com Isolamento da CPU e uma estratégia de manutenção simples, evito que as tarefas do sistema, Kthreads ou interrupções do temporizador entrem nos núcleos „quentes“. Eu também corrijo o Regulador da CPU para „desempenho“ e limitar a profundidade Estados C, se estes causarem latências de despertar. Mantenho-me atento às temperaturas do núcleo, uma vez que o apodrecimento térmico pode arruinar quaisquer retoques finais.

A escolha de Programação das aulas influencia a previsibilidade. Eu dou prioridade às threads relacionadas à rede, mas não as executo de forma agressiva e exclusiva, para que elas não compitam com o ksoftirqd pelo tempo de CPU. Verifico regularmente se o ksoftirqd está a iniciar em núcleos individuais - um sinal claro de que a carga do SoftIRQ é demasiado elevada ou está incorretamente distribuída.

Polling ocupado e caminhos de baixa latência

Quando os microssegundos contam, defino Sondagem ocupada de uma forma direcionada. As aplicações podem definir janelas de sondagem para sockets selecionados, de modo a extrair pacotes diretamente dos orçamentos NAPI sem esperar por interrupções. Escolho intervalos de sondagem curtos para evitar queimar tempo de CPU e limitar esta técnica a caminhos quentes com tráfego constante. Em paralelo, adapto orçamentos netdev moderadamente, de modo a que os lotes sejam suficientemente grandes sem que o resto do sistema fique a morrer à fome.

Disciplina e ritmo das filas de espera da rede

Configurei o qdisc por interface para corresponder à carga de trabalho. Eu uso disciplinas modernas como fq/fq_codel para regular o ritmo e o comprimento das filas, a fim de suavizar as explosões e evitar o bufferbloat. Em configurações de filas múltiplas, eu combino isso com mqprio, para que as classes de tráfego permaneçam consistentemente atribuídas às filas HW corretas. Juntamente com BQL (Byte Queue Limits) no controlador reduz a latência sob carga total porque a fila não cresce incontrolavelmente.

É importante interagir com XPS no caminho TX: Mapeio as filas de envio para os núcleos nos quais os fluxos RX correspondentes também aterram. Dessa forma, ambas as direções de um fluxo permanecem próximas à CPU e eu obtenho tempos de resposta mais estáveis com protocolos bidirecionais (por exemplo, HTTP/2, gRPC).

Fluxo de trabalho prático no Linux

Começo com um registo de carga, verifico a distribuição da CPU em top/htop, olho para /proc/interrupts e /proc/softirqs e leio as estatísticas ethtool para reconhecer os estrangulamentos e preparar o próximo Fluxo de trabalho-passo. Em seguida, determino os IDs de IRQ das filas de NICs relevantes e defino máscaras de CPU adequadas que ocupam os núcleos uniformemente e levam em conta o NUMA. Em seguida, eu fixo os trabalhadores da aplicação via taskset ou systemd-CPUAffinity nos mesmos núcleos que também servem as filas associadas. Só ativo o RPS/RFS quando ele reforça a localidade do fluxo e mantenho a configuração consistente por interface. Por fim, meço novamente a taxa de transferência, a latência e o jitter antes de implementar as alterações de maneira uniforme em vários hosts.

Medição, evitar p95/p99 e regressões

Não me baseio em intuições, mas meço latências, taxas de erro e utilização dos núcleos antes e depois de cada ronda de afinação, para que p99 permanece estável. Também controlo as alterações de contexto, as taxas de migração e a carga por tipo de SoftIRQ para identificar efeitos secundários ocultos numa fase inicial. Mantenho os testes reproduzíveis, utilizo os mesmos conjuntos de dados e versões fixas para que os resultados permaneçam comparáveis. Descubro as regressões com verificações cruzadas em condições de pico e de inatividade, bem como com execuções de longa duração. Só quando as métricas, os registos e os traços da aplicação coincidem é que declaro a configuração como o novo estado de base.

Virtualização, contentores e SR-IOV

Em ambientes virtualizados, asseguro-me de que vCPUs, A memória e as vNICs da VM estão localizadas no mesmo nó NUMA em que a NIC física associada termina. Sempre que possível, eu uso SR-IOV, para que o caminho de dados seja curto e as IRQs possam ser vinculadas diretamente aos núcleos convidados. Eu conecto as vCPUs das VMs críticas aos núcleos dedicados do host e certifico-me de que as IRQs do host e as IRQs do convidado não se sobreponham. Em configurações de contêineres, eu defino cpusets e classes de QoS „garantidas“ para que os contentores de trabalho e os seus IRQs de rede recebam tempo de CPU de forma previsível.

Verifico se o irqbalance deve ter a liderança no convidado ou no anfitrião - caso contrário, o duplo „automático“ produz uma desfocagem. Com o virtio, defino várias filas e mapeio-as de forma limpa para vCPUs para permitir o trabalho paralelo. Se o vhost-net utiliza núcleos individuais do host, eu redistribuo os backends e mantenho os threads do vhost NUMA próximos à NIC física.

Resolução de problemas: reconhecer padrões rapidamente

  • Núcleos saturados, ksoftirqd ativo: Aproximar as filas de RX, verificar o número de filas, ajustar o RPS/RFS ou aumentar ligeiramente a coalescência.
  • Tremulação do p99: Verificar o desvio NUMA, verificar C-States/Governor, ajustar as descargas e os tamanhos GRO passo a passo.
  • Muitas retransmissões/quedas: Verificar os tamanhos dos anéis RX/TX, qdisc e BQL, verificar a consistência da tabela de indirectos e do XPS.
  • Fluxos distribuídos de forma desigual: Equilibrar o hash RSS e a tabela de indirecções, considerar a fixação de fluxos quentes, manter a semente do hash estável.
  • Problema apenas com a VM: Colocar os backends do vhost/virtio perto do NUMA, avaliar o SR-IOV, separar as IRQs entre o host e o convidado.

Incluir aplicações e bases de dados

Um caminho de rede limpo é de pouca utilidade se os servidores de aplicações ou as bases de dados não estiverem a funcionar em paralelo, razão pela qual configurei o Trabalhador-Número de threads, pools de threads e limites de conexão para os núcleos disponíveis. Atribuo os trabalhadores do NGINX ou do HAProxy aos núcleos adequados para que correspondam às filas de RX. Dimensiono o PHP-FPM, o Node.js, o Java ou o Go para que favoreçam o domínio NUMA local e utilizem várias instâncias, se necessário. Eu integro caches como Redis ou Memcached perto da CPU e presto atenção aos seus próprios parâmetros de rede e thread. Somente a interação entre afinidade de IRQ, fixação de processos e escalonamento de aplicativos fornece o aumento percetível na latência e na taxa de transferência.

Cenários de alojamento com grandes benefícios

Invisto principalmente no deep tuning quando as APIs geram muitos pedidos curtos ou quando Tempo real-As comunicações, como VoIP e chats, exigem valores de jitter baixos. As configurações de comércio eletrónico com picos de carga beneficiam porque os fluxos de checkout são sensíveis à latência. Os anfitriões multi-tenant com elevada densidade beneficiam porque os núcleos dedicados por fila reduzem os efeitos de vizinhança. Os serviços de streaming também podem obter um maior débito por euro sem a aquisição imediata de novo hardware. Os custos continuam a ser calculáveis, desde que as alterações sejam mensuráveis e implementadas com exatidão.

Tabela de referência rápida: Núcleos, filas de espera, ferramentas

Eu uso o seguinte Tabela como um lembrete quando eu configurar novos hosts ou recalibrar configurações existentes. Ele mostra alvos típicos, medidas apropriadas, ferramentas comuns do Linux e o efeito pretendido na latência e na taxa de transferência. Não o utilizo de forma dogmática, mas como ponto de partida para uma série de medições com tráfego real. Se a arquitetura da placa de rede ou a topologia NUMA variar, adapto a seleção do núcleo. Continua a ser importante manter a documentação para cada anfitrião e manter as alterações rastreáveis.

Objetivo Medida Ferramenta/localização do Linux Efeito esperado
Distribuir a carga de IRQ Ligar pistas aos núcleos /proc/irq/*/smp_affinity Menos hotspots, latência mais constante
Aumentar a localidade do fluxo Definir conjuntos de CPU RPS/RFS /sys/class/net/*/queues/*/rps_cpus Menos migrações, melhores caches
Controlar o processamento de lotes Afinar a NAPI/coalescência ethtool -C / driver defaults Menor sobrecarga, jitter controlado
Emparelhar a aplicação e o IRQ Trabalhador com pinos conjunto de tarefas, systemd CPUAffinity Caminho mais curto, menor p99
Evitar NUMA Co-localizar dispositivos e núcleos numactl, lscpu, lspci -vv Menos acesso remoto, mais rendimento

Melhores práticas que funcionam a longo prazo

Apenas altero uma alavanca de controlo por cada ronda de testes, documento as métricas e guardo os resultados. Documentação no repositório do host. Mantenho as configurações consistentes, descrevendo claramente os mapeamentos de fila para núcleo e usando scripts para replicação. Monitorizo os registos de quedas, retransmissões e tempos limite e correlaciono-os com as métricas do kernel. Incluo o hipervisor e o nível de armazenamento na análise para que não subsistam estrangulamentos obscuros. Tenho reversões prontas para o caso de os testes mostrarem efeitos negativos ou as cargas de trabalho mudarem.

Brevemente resumido

Consigo obter o máximo desempenho da rede utilizando interrupções, Tacos e trabalhadores e, assim, manter estável o caminho de dados por fluxo. O IRQ Affinity distribui a carga de hardware de forma sensata, enquanto os SoftIRQs, NAPI e RPS/RFS tornam o processamento eficiente. A proximidade NUMA protege contra desvios de memória evitáveis e reduz a instabilidade. O ajuste passo a passo com medições reproduzíveis evita configurações incorrectas e mostra um progresso real. Se pensar nestes blocos de construção em conjunto, pode utilizar com confiança as capacidades dos modernos servidores multi-core para serviços críticos em termos de latência.

Artigos actuais