...

softirq cpu hosting: otimizar o desempenho do servidor e o débito da rede

Eu mostro como softirq cpu juntamente com a NAPI, a distribuição de IRQ e o design de filas limita ou liberta o débito da rede no alojamento. Com pontos de medição claros, afinação direcionada e afinidades limpas, reduzo Latências e aumentar consistentemente a taxa de transferência de pps em servidores produtivos.

Pontos centrais

Estas ideias nucleares transportam os pacotes de rede de forma eficiente através da CPU, do kernel e da placa de rede - e mantêm os tempos de resposta a um nível mínimo. constante baixo.

  • Orçamento NAPI ajuste fino: Mais pacotes por sondagem reduzem as despesas gerais e suavizam o Carga da CPU.
  • Equilíbrio de IRQ e afinidade: evitar hotspots, aumentar os acessos à cache, Picos de latência pressionar.
  • Fila múltipla, RSS/RPS/XPS: paralelizar fluxos, manter o alinhamento NUMA, pps aumentar.
  • Descargas utilizar conscientemente: GRO/LRO, TSO, avaliar a coalescência, Jitter em vista.
  • Isolamento e Busy Polling: Tempos de resposta previsíveis em sistemas dedicados Núcleos.

Noções básicas: O que acontece no kernel durante o tráfego de rede

Um pacote chega primeiro a uma interrupção de hardware, após o que o kernel assume o trabalho em SoftIRQs e loops de sondagem NAPI. Eu me certifico de que a fase rápida do HardIRQ permaneça realmente curta e que a lógica real se mova para o contexto correto para que o tempo de CPU não se esgota. As threads ksoftirqd só entram em cena se o processamento direto não for possível, o que leva rapidamente a filas de espera sob carga contínua. É exatamente aqui que surge o tempo de espera, que se reflecte no aumento do TTFB e na flutuação do débito. Se você quiser se aprofundar, pode encontrar conhecimento prático sobre o processamento de IRQ neste artigo sobre Tratamento de interrupções e desempenho da CPU, que utilizo para a categorização.

NAPI, SoftIRQs e ksoftirqd: controlar a latência em vez de a gerir

A NAPI reduz as tempestades de interrupções, recolhendo vários pacotes por execução dentro de um orçamento definido e minimizando assim o tempo de interrupção. Despesas gerais diminui. Se o orçamento não for suficiente, as encomendas amontoam-se, a procura aquece e o Latência aumenta de forma mensurável. Em tais situações, eu verifico sistematicamente /proc/softirqs e /proc/net/softnet_stat para visualizar quedas, time_squeeze ou filas transbordando. Depois, aumento gradualmente o net.core.netdev_budget ou net.core.netdev_budget_usecs e monitorizo a carga da CPU, a distribuição p95/p99 e as perdas de pacotes em paralelo. O truque é conseguir trabalho suficiente por sondagem sem atrapalhar a execução interativa das threads do userland.

Equilíbrio e afinidade de IRQ: evitar pontos de acesso, aumentar os acessos à cache

Um único núcleo com todos os IRQs da NIC torna-se um gargalo porque tem de servir interrupções, IRQs suaves, bem como threads de aplicações; assim, distribuo IRQs direcionado. O serviço irqbalance ajuda, mas para taxas de pps elevadas, mapeio explicitamente as filas RX/TX através de afinidade para núcleos. Nos sistemas NUMA, as filas de espera são ligadas a núcleos do mesmo nó para evitar acessos remotos à memória. Os threads de aplicação são executados em núcleos vizinhos mas separados, o que melhora a localidade da cache e a capacidade de programação. Este guia de distribuição estratégica fornece uma boa visão geral do Equilíbrio de IRQ no centro de dados, que utilizo como referência para afinações.

Filas múltiplas, RSS/RPS/XPS: utilização correta da paralelização

As placas de rede modernas vêm com várias filas RX/TX, que posso controlar através de RSS para os fluxos e assim conseguir um paralelismo real. Se a placa oferecer muito poucas filas, uso o RPS/XPS para fazer ajustes no software, a fim de distribuir os pacotes de forma sensata entre os fluxos. núcleos para enviar. A distribuição limpa de hash é importante para que um fluxo permaneça sempre na mesma CPU e não ocorram distorções caras no cache. Ao mesmo tempo, mantenho os caminhos TX e RX próximos para evitar contenção de bloqueio e acessos desnecessários entre nós. Isso aumenta a taxa de transferência de pps sem que um único núcleo puxe os freios.

Afinidade com a CPU diretamente no espaço do utilizador: pensamento de ponta a ponta

Planeio o caminho dos dados desde a NIC-IRQ através das filas NAPI até às threads de trabalho da aplicação, de modo a que os pacotes cheguem ao seu destino sem hooks desnecessários e a Tempo de resposta permanece constante. Para o conseguir, separo consistentemente os núcleos para interrupções/softIRQs dos núcleos de aplicações e crio núcleos Afinidade-regras. Servidores Web, proxies reversos e bancos de dados recebem conjuntos fixos de CPU que estão próximos aos núcleos IRQ para manter os caminhos curtos. Além disso, eu defini o regulador de CPU para desempenho para que as mudanças de relógio não empurrem o jitter para o p99. Esta atribuição consistente torna o comportamento previsível e ajuda a diagnosticar gargalos de forma limpa.

Descargas, GRO/LRO, firewall e eBPF: Poupe carga sem andar às cegas

Guardar a descarga da soma de controlo, TSO e coalescência tempo de CPU, No entanto, podem alterar o tamanho dos pacotes, o comportamento das rajadas e o jitter, razão pela qual meço especificamente os efeitos. O GRO/LRO agrupa os quadros e reduz a carga na pilha, mas para os requisitos em tempo real, decido numa base situacional sobre Desativação ou de uso limitado. As tabelas Conntrack e as cadeias nftables/iptables profundas custam relógios, por isso arrumo as regras supérfluas e simplifico os caminhos. Se necessário, recorro ao eBPF (XDP, tc-BPF) para tomar decisões antecipadas na placa de rede e evitar caminhos dispendiosos. Um bom ponto de partida para a prática do ajuste fino é esta visão geral de Coalescência de interrupções, que tenho em conta para orçamentos de latência sensíveis.

Sondagem de ocupação e isolamento da CPU: Bloqueio dos tempos de resposta

Para alvos de latência difícil, eu uso polling ocupado para que os sockets do espaço do utilizador apanhem os pacotes ainda mais cedo e Tempos de espera encurtar. Isto aumenta a carga, mas proporciona-me distribuições muito estreitas de p99 para API ou cargas de trabalho de negociação em servidores dedicados Núcleos. Além disso, isolo os núcleos com isolcpus=, nohz_full= e rcu_nocbs= para que os temporizadores, a RCU e os serviços do sistema sejam executados apenas nas CPUs de manutenção. Esta separação evita interferências nos núcleos de latência e torna o comportamento reproduzível. O resultado é um roteiro claro: núcleos dedicados, recolha precoce de pacotes, orçamentos definidos.

Monitorização e resolução de problemas: do sintoma à causa

Começo com pps, taxa de transferência e carga do núcleo, depois verifico as quedas e a atividade do ksoftirqd-threads ao longo do tempo para reconhecer padrões de forma fiável. Ferramentas como sar, htop, ss, nload e ethtool mostram-me quando e onde ocorre o congestionamento e se o Tacos atingem os seus limites. As distribuições são importantes em vez dos valores médios para que os picos noturnos, as janelas do cron ou as campanhas não se percam. Eu correlaciono os picos de TTFB com a distribuição de IRQ, o orçamento da NAPI e as configurações de descarregamento para fazer ajustes direcionados. Uma afinidade de IRQ ajustada ou um novo orçamento NAPI adaptado é muitas vezes suficiente para reduzir visivelmente os tempos limite.

Parâmetros de afinação num relance

A visão geral que se segue ajuda-me a utilizar as alterações de forma sensata e a atribuir claramente os efeitos antes de efetuar alterações permanentes. lançamentos plano. Testo cada ajuste iterativamente, meço as distribuições de latência e observo os efeitos colaterais em CPU e memória. Só altero um ponto por janela de teste para que a causa e o efeito permaneçam claros. Em seguida, documento os resultados e defino valores-limite para os alertas. Desta forma, obtenho melhorias reproduzíveis sem correr o risco de surpresas no tráfego produtivo.

Parâmetro/caraterística Efeito no percurso dos dados Quando angariar/ativar Riscos/efeitos secundários
net.core.netdev_budget Mais pacotes por sondagem NAPI Para gotas em softnet_stat As sondagens mais longas substituem os tópicos dos utilizadores
net.core.netdev_budget_usecs Limitar a janela de tempo por sondagem Para o jitter devido a grandes rajadas Demasiado pequeno: mais alterações de contexto
RSS/RPS/XPS Distribuir fluxos pelos núcleos Para hotspots num núcleo Hashes incorrectos: distorções da cache
Afinidade IRQ Associar IRQs perto do núcleo Com NUMA-Missmatch A má afetação de recursos cria novos focos de tensão
GRO/LRO/TSO Reduz o número de pacotes Para estrangulamento da CPU Jitter, rajadas maiores
Sondagem ocupada Recolha antecipada de encomendas Para alvos difíceis do p99 Maior consumo de CPU

Anéis RX/TX e profundidade de cue: dimensionar corretamente os buffers

Mesmo com IRQs corretamente distribuídos e orçamentos adequados, os anéis de placa de rede demasiado pequenos ou demasiado grandes podem reduzir o desempenho. Por isso, verifico os tamanhos dos anéis RX/TX da placa e adapto-os aos objectivos de carácter burst e latência. Os anéis demasiado pequenos provocam quedas na placa de rede durante os picos de tráfego, visíveis como rx_missed_errors ou fifo_errors nas estatísticas do controlador. Os anéis demasiado grandes disfarçam o congestionamento, aumentam a latência e criam longos trailing edges em p95/p99. Estou à procura do meio termo: buffer suficiente para absorver rajadas curtas, mas não tanto que os pacotes “envelheçam” nas filas.

Para além disso, analiso o lado do anfitrião tx_queue_len e o Qdisc utilizado. Com sch_fq ou fq_codel posso suavizar o comportamento de burst e distribuir grandes pacotes TSO através de pacing. Isso reduz microbursts na porta do switch e torna a curva de latência mais suave - importante para cargas de trabalho mistas nas quais pequenos RPCs são executados juntamente com grandes uploads. Eu monitorizo as estatísticas ethtool e correlaciono-as com softnet_stat para reconhecer se o congestionamento está a ocorrer no anel NIC, no backlog netdev ou no Qdisc.

MTU, jumbo frames e segmentação

O MTU é uma alavanca clássica que é frequentemente subestimada. Os Jumbo frames reduzem o número de pacotes por Gbit/s e reduzem a carga na CPU - mas apenas se o caminho for verdadeiramente capaz de jumbo de ponta a ponta. Por isso, valido sistematicamente as estações remotas, os comutadores e os túneis. Assim que houver uma fragmentação de volta a 1500 em algum lugar, há o risco de problemas de MTU no caminho, retransmissões e Jitter. Em centros de dados com comunicação dominante Este/Oeste, vale a pena uma estratégia homogénea de 9k, enquanto 1500 é frequentemente a escolha mais estável para cargas de trabalho viradas para a Internet.

Avalio sempre o MTU em conjunto com TSO/GSO/GROUm agrupamento demasiado agressivo pode levar a grandes explosões no TX que enchem os buffers a montante e geram picos de latência. O objetivo é um caminho consistente: segmentação sensata no transmissor, mecanismos de estimulação suficientes e GRO que poupam trabalho no lado do recetor sem frustrar os requisitos de tempo real.

UDP, QUIC e cargas de trabalho de fluxo contínuo: considere as especificidades

Nem todo o tráfego é TCP. UDPperfis pesados (DNS, VoIP, QUIC, telemetria) comportam-se de forma diferente em RSS/RPS e GRO. As pilhas modernas suportam UDP-GRO/GSO, o que pode reduzir a carga na CPU - utilizo-o seletivamente e meço se os riscos de reordenação ou o aumento do jitter aumentam. Para cargas QUIC/HTTP3, a distribuição limpa de fluxos é crucial: o RPS pode ajudar se a placa de rede oferecer poucas filas RSS, mas não deve “jogar fora” nenhum fluxo de cache quente. No lado do TX, defini XPS para agrupar caminhos de transmissão e reduzir a contenção de bloqueios. Na prática, uma alocação silenciosa e com afinidade de núcleo compensa, especialmente com muitos fluxos UDP de tamanho médio em que cada acerto de cache conta.

Virtualização e contentores: integração simples de anfitrião, convidado e vhost

Em ambientes virtualizados, o trabalho alterna entre host, threads vhost e IRQs guest. Certifico-me de que vhost-net-Os threads recebem seus próprios núcleos e não colidem com os trabalhadores de aplicativos. Suas afinidades devem corresponder às filas RX/TX físicas, caso contrário, haverá migração desnecessária entre CPUs. No convidado, eu verifico as filas da virtio-net, ativo a fila múltipla e configuro o RSS/RPS análogo ao bare metal. Onde a latência e os pps estão em primeiro plano SR-IOV reduzir ainda mais as despesas gerais - o pré-requisito é uma topologia NUMA consistente: VF, vCPU e memória pertencem ao mesmo nó.

Na pilha de contentores, as redes sobrepostas, as cadeias NAT profundas e as topologias CNI complexas causam saltos adicionais. Para serviços críticos de latência, prefiro hostNetwork ou redes enxutas (macvlan/ipvlan), equalizo os caminhos NAT e mantenho Conntrack tão pequeno quanto possível. É importante uma estratégia de CPU consistente: os núcleos IRQ e NAPI do anfitrião devem estar localizados na vizinhança dos núcleos em que os trabalhadores vhost/contentor estão a funcionar - esta é a única forma de manter o caminho dos dados curto e previsível.

Agendamento, C-States e IRQ-Threading

Porque a latência não é apenas o tempo de computação, mas também Hora de despertar Eu minimizo os estados C profundos nos núcleos de latência. Uma poupança de energia agressiva pode custar milissegundos antes de um SoftIRQ ser efetivamente executado. Por isso, confio nos reguladores de desempenho, limito os C-states profundos e mantenho o turbo consistente para tornar previsíveis os saltos de frequência. Igualmente importante é Encadeamento de IRQOnde os drivers o permitem, eu transfiro o trabalho para threads IRQ e priorizo para que o RX inicie antes do trabalho downstream sem deslocar completamente o userland. A interação das políticas de agendamento, afinidades e orçamentos é complicada; eu testo passo a passo, registo p99 e tenho cuidado com a interferência com o ksoftirqd, que de outra forma se torna um estrangulamento secreto.

Observação em profundidade: pontos de rastreio, contadores, histos

Se as métricas permanecerem vagas, vou a um nível mais profundo: utilizo tracepoints do kernel em torno de netif_receive_skb, napi_poll e fila_dev_rede, para ver as durações das sondagens, volumes de pacotes e tempos de espera como histogramas. Estas distribuições mostram se 1 % das sondagens estão a demorar demasiado tempo ou se as filas individuais estão a esgotar-se. Além disso, o ethtool-rx/tx-counters, TCP retransmits, busy poll hits e softnet_stat indicam claramente onde os pacotes estão a ser perdidos. Utilizo a análise de queda para reconhecer se a placa de rede está a cair (anel cheio), se o backlog do netdev está a colapsar (time_squeeze) ou se o Qdisc/firewall está a abrandar. Só quando estas peças do puzzle se encaixam é que ajusto os anéis, os orçamentos ou as descargas.

Simplificar os caminhos de segurança e filtragem

ACLs complexas, cadeias nftables/iptables profundas e tabelas conntrack amplas adicionam latência constante por pacote. Eu consolido regras, trabalho com conjuntos/mapas e movo drops genéricos o mais adiante possível no caminho - idealmente o mais cedo possível na NIC (XDP/clsact) se a latência for crítica. Fluxos sem estado, telemetria ou portas “seguras” conhecidas podem ser usados de forma direcionada. sem rastreio para eliminar a necessidade de pesquisas dispendiosas. Ao mesmo tempo, mantenho as tabelas de estado actualizadas, ajusto os tamanhos de hash aos picos de carga e arrumo agressivamente as entradas órfãs. O objetivo é um caminho de política limpo e rastreável que não seja percetível no perfil como uma carga permanente.

Anti-padrões típicos e como os evito

  • Todos os IRQs num núcleo: leva ao congestionamento e ao aquecimento. Antídoto: afinidades direcionadas por pista, coerentes com NUMA.
  • Maximização cega de anéis/orçamentos: oculta o congestionamento, aumenta as caudas de latência. Antídoto: aumentar gradualmente, medir as distribuições.
  • Configuração incorrecta do hashing de fluxo: Os fluxos saltam entre os núcleos, as caches desaparecem. Antídoto: chaves RSS estáveis, RPS/XPS apenas com um objetivo claro.
  • Threads de aplicações nos mesmos núcleos que os SoftIRQs: Interferências e instabilidade. Antídoto: separação rígida, atribuição por vizinhança.
  • Sobreposições/NAT sem orçamento: adicionados a cada salto. Solução: simplificar os caminhos, hospedar redes para cargas de trabalho com latência.
  • Poupança de energia em núcleos de latência: Os estados C profundos abrandam a reação. Antídoto: regulador de desempenho, limitação do estado C.
  • Descargas sem medição: O TSO/GRO pode exacerbar as rajadas e o jitter. Solução: Ativar carga de trabalho específica, monitorizar p99.

Acolhimento prático: passos que funcionam

Começo com uma fase de medição limpa, estabeleço linhas de base e mantenho todas as alterações pequenas em janelas de tempo curtas para poder Causas podem ser separados. Em seguida, ativo o irqbalance, verifico a distribuição automática e, se necessário, defino as afinidades manuais até que não haja Pontos de acesso já não são visíveis. Em seguida, configuro o Multi-Queueue, RSS e - se necessário - RPS/XPS, sincronizado com NUMA. Atribuo os trabalhadores de aplicações a núcleos na vizinhança dos seus núcleos IRQ, mas sem colisão direta. Por fim, limpo os caminhos do firewall, verifico as tabelas do conntrack e tomo decisões conscientes sobre descargas com base em metas de latência.

Exemplo de livro de jogo para latências do p99

Primeiro, meço p95/p99 através de carga representativa e registos seguros de /proc/softirqs e /proc/net/softnet_stat para Gotas e time_squeeze são claramente visíveis. Em seguida, aumento o netdev_budget ou netdev_budget_usecs passo a passo e mantenho o p99 após cada alteração para que eu possa ver a real Tendências reconhecer. Em paralelo, atribuo IRQs a núcleos de um nó NUMA e transfiro trabalhadores de aplicações para vizinhos adequados. Se o p99 continuar a saltar, testo as variantes GRO/LRO e interrompo os perfis de coalescência, cada um com um caminho de medição curto. Só quando a distribuição permanece estável é que transfiro a configuração para funções Ansible ou dropins systemd.

Versão curta para administradores

Consigo o maior efeito de alavanca ao SoftIRQs, orçamento da NAPI, afinidades de IRQ e threads de aplicativos como um caminho de dados coerente. Eu distribuo o trabalho de rede entre os núcleos, mantenho filas coerentes com NUMA e conecto os trabalhadores de forma sensata para que Rotas resumir. Defino os offloads deliberadamente e meço o jitter em vez de otimizar cegamente o throughput. Para alvos de latência difícil, eu confio em polling ocupado e isolamento de CPU, enquanto CPUs de manutenção interceptam a interferência. Se implementar estes passos de forma disciplinada, obtém um débito constante, distribuições de latência mais estreitas e um ambiente de alojamento que reage de forma previsível aos picos de carga.

Artigos actuais