Linux NUMA As estatísticas mostram-me até que ponto os processos mantêm a sua memória local e onde os acessos remotos aumentam as latências. Explico como interpreto estes números de forma específica, avalio as tendências ao longo do tempo e, a partir daí, defino medidas claras de otimização para Desempenho derivado.
Pontos centrais
- Compreender os contadores: numa_hit, numa_miss, numa_foreign, local_node, other_node, interleave_hit
- Avaliar o contexto: Perfil de carga, topologia, tipo de carga de trabalho
- Avaliar tendências: Antes/depois e por intervalos
- Verificar processos: A nível do sistema vs. por processo
- Aplicar o ajuste: Afinidade, Políticas, Colocação
O que as estatísticas NUMA realmente revelam
Interpreto os números NUMA como um mapa para Local de armazenamento e vias de transmissão de dados. Um elevado numa_hit significa que as alocações foram realizadas no nó pretendido. Por outro lado, o valor «numa_miss» indica que o kernel teve de recorrer a outro nó. O contador «numa_foreign» mostra o equivalente no nó de destino e completa o quadro. Com local_node e other_node, consigo identificar se os acessos permaneceram locais ou se utilizaram memória remota.
Estes valores nunca devem ser interpretados isoladamente, porque Cargas de trabalho reagem de forma muito diferente. Os processos curtos geram, ocasionalmente, erros, sem alterar sensivelmente o desempenho global. Por sua vez, as políticas de intercalamento geram alocações deliberadamente distribuídas, o que faz com que o interleave_hit aumente. Por isso, verifico sempre a política pretendida e a carga atual. Só então decido se um valor justifica uma intervenção ou se se enquadra no desenho do sistema.
Para mim, a ideia central é a seguinte: Contador fornecem sinais, não julgamentos. Procuro padrões ao longo do tempo, não valores isolados. É assim que percebo se uma alteração no sistema desloca a localidade. Só com este panorama das tendências é que avalio se devo deslocar processos, ajustar políticas ou definir restrições de CPU. Por isso, cada análise NUMA começa com uma questão clara e pontos de medição repetíveis.
Interpretar os contadores de núcleos no seu contexto
Estou sempre a comparar numa_hit e o numa_miss em conjunto, em vez de avaliar valores absolutos. Se os erros aumentarem, verifico em paralelo a evolução do numa_foreign nos potenciais nós de destino. Se ambos coincidirem, isso indica uma deslocalização real e não um mero artefacto de leitura. O `local_node` e o `other_node` complementam este quadro através dos acessos efetivos. Assim, consigo perceber se uma alocação teve início localmente, mas se a operação leu posteriormente memória mais distante.
Um único alto outro_nó Não me incomoda que a carga de trabalho seja distribuída de forma deliberada. Por outro lado, os servidores web com muitos workers beneficiam de uma localização consistente. Por isso, analiso os processos individualmente, não apenas a visão global. Assim que serviços específicos se desviam da norma, reviso a sua localização. Só quando as falhas aumentam a nível do sistema é que procuro causas relacionadas com a topologia ou a carga de trabalho.
Comparações ao longo do tempo: uma rotina de medição que compensa
Medei os valores dos contadores no início e no final de uma Fase final e calculo a diferença. Os valores isolados ocultam os efeitos; as diferenças revelam a evolução. Intervalos repetidos de, por exemplo, 30 a 60 segundos, são muitas vezes suficientes para identificar tendências. Após implementações, atualizações do kernel ou alterações de hardware, volto a comparar os mesmos intervalos. Se, nessa altura, houver mais falhas ou se o `local_node` se deslocar, significa que existe uma alteração real.
Essas séries temporais abrangem Erro de posicionamento mais rapidamente do que instantâneos. Estabeleço uma correlação entre as linhas e a utilização da CPU, as mudanças de contexto e a ocupação de memória por nó. Desta forma, consigo ver se os estrangulamentos na RAM de um nó levam a movimentos de desvio. Ou se novos processos alteram o equilíbrio dos nós. A taxa pura de acertos e erros apresento-a sempre como uma tendência, e não como um valor isolado.
Analisar a nível do sistema e, em seguida, aprofundar os processos
Começo pela visão geral a partir de numastato e só depois analiso os processos individualmente. Esta ordem poupa tempo, porque muitos efeitos tornam-se visíveis a nível global. Para a análise dos processos, utilizo a saída específica de cada processo para isolar os serviços que chamam a atenção. Assim que os candidatos estiverem identificados, ajusto o Colocação sobre a alocação de CPU e memória. O artigo sobre este tema reúne dicas práticas a este respeito Afinidade da CPU e da memória.
Especialmente no caso de serviços Java, PHP-FPM ou bases de dados, muitas vezes basta uma Afinidade, para reduzir significativamente os erros. A orquestração de contentores tende a ocultar estes problemas, porque os programadores distribuem sem ter em conta a NUMA. Por isso, controlo a atribuição de nós por pod ou VM. Quando os conjuntos de CPU e a atribuição de RAM coincidem, o `local_node` aumenta de forma mensurável. Alguns problemas resolvem-se assim que o processo é executado próximo do conjunto de dados necessário.
Visão geral dos contadores NUMA (tabela)
Quando avalio um novo sistema, preencho a tabela seguinte para poder analisar cada Índice classifico rapidamente. Apresenta o significado, a interpretação típica e as medidas possíveis. Não a encaro como um esquema rígido, mas sim como uma lista de verificação. O que continua a ser decisivo é a comparação com o perfil de carga e a topologia do servidor. Só neste contexto é que consigo tomar uma decisão sensata.
| Contador | Significado | Interpretação | Abordagem |
|---|---|---|---|
| numa_hit | Alocação no nó pretendido | Um valor elevado é positivo | Manter a posição |
| numa_miss | A alocação foi redirecionada para outros nós | Risco acrescido de latências | Verificar a afinidade/política |
| numa_foreign | Alocação externa neste nó | Contraparte de numa_miss | Analisar o nó de destino |
| local_node | Acessos à memória local | Quanto mais alto, mais barato | Processo mais próximo da RAM |
| outro_nó | Acessos à memória remota | Apenas preocupante, sem intenção | Verificar a topologia/carga |
| interleave_hit | Resultados na distribuição intercalada | Esperado na política de intercalamento | Avaliar a uniformidade |
Com esta Visão geral decido mais rapidamente quando devo intervir. Um aumento de «numa_miss» sem alterações explicáveis desencadeia uma análise das causas. Se os valores de interleave_hit se mantiverem elevados, verifico se a política está ativa intencionalmente. Se o other_node apresentar um aumento sem aumento da carga, verifico se há cargas de trabalho que estejam a ocupar recursos. Assim, a tabela torna-se o ponto de partida para medidas específicas.
Compreender e utilizar a topologia NUMA
Antes de fazer o ajuste, verifico o Topologia do servidor: soquetes, núcleos, canais de memória, percursos de latência. Se um processo estiver a ser executado no soquete 0, mas os blocos de trabalho estiverem no soquete 1, o tempo de acesso aumenta. Isso reduz o débito e faz com que os tempos de resposta oscilem. Os serviços que exigem muita memória são particularmente sensíveis a qualquer distância desnecessária. Por isso, atribuo os processos que consomem muitos dados a nós com RAM livre suficiente.
Assimétricas Ligações reforçam os efeitos, por exemplo, quando um nó utiliza menos canais. Nesses casos, transfiro os dados armazenados em cache de forma seletiva, em vez de distribuir o processo. Configuro os hosts de máquinas virtuais e de contentores de forma a que cada instância tenha uma ligação consistente ao nó. Desta forma, reduzo o tráfego de dados remoto sem limitar as quotas. A física da máquina estabelece os limites, e eu cumpro-os.
Compreender corretamente o interleave e o balanceamento
As políticas de interleave distribuem a memória de forma deliberada pelos nós, para que Rendimento aumenta por processo ou os pontos de concentração diminuem. Nesta configuração, valores elevados de interleave_hit são considerados desejáveis. Verifico então, acima de tudo, a uniformidade, e não a localidade absoluta. O AutoNUMA ou o NUMA-Balancing podem ajudar, mas não em todas as situações.
Decido, consoante a situação, se a função automática Equilíbrio permanece ativo. No caso de serviços consistentes e de longa duração, prefiro utilizar ligações fixas. Em caso de cargas variáveis, o AutoNUMA pode reagir de forma adequada. Pode encontrar uma boa visão geral das vantagens e dos riscos no artigo Equilíbrio NUMA. Só quando o objetivo e o contexto estiverem claros é que escolho o modo adequado.
Padrões de carga de trabalho: bases de dados, máquinas virtuais, serviços Web
As bases de dados são sensíveis a Latência entre a CPU e a RAM. Por isso, mantenho a instância, a cache de buffer e os shards ativos no mesmo nó. As máquinas virtuais beneficiam de conjuntos de CPU bem definidos, juntamente com a RAM do nó, para que os sistemas operativos convidados vejam percursos consistentes. Os serviços web com muitos trabalhadores têm um desempenho excelente quando os grupos de trabalhadores permanecem ligados a um único nó. Para a estratégia de memória, utilizo, consoante o caso, abordagens específicas Políticas de memória NUMA.
Por outro lado, as tarefas analíticas e as digitalizações de grande dimensão, realizo-as parcialmente distribuído . Neste caso, o interleave proporciona frequentemente melhores larguras de banda do que a localidade rígida. É importante analisar com objetividade os padrões de E/S da carga de trabalho. A gravação tem um peso diferente da leitura, e os acessos aleatórios diferem dos sequenciais. Escolho a política que se adequa ao padrão de acesso, e não aquela que parece mais adequada nos livros didáticos.
Rotina prática de medição e ferramentas
Para começar, basta-me numastato e a visualização do processo. Registo as leituras dos contadores com a data, o PID e os indicadores de carga. É importante efetuar as medições em intervalos de tempo idênticos. Desta forma, é possível representar com clareza as diferenças entre o «antes» e o «depois». Nos intervalos produtivos, registo as variações e correlaciono-as com as datas de lançamento.
No caso de Serviços Além disso, verifico a utilização da CPU e a visibilidade dos nós com ferramentas como o lscpu, o numactl e o perf para analisar a carga remota. Documento a política selecionada em cada ciclo de medição. Após uma alteração, volto a efetuar medições. Só quando as linhas de tendência se estabilizam é que considero o efeito bem-sucedido. A mudança cega pode facilmente levar a melhorias ilusórias.
Evitar interpretações erradas frequentes
Um alto interleave_hit Não é um erro se o Interleave estiver ativo de forma intencional. Da mesma forma, um único erro é insignificante num período de execução prolongado. Verifico sempre a densidade e a distribuição ao longo do intervalo, não apenas os picos. Algumas pessoas interpretam «other_node» como algo globalmente negativo e ignoram a natureza da carga de trabalho. Por isso, analiso primeiro o objetivo de conceção e só depois avalio os números.
Outro equívoco: Visão geral Bem, assim fica tudo bem. Muitas vezes, os valores atípicos escondem-se apenas em alguns PIDs. Ou os agendadores de contentores distribuem os pods por vários nós, embora fizesse sentido formar um grupo local. Só consigo detetar esses efeitos quando faço medições por processo. Sem esse nível de detalhe, a análise fica incompleta.
Passos de afinação com resultados visíveis
Começo por Colocação: Processos nos nós onde os dados se encontram ou devem encontrar-se. Em seguida, defino a afinidade da CPU para que os threads não saltem de um soquete para outro. Segue-se a vinculação de memória, para que o kernel aloque a memória no local pretendido. Para cargas variáveis, verifico as políticas e, se for o caso, utilizo o AutoNUMA.
Em seguida, encarreguei-me de Consistência No ciclo de vida: os reinícios, as implementações e os ajustes de escala não devem alterar aleatoriamente as ligações entre nós. Documento as ligações em código, para que se mantenham reproduzíveis. Depois, volto a medir, avalio as variações e decido sobre o ajuste fino. Cada alteração merece uma prova clara, baseada em medições.
Exemplo prático: De fracasso a sucesso
Suponhamos que uma Base de dados Apresenta, sob carga, um aumento do «numa_miss» e do «other_node». A latência das consultas varia mais acentuadamente. Verifico primeiro a alocação de processos e constato que, após uma implementação, o serviço está a ser executado no nó A, mas a cache foi alocada no nó B. Após a atribuição fixa da CPU e da memória ao nó B, a relação inverte-se: o «numa_hit» aumenta e os «misses» diminuem. Os tempos de resposta tornam-se mais constantes e a carga da CPU diminui ligeiramente, uma vez que os acessos remotos deixam de ocorrer.
Ao mesmo tempo, verifico a Política. O Interleave estava ativado inadvertidamente e distribuiu alocações. Após a mudança para o nó preferencial, a cache permanece fechada localmente. Após uma hora de medição, os deltas confirmam a melhoria. Só então considero o ajuste um sucesso. Sem esta verificação cruzada, um instantâneo teria induzido em erro.
Métricas e valores de referência para a prática
Para tomar decisões, recorro a critérios sólidos Probabilidades em vez de valores brutos individuais. Calculo, para cada processo, a taxa de alocação local_alloc = numa_hit / (numa_hit + numa_miss). Além disso, analiso a Taxa de acesso calculo local_access = local_node / (local_node + other_node). Ambos, em conjunto, indicam se a memória continua a ser utilizada localmente mesmo após a alocação. Como valores de referência aproximados, utilizo o seguinte: no caso de serviços em que a latência é crítica, procuro manter os acessos remotos abaixo de 5–10 %. No caso de cargas de trabalho analíticas de largura de banda, tolero 20–30 %, desde que o débito aumente. O fator decisivo é a Estabilidade ao longo do tempo. Prefiro um valor que se mantenha estável sob carga a um pico breve com valores perfeitos. Registo estes intervalos-alvo por cada intervenção de manutenção, para que as medições posteriores possam ser claramente contextualizadas.
Cgroups, contentores e armadilhas do agendador
Em ambientes de contentores, verifico primeiro o cpuset‑Atribuição: conjuntos de CPU e cpuset.mems têm de corresponder ao mesmo espaço de nós, caso contrário, surgirão inevitavelmente erros. Asseguro-me de que os pods com pedidos fixos de CPU não sejam distribuídos por vários nós NUMA e de que o agendador não distribua workers da mesma aplicação por vários nós. No caso dos tipos «burst», limito o número máximo de threads por pod de forma a que permaneçam dentro de um único nó. Documento o Domínio NUMA por implementação e exijo réplicas consistentes (um grupo de trabalhadores por nó, não meios grupos distribuídos por dois nós). Se calcular o tamanho da memória por pod de forma demasiado apertada, estou a criar uma pressão indesejada: uma ligeira margem por nó evita que o kernel tenha de recorrer prematuramente a nós externos. Se os contentores forem reiniciados com frequência, presto atenção à ligação determinística, para que Arranques a frio não ficar com uma localização pior por acaso.
Alinhar máquinas virtuais e vNUMA de forma consistente
No que diz respeito às máquinas virtuais, a minha atenção centra-se em vNUMA: A topologia virtual tem de corresponder à física. Distribuo as vCPUs de forma a que cada nó vNUMA fique alojado exatamente num nó NUMA físico. Do lado do anfitrião, atribuo os threads do QEMU/hipervisor a este domínio e garanto que a RAM atribuída seja fornecida na totalidade a partir deste nó. Balonismo E, no caso de máquinas virtuais em que a latência é crítica, aceito o overcommit com cautela; o ballooning agressivo pode empurrar os hotsets para fora do nó e aumentar significativamente os acessos remotos. Na migração em tempo real, verifico novamente as ligações após a transferência – alguns ambientes perdem, nesse processo, as ligações de CPU e memória definidas com precisão. Só quando o alinhamento vNUMA estiver correto é que avalio o `numastat` em todo o sistema: caso contrário, estou a corrigir os sintomas, não a causa.
THP, Hugepages e migração de páginas
Páginas enormes transparentes (THP) podem tanto ajudar como prejudicar. Páginas maiores reduzem as falhas de TLB e melhoram a largura de banda, mas se o kernel só ativar as Hugepages numa fase tardia desmoronou-se ou migrados, podem ser inadequados Distâncias longas surgem. Sigo duas regras: em primeiro lugar, a Política definir claramente (por exemplo, nó preferencial) e, sempre que possível, criar alocações locais de grande dimensão desde o início. Em segundo lugar, no caso de cargas de trabalho com caches fixas e de grande dimensão, defino, sempre que possível, Hugepages estáticas (hugetlb), que reservo explicitamente num nó. Isto reduz a fragmentação e os movimentos de compensação. Se observar nas séries temporais que, após um período de execução mais longo, o outro_nó‑À medida que a percentagem aumenta, verifico se Migração de páginas ou se ocorre compactação e se as configurações do THP se adequam ao padrão. O que é importante para mim é: não desativar nem ativar globalmente – decido caso a caso, por serviço, e avalio o efeito na localidade e na latência.
Como interpretar corretamente os parâmetros «Reclaim», «Swap» e «Memory-Pressure»
Se as caixas subirem sem que haja uma alteração evidente na sua posição, procuro pressão de armazenamento por nó. Os nós cheios obrigam o kernel a realizar operações de recuperação e compactação, em parte desencadeadas por kswapd num outro nó – isso provoca efeitos colaterais nos contadores. Verifico, por cada nó, a memória livre e a utilização da cache de páginas. Ativado Troca pode substituir os Hotsets e fazer com que as latências disparem; para serviços particularmente sensíveis, desativo o swap ou limito-o rigorosamente. Nos registos e nas visualizações do perf, procuro picos de recuperação durante os picos de carga. O objetivo é ter espaço livre suficiente, locais Manter a RAM no nó de destino, para que as alocações não se desloquem. Se for necessário reduzir as caches para tal, dou prioridade ao conjunto de trabalho do serviço em relação à cache de páginas genérica.
Comandos práticos e análise
Para a perspetiva do processo, utilizo numastat -p e acrescento cat /proc//numa_maps, para ver as alocações por área (anónimas, baseadas em ficheiros) e por nó. numactl --hardware fornece-me matrizes de latência e tamanhos de nós, lscpu --extended mostra a atribuição da CPU aos nós. Para acessos à memória com ênfase em percursos remotos, defino memória de desempenho para verificar os padrões de carga. Recolho os deltas de forma reprodutível, por exemplo:
Rotina de medição
- t0: guardar o «numastat» (total) e o «numastat -p» para os PIDs principais
- 30–60 s de exercício com carga, fase de esforço idêntica
- t1: reler o numastat, calcular os deltas por contador
- Registar valores de memória paralelos da CPU, de mudança de contexto e dos nós
Em seguida, calculo o Probabilidades e identifico quais os processos que se desviam significativamente das tendências a nível do sistema. Nos casos em que subsiste alguma incerteza, repito a medição pelo menos três vezes. Só considero fiáveis as variações que se revelam consistentes. Para uma monitorização contínua, mapeio os contadores em séries temporais e associo-os aos metadados de lançamento – assim consigo identificar Pontos de regressão imediatamente.
Lista de verificação para um ajuste NUMA organizado
- Definir o objetivo: latência vs. débito, carga fixa vs. carga variável
- Analisar a topologia: nós, latências, reservas de RAM disponíveis por nó
- Medir a linha de base: numastat total e por processo, calcular as proporções
- Corrigir a afetação: afinidade da CPU, vinculação de memória, políticas
- Configurar contentores/máquinas virtuais: cpuset.cpus = nós, cpuset.mems adequados; mapear corretamente o vNUMA
- Escolher deliberadamente THP/Hugepages, tendo em conta a fragmentação
- Reduzir a pressão de memória: verificar a margem de memória por nó e a estratégia de swap
- Medições posteriores: comparar os deltas, garantir a estabilidade ao longo do tempo
- Documentação: ligações como código, notas de lançamento com contexto NUMA
Brevemente resumido
Analiso os dados NUMA ao Sinais Leia também: pares de contadores, evolução temporal e visão do processo. Os valores-chave numa_hit, numa_miss, numa_foreign, local_node, other_node e interleave_hit mostram-me a localidade, os movimentos de desvio e as estratégias de distribuição. Tomo as minhas decisões com base na topologia e na carga de trabalho, e não com base em valores-limite rígidos. O ajuste começa com o posicionamento, a afinidade, uma política adequada e uma rotina de medição rigorosa. É assim que consigo garantir um desempenho constante Desempenho, porque a CPU e a RAM são adequadas à aplicação e os transferências de dados de longa distância são raras.


