...

Come interpretare correttamente le statistiche NUMA di Linux

Linux NUMA Le statistiche mi mostrano in che misura i processi mantengono la memoria a livello locale e dove gli accessi remoti aumentano le latenze. Spiego come interpreto questi dati in modo mirato, valuto le tendenze nel tempo e ne ricavo chiare misure di ottimizzazione per Prestazioni derivare.

Punti centrali

  • Capire i contatori: numa_hit, numa_miss, numa_foreign, local_node, other_node, interleave_hit
  • Valutare il contesto: profilo di carico, topologia, tipo di carico di lavoro
  • Misurare le tendenze: Prima/dopo e a intervalli
  • Verificare i processi: A livello di sistema vs. per processo
  • Applicare le modifiche: Affinità, Politiche, Posizionamento

Cosa rivelano realmente le statistiche NUMA

Interpreto i dati NUMA come una mappa per Posizione di stoccaggio e i percorsi dei dati. Un elevato numa_hit significa che le allocazioni sono state effettuate sul nodo desiderato. Al contrario, numa_miss indica che il kernel ha dovuto ricorrere a un altro nodo. Il contatore numa_foreign mostra la corrispondenza sul nodo di destinazione e completa il quadro. Con local_node e other_node posso capire se gli accessi sono rimasti locali o hanno utilizzato memoria remota.

Questi valori non vanno mai interpretati isolatamente, perché Carichi di lavoro reagiscono in modo molto diverso. I processi brevi generano occasionalmente errori, senza modificare in modo percettibile le prestazioni complessive. Le politiche di interleave, invece, generano allocazioni intenzionalmente distribuite, il che fa aumentare il valore di interleave_hit. Verifico quindi sempre la politica prevista e il carico attuale. Solo allora decido se un valore richiede un intervento o se è in linea con il progetto.

Per me l'idea centrale è questa: Contatore Forniscono segnali, non giudizi. Cerco modelli nel tempo, non singoli valori. In questo modo capisco se una modifica al sistema sposta la località. Solo sulla base di questo quadro di tendenza valuto se spostare i processi, adeguare le policy o impostare i vincoli di CPU. Ogni analisi NUMA parte quindi da una domanda chiara e da punti di misurazione ripetibili.

Interpretare i conteggi dei nuclei nel loro contesto

Faccio sempre dei confronti numa_hit e numa_miss insieme, invece di valutare i valori assoluti. Se i “miss” aumentano, controllo parallelamente l’andamento di numa_foreign sui potenziali nodi di destinazione. Se entrambi i valori coincidono, ciò indica un vero e proprio spostamento e non un semplice artefatto di lettura. local_node e other_node completano questo quadro fornendo informazioni sugli accessi effettivi. In questo modo riesco a capire se un'allocazione è iniziata a livello locale, ma l'operazione ha successivamente letto da una memoria più distante.

Un unico alto altro_nodo Non mi dà fastidio se il carico di lavoro viene distribuito in modo mirato. I server web con molti worker, invece, traggono vantaggio da una localizzazione coerente. Per questo motivo esamino i singoli processi, non solo il quadro generale. Non appena singoli servizi escono dagli schemi, intervengo sulla loro collocazione. Solo quando aumentano gli errori a livello di sistema, cerco le cause nella topologia o nel carico di lavoro.

Confronti nel tempo: una routine di misurazione che dà risultati concreti

Rilevo le letture dei contatori all'inizio e alla fine di un Fase finale e calcolo la differenza. I singoli valori nascondono gli effetti, mentre le differenze evidenziano l’andamento. Intervalli ripetuti, ad esempio da 30 a 60 secondi, sono spesso sufficienti per individuare le tendenze. Dopo i deploy, gli aggiornamenti del kernel o le modifiche hardware, confronto nuovamente gli stessi intervalli. Se si verificano più errori o se local_node si sposta, si tratta di un cambiamento reale.

Tali serie temporali coprono Errore di posizionamento più rapidamente rispetto alle istantanee. Metto in correlazione le linee con il carico della CPU, i cambi di contesto e l’occupazione della memoria per ogni nodo. In questo modo riesco a capire se i colli di bottiglia nella RAM di un nodo causano movimenti di compensazione. Oppure se i nuovi processi alterano l’equilibrio dei nodi. Il rapporto puro tra «hit» e «miss» lo riporto sempre come andamento, non come singolo valore.

Analizzare a livello di sistema, poi approfondire i processi

Comincio con una panoramica generale da numastat e solo dopo verifico i singoli processi. Questa sequenza fa risparmiare tempo, perché molti effetti diventano visibili a livello globale. Per la visione a livello di processo, utilizzo l’output specifico del processo per isolare i servizi che destano sospetti. Non appena ho individuato i candidati, regolo il Posizionamento sull’utilizzo della CPU e della memoria. Il contributo su questo argomento raccoglie alcuni consigli pratici Affinità della CPU e della memoria.

Soprattutto nel caso dei servizi Java, PHP-FPM o dei database, spesso è sufficiente una corretta Affinità, per ridurre significativamente gli errori. L’orchestrazione dei container tende a mascherare questi problemi, poiché gli scheduler distribuiscono le risorse senza tenere conto della struttura NUMA. Controllo quindi l’assegnazione dei nodi per ogni pod o VM. Se i set di CPU e l’assegnazione della RAM coincidono, il valore di `local_node` aumenta in modo misurabile. Alcuni problemi si risolvono non appena il processo viene eseguito in prossimità del set di dati richiesto.

Panoramica dei contatori NUMA (tabella)

Quando valuto un nuovo sistema, inserisco la tabella seguente in modo da poter esaminare ogni Figura chiave la classifichi rapidamente. Essa indica il significato, l’interpretazione tipica e le possibili misure da adottare. Non la considero uno schema rigido, ma piuttosto una lista di controllo. Rimane fondamentale il confronto con il profilo di carico e la topologia del server. Solo in questo contesto posso prendere una decisione sensata.

Contatore Significato Interpretazione Approccio
numa_hit Allocazione nel nodo desiderato Un valore elevato è positivo Mantenere la posizione
numa_miss L'allocazione è stata trasferita ad altri nodi Maggiore rischio di ritardi Verifica Affinity/Policy
numa_foreign Allocazione esterna su questo nodo Controparte di numa_miss Analizzare i nodi di destinazione
local_node Accessi alla memoria locale Più in alto costa meno Processo più vicino alla RAM
altro_nodo Accessi alla memoria remota Solo discutibile, senza alcuna intenzione Verifica topologia/carico
interleave_hit Risultati con distribuzione interleave Previsto nella politica di interleave Valutare l'uniformità

Con questo Panoramica Decido più rapidamente quando intervenire. Un aumento di numa_miss senza variazioni spiegabili fa scattare un’analisi delle cause. Se i valori di interleave_hit rimangono elevati, verifico se la policy è attiva come previsto. Se other_node mostra un aumento senza un corrispondente aumento del carico, controllo la presenza di carichi di lavoro che potrebbero essere in competizione. In questo modo, la tabella diventa il punto di partenza per interventi mirati.

Comprendere e utilizzare la topologia NUMA

Prima di effettuare la messa a punto, controllo il Topologia del server: socket, core, canali di memoria, percorsi di latenza. Se un processo viene eseguito sul socket 0, ma i record di lavoro si trovano sul socket 1, il tempo di accesso aumenta. Ciò riduce la larghezza di banda e fa variare i tempi di risposta. I servizi particolarmente esigenti in termini di memoria risentono di ogni distanza superflua. Pertanto, assegno i processi ad alta intensità di dati a nodi con RAM libera sufficiente.

Asimmetriche Collegamenti Gli effetti si amplificano, ad esempio quando un nodo utilizza un numero inferiore di canali. In questi casi, sposto in modo mirato i dati memorizzati nella cache, invece di distribuire il processo. Configuro gli host delle VM e dei container in modo tale che ogni istanza mantenga un collegamento coerente con il nodo. In questo modo riduco il traffico dati remoto senza limitare le quote. Sono i limiti fisici della macchina a dettare le regole, e io mi attengo a esse.

Comprendere correttamente i concetti di interleave e balancing

Le politiche di interleave distribuiscono intenzionalmente la memoria tra i nodi in modo che Produttività aumenta per ogni processo oppure gli hotspot diminuiscono. In questa configurazione, valori elevati di interleave_hit sono considerati desiderabili. In questo caso verifico soprattutto l'uniformità, non la località assoluta. L'AutoNUMA o il bilanciamento NUMA possono essere d'aiuto, ma non in ogni situazione.

Decido a seconda della situazione se utilizzare la funzione automatica Bilanciamento rimane attivo. Per i servizi costanti e di lunga durata, tendo a impostare collegamenti fissi. In caso di carichi variabili, AutoNUMA può reagire in modo adeguato. Una buona panoramica dei vantaggi e dei rischi è disponibile nell'articolo Bilanciamento NUMA. Solo quando l’obiettivo e il contesto sono chiari, scelgo la modalità più adatta.

Modelli di carico di lavoro: database, macchine virtuali, servizi web

I database sono sensibili a Latenza tra CPU e RAM. Per questo motivo mantengo l’istanza, la cache del buffer e gli shard attivi sullo stesso nodo. Le macchine virtuali traggono vantaggio da set di CPU ben definiti e dalla RAM del nodo, in modo che i sistemi operativi ospiti vedano percorsi coerenti. I servizi web con molti worker danno il meglio di sé quando i gruppi di worker rimangono associati a un singolo nodo. Per la strategia di archiviazione, a seconda dei casi, utilizzo misure mirate Politiche di memoria NUMA.

Per quanto riguarda invece i lavori analitici e le scansioni di grandi dimensioni, li svolgo in parte distribuito . In questo caso, l’interleave offre spesso una larghezza di banda migliore rispetto alla località rigida. È fondamentale analizzare con obiettività i modelli di I/O del carico di lavoro. La scrittura prevale rispetto alla lettura, così come gli accessi casuali prevalgono su quelli sequenziali. Scelgo la politica che si adatta al modello di accesso, non quella che sembra più appropriata nei libri di testo.

Procedura di misurazione pratica e strumenti

Per iniziare mi bastano numastat e la vista del processo. Registro le letture dei contatori con data, PID e indicatori di carico. È importante effettuare le misurazioni in intervalli di tempo identici. In questo modo è possibile rappresentare chiaramente le differenze tra la situazione precedente e quella successiva. Nei periodi di produzione, annoto le variazioni e le metto in correlazione con le date di rilascio.

In caso di anomalie Servizi Verifico inoltre l'utilizzo della CPU e la visualizzazione dei nodi con strumenti come lscpu, numactl e perf per monitorare il carico remoto. Per ogni ciclo di misurazione documento la politica scelta. Dopo ogni modifica, ripeto la misurazione. Solo quando le linee di tendenza si stabilizzano, valuto l’effetto come positivo. Il cambio cieco delle impostazioni porta facilmente a miglioramenti apparenti.

Evitare le interpretazioni errate più comuni

Un alto interleave_hit Non è un errore se l’interleave è attivamente previsto. Allo stesso modo, un singolo errore è trascurabile in presenza di una durata prolungata. Verifico sempre la densità e la distribuzione sull’intervallo, non solo i picchi. Alcuni interpretano «other_node» come un dato generalmente negativo e trascurano la natura del carico di lavoro. Per questo motivo, esamino prima l’obiettivo di progettazione e poi valuto i numeri.

Un altro errore: Vista d'insieme Bene, così va tutto bene. Spesso i valori anomali si nascondono solo in alcuni PID. Oppure gli scheduler dei container distribuiscono i pod su più nodi, anche se sarebbe più sensato raggrupparli localmente. Mi accorgo di questi effetti solo quando effettuo misurazioni per singolo processo. Senza questo livello di dettaglio, l’analisi rimane incompleta.

Passaggi di messa a punto efficaci

Inizio con Posizionamento: Processi sui nodi in cui si trovano o dovrebbero trovarsi i dati. Quindi imposto l’affinità della CPU affinché i thread non saltino da un socket all’altro. Segue l’assegnazione della memoria, in modo che il kernel effettui l’allocazione nella posizione desiderata. Per i carichi variabili, valuto le politiche e, se opportuno, utilizzo AutoNUMA.

Successivamente mi occuperò di Coerenza Nel ciclo di vita: i riavvii, i deploy e le operazioni di scalabilità non devono modificare casualmente i riferimenti ai nodi. Documento i collegamenti nel codice, in modo che rimangano riproducibili. Successivamente, effettuo una nuova misurazione, valuto le variazioni e decido se procedere con una messa a punto. Ogni modifica merita una chiara prova misurabile.

Esempio pratico: da fiasco a successo

Supponiamo che una Banca dati mostra, sotto carico, un valore più elevato di numa_miss e un aumento di other_node. La latenza delle query oscilla maggiormente. Controllo innanzitutto l’assegnazione dei processi e constato che, dopo un rollout, il servizio gira sul nodo A, mentre la cache è stata allocata sul nodo B. Dopo aver fissato l’assegnazione della CPU e della memoria al nodo B, il rapporto si inverte: numa_hit aumenta, mentre i miss diminuiscono. I tempi di risposta diventano più costanti e il carico della CPU diminuisce leggermente, poiché vengono eliminati gli accessi remoti.

Nel frattempo, controllo la Politica. La funzione Interleave era attiva involontariamente e ha distribuito le allocazioni. Dopo il passaggio al nodo preferito, la cache rimane chiusa a livello locale. Dopo un’ora di misurazione, i delta confermano il miglioramento. Solo allora considero la messa a punto un successo. Senza questa verifica incrociata, un’istantanea avrebbe potuto trarre in inganno.

Metriche e valori di riferimento per la pratica

Per prendere decisioni mi affido a dati affidabili Probabilità anziché singoli valori grezzi. Per ogni processo calcolo il tasso di allocazione local_alloc = numa_hit / (numa_hit + numa_miss). Inoltre, valuto il Tasso di accesso calcolo local_access = local_node / (local_node + other_node). Questi due valori insieme indicano se la memoria rimane utilizzata localmente anche dopo l’allocazione. Come valori di riferimento approssimativi, mi attengo a quanto segue: per i servizi in cui la latenza è critica, punto a accessi remoti inferiori a 5–10 %. Per i carichi di lavoro analitici legati alla larghezza di banda, tollero valori compresi tra 20 e 30 %, a condizione che la velocità di trasmissione aumenti. Il fattore decisivo è la Stabilità nel tempo. Preferisco un valore che rimanga stabilmente costante sotto carico piuttosto che un breve picco con valori perfetti. Documento questi intervalli target per ogni intervento di manutenzione, in modo che le misurazioni successive possano essere chiaramente classificate.

Cgroups, container e le insidie dello scheduler

Negli ambienti container, per prima cosa controllo il cpuset‑Assegnazione: set di CPU e cpuset.mems devono mappare lo stesso spazio di nodo, altrimenti si verificano inevitabilmente errori. Mi assicuro che i pod con richieste CPU fisse non vengano distribuiti su più nodi NUMA e che lo scheduler non distribuisca i worker della stessa applicazione su più nodi. Per i tipi burst, limito il numero massimo di thread per pod in modo che rimangano all’interno di un singolo nodo. Documento il Dominio NUMA per ogni distribuzione e richiedo repliche coerenti (un gruppo di worker per nodo, non mezzi gruppi distribuiti su due nodi). Se calcolo al minimo lo spazio di archiviazione per ogni pod, mi creo una pressione indesiderata: un leggero margine per ogni nodo impedisce al kernel di dover ricorrere prematuramente a nodi esterni. Se i container vengono riavviati frequentemente, faccio attenzione al binding deterministico, in modo che Avviamenti a freddo non ricevere per caso una sede peggiore.

Allineamento coerente delle macchine virtuali e dei vNUMA

Per quanto riguarda le macchine virtuali, la mia attenzione si concentra su vNUMA: La topologia virtuale deve corrispondere a quella fisica. Assegno le vCPU in modo tale che ogni nodo vNUMA corrisponda esattamente a un nodo NUMA fisico. A livello di host, assegno i thread di QEMU/hypervisor a questo dominio e mi assicuro che la RAM assegnata provenga interamente da quel nodo. Mongolfiera E accetto con cautela l’overcommit per le VM in cui la latenza è critica; un ballooning aggressivo può spingere gli hotset fuori dal nodo e far aumentare gli accessi remoti. In caso di migrazione live, dopo lo spostamento verifico nuovamente i collegamenti: in alcuni ambienti, infatti, si perdono le impostazioni precise relative alla CPU e alla memoria. Solo quando l’allineamento vNUMA è corretto, valuto numastat a livello di sistema: altrimenti mi limito a risolvere i sintomi, non la causa.

THP, Hugepages e migrazione delle pagine

Pagine trasparenti di grandi dimensioni (THP) possono essere sia d’aiuto che d’intralcio. Le pagine più grandi riducono i TLB miss e migliorano la larghezza di banda, ma se il kernel attiva le Hugepages solo in una fase avanzata crollato oppure migrati, possono risultare inadeguati Distanze a lunga distanza nascono. Mi attengo a due regole: in primo luogo, la Politica definire chiaramente (ad es. nodo preferito) e, per quanto possibile, impostare fin dall’inizio allocazioni locali di grandi dimensioni. In secondo luogo, per i carichi di lavoro con cache fisse e di grandi dimensioni, impiego, se possibile, Hugepages statiche (hugetlb) che riservo esplicitamente su un nodo. Ciò riduce la frammentazione e i movimenti di compensazione. Se osservo nelle serie temporali che, dopo un periodo di esecuzione prolungato, il altro_nodo- Se la quota aumenta, verifico se Migrazione delle pagine o se si verifica una compattazione e se le impostazioni THP corrispondono al modello. Per me è importante non disattivare o attivare l'opzione a livello globale: prendo una decisione per ogni servizio e ne valuto l'effetto in termini di località e latenza.

Come interpretare correttamente i termini “Reclaim”, “Swap” e “Memory-Pressure”

Se le miss salgono senza un evidente cambiamento di posizione, cerco pressione di accumulo per nodo. I nodi pieni costringono il kernel a eseguire operazioni di recupero dello spazio e compattazione, in parte innescate da kswapd su un altro nodo – ciò provoca effetti collaterali sui contatori. Verifico per ogni nodo la memoria libera e il carico della cache di pagina. Attivato Scambio può sostituire gli hot set e far aumentare vertiginosamente le latenze; per i servizi particolarmente sensibili, disattivo lo swap o lo limito rigorosamente. Nei log e nelle viste perf cerco i picchi di reclaim durante i picchi di carico. L’obiettivo è avere spazio libero sufficiente, locali Mantenere la RAM sul nodo di destinazione, in modo che le allocazioni non vadano perse. Se per farlo è necessario ridurre le cache, do la priorità al set di lavoro del servizio rispetto alla cache di pagina generica.

Comandi pratici e analisi dei risultati

Per la visualizzazione del processo utilizzo numastat -p e aggiungo: cat /proc//numa_maps, per visualizzare le allocazioni per area (anonime, basate su file) e per nodo. numactl --hardware mi fornisce le matrici di latenza e le dimensioni dei nodi, lscpu --extended mostra l'assegnazione della CPU ai nodi. Per gli accessi alla memoria incentrati sui percorsi remoti, imposto memoria perf per verificare i modelli di carico. Raccolgo i delta in modo riproducibile, ad esempio:

Routine di misurazione

  • t0: eseguire il backup di numastat (completo) e numastat -p per i PID principali
  • 30–60 s di esercizio con carico, fase di carico identica
  • t1: rileggere numastat, calcolare i delta per ogni contatore
  • Registrazione dei valori della CPU parallela, dei cambi di contesto e della memoria dei nodi

Successivamente calcolo il Probabilità e evidenzio quali processi si discostano in modo significativo dalle tendenze a livello di sistema. Laddove permangono incertezze, ripeto la misurazione almeno tre volte. Solo i delta coerenti li considero affidabili. Per un monitoraggio continuo, mappo i contatori su serie temporali e li collego ai metadati delle versioni – in questo modo riesco a individuare Punti di regressione immediatamente.

Lista di controllo per un’ottimizzazione NUMA ben strutturata

  • Definire l'obiettivo: latenza vs. throughput, carico fisso vs. variabile
  • Acquisizione della topologia: nodi, latenze, riserve di RAM libere per nodo
  • Misurare i valori di riferimento: numastat complessivo e per processo, ricavare i rapporti
  • Correzione dell'allocazione: affinità CPU, associazione della memoria, criteri
  • Configurazione dei container/VM: cpuset.cpus = nodi, cpuset.mems adeguato; mappatura corretta di vNUMA
  • Scegliere consapevolmente THP/Hugepages, tenendo d’occhio la frammentazione
  • Ridurre la pressione sulla memoria: verificare l’headroom per ogni nodo e la strategia di swap
  • Verifica successiva: confrontare i delta, garantire la stabilità nel tempo
  • Documentazione: collegamenti come codice, note di rilascio con contesto NUMA

Riassumendo brevemente

Analizzo i dati NUMA utilizzando Segnali A questo proposito, si veda: coppie di contatori, andamenti temporali e visione del processo. I valori chiave numa_hit, numa_miss, numa_foreign, local_node, other_node e interleave_hit mi indicano la località, i movimenti di deviazione e le strategie di distribuzione. Prendo le mie decisioni in base alla topologia e al carico di lavoro, non in base a valori limite rigidi. L’ottimizzazione inizia con il posizionamento, l’affinità, una politica adeguata e una routine di misurazione accurata. In questo modo garantisco prestazioni costanti Prestazioni, poiché la CPU e la RAM sono adeguate all'applicazione e i trasferimenti di dati su lunghe distanze sono rari.

Articoli attuali