...

NUMA Balancing Server: Memory Access Optimization für Hosting Hardware

Ich zeige, wie NUMA Balancing Server auf Hosting-Hardware den Speicherzugriff strafft und Latenzen senkt, indem der Kernel Prozesse und Daten an den passenden NUMA-Knoten bindet. Entscheidend ist die Memory-Access-Optimierung durch lokale Zugriffe, Task-Platzierung und gezielte Seitenmigration auf Linux-Hosts mit vielen Cores.

Zentrale Punkte

  • NUMA trennt CPUs und Speicher in Knoten; lokale Zugriffe liefern geringe Latenz.
  • Automatic NUMA Balancing migriert Seiten und platziert Tasks knotennah.
  • VM-Größe pro Knoten vermeiden, sonst droht NUMA-Trashing.
  • Tools wie numactl, lscpu, numad zeigen Topologie und Nutzung.
  • Tuning: C-States, Node Interleaving aus, Huge Pages, Affinitäten.

Was NUMA ist – und warum es für Hosting zählt

NUMA teilt ein Mehrprozessorsystem in Knoten, die jeweils eigene CPUs und lokalen Speicher enthalten, wodurch nahe Zugriffe schneller laufen als entfernte. Während UMA alle Kerne auf einen gemeinsamen Pfad schickt, verhindert NUMA Engpässe durch lokale Speicherkanäle pro Knoten. In Hosting-Umgebungen mit vielen parallelen VMs summiert sich jede Millisekunde Latenz, daher profitiert jede Anfrage messbar. Wer Hintergründe vertiefen möchte, findet mehr zur NUMA-Architektur. Für mich steht fest: Wer Knoten versteht und nutzt, holt mehr Bandbreite aus derselben Hardware.

Automatic NUMA Balancing im Linux-Kernel – so arbeitet es

Der Kernel scannt periodisch Teile des Adressraums und „unmappt“ Seiten, damit ein Hinting Fault den optimalen Knoten sichtbar macht. Tritt der Fault auf, bewertet der Algorithmus, ob sich eine Migration der Seite oder eine Verschiebung des Tasks lohnt und vermeidet unnötige Bewegungen. Migrate-on-Fault bringt Daten näher an die ausführende CPU, Task-NUMA-Placement rückt Prozesse näher an ihren Speicher. Der Scanner verteilt seine Arbeit stückweise, damit der Overhead im Rauschen der normalen Last bleibt. So entsteht ein laufendes Feintuning, das Latenz reduziert, ohne harte Pinning-Regeln zu verlangen.

Memory Access Optimization: lokal schlägt fern

Lokale Zugriffe nutzen den Memory-Controller des eigenen Knotens und minimieren Wartezeiten auf den Interconnect. Entfernte Zugriffe kosten Zyklen über QPI/UPI oder Infinity Fabric und drücken so die effektive Bandbreite. Hohe Core-Anzahlen verschärfen diesen Effekt, weil immer mehr Kerne um dieselben Verbindungen konkurrieren. Ich plane daher so, dass heißer Code und aktive Daten auf einem Knoten zusammenfinden. Wer dies missachtet, verschenkt Prozentpunkte, die bei Lastspitzen über Antwortzeit oder Timeout entscheiden.

VM-Größen, NUMA-Trashing und Host-Zuschnitt

Ich dimensioniere VMs so, dass vCPUs und RAM in einen NUMA-Knoten passen, um Cross-Node-Zugriffe zu vermeiden. Häufig liefern 4–8 vCPUs pro Knoten gute Trefferquoten, abhängig von Plattform und Cache-Hierarchie. Zusätzlich helfen Huge Pages, weil der TLB effizienter arbeitet und Seitenmigrationen seltener auftreten. Bei Bedarf setze ich CPU-Affinity für latenzkritische Prozesse, um Threads auf passende Kerne zu binden – mehr dazu unter CPU-Affinity. Wer VMs über Knoten spannt, riskiert NUMA-Trashing, also ein Ping-Pong von Daten und Threads.

Werkzeuge in der Praxis: numactl, lscpu, numad

Mit „lscpu“ lese ich Topologie und NUMA-Knoten, inklusive Zuordnung der Kerne, aus. „numactl –hardware“ zeigt mir Speicher pro Node und verfügbare Distanzen, was die Bewertung der Wege erleichtert. Der Daemon „numad“ beobachtet Auslastung und passt Affinitäten dynamisch an, wenn Lastschwerpunkte wandern. Für fixe Szenarien nutze ich „numactl –cpunodebind/–membind“, um Prozesse und Speicher explizit zu pinnen. So kombiniere ich automatisches Balancing mit gezielten Vorgaben und kontrolliere das Resultat über „perf“, „numastat“ und „/proc“.

Wie ich Wirkung messe: Kennzahlen und Befehle

Ich bewerte NUMA-Tuning immer über Messreihen, nicht über Bauchgefühl. Drei Indikatoren haben sich bewährt: Verhältnis lokaler zu entfernter Seitenzugriffe, Migrationsrate und Latenzverteilung (P95/P99).

  • Systemweit: „numastat“ zeigt lokale/entfernte Zugriffe und migrierte Seiten je Node.
  • Prozessbezogen: „/proc/<pid>/numa_maps“ verrät, wo Speicher liegt und wie verteilt wurde.
  • Scheduler-Sicht: „Cpus_allowed_list“ und echte „Cpus_allowed“ prüfen, ob Bindungen greifen.
# Systemweite Sicht
numastat
numastat -m

# Prozessbezogene Verteilung und Binds
pid=$(pidof <Dienst>)
numastat -p "$pid"
cat /proc/"$pid"/numa_maps | head
cat /proc/"$pid"/status | grep -E 'Cpus_allowed_list|Mems_allowed_list'
taskset -cp "$pid"

# Kernel-Zähler für NUMA-Aktivität
grep -E 'numa|migrate' /proc/vmstat

# Trace-Events für tiefe Analysen (kurzzeitig aktivieren)
echo 1 > /sys/kernel/debug/tracing/events/mm/enable
sleep 5; cat /sys/kernel/debug/tracing/trace | grep -i numa; echo 0 > /sys/kernel/debug/tracing/events/mm/enable

Ich vergleiche jeweils A/B: ungebunden vs. gebunden, Automatic Balancing an/aus und verschiedene VM-Zuschnitte. Ziel ist ein klarer Rückgang entfernter Zugriffe und Migrationsrauschen sowie engere P95/P99-Latenzen. Erst wenn die Messwerte stabil besser sind, übernehme ich das Tuning.

BIOS- und Firmware-Einstellungen, die wirklich tragen

Ich schalte „Node Interleaving“ im BIOS ab, damit die NUMA-Struktur sichtbar bleibt und der Kernel lokal planen kann. Reduzierte C-States stabilisieren Latenzspitzen, weil Kerne seltener in tiefe Schlafzustände fallen, was Aufweckzeit spart. Speicherkanäle belege ich symmetrisch, damit jeder Knoten seine maximale Bandbreite erreicht. Prefetcher und RAS-Features teste ich mit Workload-Profilen, da sie je nach Zugriffsmuster helfen oder schaden. Jede Änderung messe ich gegen eine Baseline, erst dann übernehme ich die Einstellung dauerhaft.

Kernel- und Sysctl-Parameter, die den Ausschlag geben

Das Feintuning im Kernel hilft mir, Overhead und Reaktionszeit des Balancers passend zur Workload einzustellen. Ich starte mit konservativen Defaults und arbeite mich schrittweise vor.

  • kernel.numa_balancing: Ein/Aus des automatischen Balancings. Für wandernde Lasten bleibt es bei mir an; für streng gepinnte Spezialdienste schalte ich es testweise ab.
  • kernel.numa_balancing_scan_delay_ms: Wartezeit vor dem ersten Scan nach Prozesserzeugung. Größer wählen, wenn viele kurzlebige Tasks laufen; kleiner für langlaufende Dienste, die schnell Nähe brauchen.
  • kernel.numa_balancing_scan_period_min_ms / _max_ms: Bandbreite der Scan-Intervalle. Enge Intervalle erhöhen die Reaktionsfreude, aber auch CPU-Last.
  • kernel.numa_balancing_scan_size_mb: Anteil des Adressraums pro Scan. Zu groß erzeugt Hint-Fault-Stürme, zu klein reagiert träge.
  • vm.zone_reclaim_mode: Bei Speicherknappheit bevorzugt der Kernel lokale Reclaim statt Remote-Alloc. Für allgemeine Hosting-Workloads lasse ich meist 0; bei strikt latenzsensitiven, speicherlokalen Diensten teste ich vorsichtig höhere Werte.
  • Transparent Huge Pages (THP): Unter „/sys/kernel/mm/transparent_hugepage/{enabled,defrag}“ stelle ich meist auf madvise und konservatives Defragmentieren. Harte „always“-Profile bringen zwar TLB-Vorteile, riskieren aber Stalls durch Kompaktierung.
  • sched_migration_cost_ns: Kostenschätzung für Task-Migration. Höhere Werte dämpfen das Umverteilen aggressiver Scheduler.
  • cgroups cpuset: Mit cpuset.cpus und cpuset.mems trenne ich Services sauber nach Knoten und stelle sicher, dass First-Touch innerhalb zulässiger Nodes bleibt.
# Beispiel: konservatives, aber reaktionsfreudiges Balancing
sysctl -w kernel.numa_balancing=1
sysctl -w kernel.numa_balancing_scan_delay_ms=30000
sysctl -w kernel.numa_balancing_scan_period_min_ms=60000
sysctl -w kernel.numa_balancing_scan_period_max_ms=300000
sysctl -w kernel.numa_balancing_scan_size_mb=256

# THP vorsichtig nutzen
echo madvise > /sys/kernel/mm/transparent_hugepage/enabled
echo defer > /sys/kernel/mm/transparent_hugepage/defrag

Wichtig bleibt: Nur eine Stellschraube pro Testrunde verändern und die Wirkung gegen dieselbe Lastkurve verproben. So entwirre ich Ursache und Effekt.

Workloads richtig platzieren: Datenbanken, Caches, Container

Datenbanken profitieren, wenn Buffer Pools pro NUMA-Knoten lokal bleiben und Threads nahe an ihre Heaps gebunden sind. In In-Memory-Caches setze ich sharding nach Knoten um, um entfernte Fetches zu vermeiden. Container-Plattformen erhalten Limits und Requests so, dass Pods nicht quer über Knoten springen. Für Speicherreservierungen nutze ich Huge Pages, wodurch Hotsets besser in Caches passen. Die folgende Tabelle fasst Strategien und typische Effekte kompakt zusammen.

Strategie Einsatz Erwarteter Effekt Hinweis
First-Touch Datenbanken, JVM-Heaps Lokale Seitenallokation Initialisierung auf Zielknoten ausführen
Interleave Breit verteilte Last Gleichmäßige Verteilung Nicht optimal bei Hotspots
Task Pinning Latenzkritische Dienste Konstante Latenz Weniger flexibel bei Lastwechsel
Automatic Balancing Gemischte Workloads Dynamische Nähe Overhead gegen Gewinn abwägen
Huge Pages Große Heaps, Caches Weniger TLB-Misses Sauberes Reservieren einplanen

Virtualisierung: Virtual NUMA, Scheduler und Gastanpassung

Virtual NUMA gibt die Host-Topologie in vereinfachter Form an das Gast-OS weiter, damit First-Touch und Allocator sinnvoll arbeiten. Hypervisor-Scheduler achten auf Knotennähe, wenn sie vCPUs verteilen und VMs migrieren. Ich richte große VMs selten über mehrere Knoten aus, außer die Workload streamt breit und profitiert von Interleave. Im Gast passe ich die Heaps von JVMs oder Datenbanken so an, dass sie auf sichtbaren NUMA-Nodes lokal bleiben. Für Speicherverwaltung im Gast hilft ein Blick auf virtueller Speicher, um Seitengrößen und Swapping zu zähmen.

PCIe-Nähe: NVMe und NICs an den richtigen Knoten

NVMe-SSDs und schnelle NICs ordne ich möglichst dem Knoten zu, auf dem die Workload läuft. So vermeide ich, dass I/O-Anfragen den Interconnect queren und Latenz aufaddieren. Multiqueue-NICs binde ich mit RSS/RPS an Kernmengen eines Knotens, damit IRQs lokal bleiben. Für Storage-Stacks lohnt es sich, die Thread-Pools knotenweise zu splitten. Wer das beachtet, drückt P99-Latenzen spürbar und schafft Headroom für Lastspitzen.

IRQ- und Queue-Affinität in der Praxis

Ich prüfe zuerst, auf welchem NUMA-Node Geräte hängen, und pinne IRQs sowie Queues passend. So bleibt Datenpfad-Lokalität gewahrt.

# Gerät-zu-Node-Zuordnung
cat /sys/class/net/eth0/device/numa_node
cat /sys/block/nvme0n1/device/numa_node

# IRQ-Affinität gezielt setzen (Beispiel: Kerne 0-7 eines Knotens)
irq=<IRQ-Nummer>
echo 0-7 > /proc/irq/$irq/smp_affinity_list

# NIC-Queues an Kerne binden (RPS/RFS)
for q in /sys/class/net/eth0/queues/rx-*; do echo 0-7 > "$q"/rps_cpus; done
sysctl -w net.core.rps_sock_flow_entries=32768
for q in /sys/class/net/eth0/queues/rx-*; do echo 4096 > "$q"/rps_flow_cnt; done

# NVMe-Queue-Affinität verbessern
echo 2 > /sys/block/nvme0n1/queue/rq_affinity
cat /sys/block/nvme0n1/queue/scheduler   # "none" bevorzugt

„irqbalance“ lasse ich mit Node-Bewusstsein laufen oder setze Ausnahmen für Hot-Path-Interrupts. Ergebnis sind stabilere Latenzen, weniger Cross-Node-IRQ-Hops und ein messbarer Anstieg lokaler I/O-Treffer.

Statische Bindung vs. dynamisches Balancing – der Mittelweg

Mit „taskset“ und cgroups setze ich harte Regeln, wenn deterministische Latenz zählt. Automatic NUMA Balancing lasse ich aktiv, wenn die Last wandert und ich adaptive Nähe brauche. Eine Mischung funktioniert oft am besten: harte Pins für Hotpaths, offenere Grenzen für Nebenarbeit. Dabei prüfe ich regelmäßig, ob Migrationen auffällig zunehmen, denn das signalisiert Fehlplanung. Ziel bleibt, Daten- und Thread-Orte so zu wählen, dass Migration selten, aber möglich bleibt.

NUMA in Containern und Kubernetes

Container bringe ich mit cpusets und Huge Pages auf Linie. Ich weise Pods/Container einem NUMA-Knoten zu, indem ich konsistente CPU- und Speicher-Mengen hinterlege. In Orchestrierungen setze ich Policys, die Single-Node-Zuweisungen bevorzugen und so First-Touch respektieren.

  • Container-Runtime: „–cpuset-cpus“ und „–cpuset-mems“ halten Tasks und Speicher zusammen; Huge Pages als Ressourcen zuweisen.
  • Topology-/CPU-Manager: Strikte oder präferierte Zuweisungen sorgen dafür, dass zusammengehörige Kerne und Speicherbereiche vergeben werden.
  • Guaranteed QoS: Feste Requests/Limits minimieren Umverteilungen durch den Scheduler.

Ich splitte Sidecars und Hilfsprozesse bewusst auf andere Kerne innerhalb desselben Knotens, damit der Hotpath ungestört bleibt, aber nicht ins Cross-Node-Rennen gerät.

CPU-Topologien verstehen: CCD/CCX, SNC und Cluster-on-Die

Aktuelle Server-CPUs zerlegen Sockel in Subdomänen mit eigenen Caches und Pfaden. Ich berücksichtige das, wenn ich Kerne/Heaps zuschneide:

  • AMD EPYC: CCD/CCX und „NUMA per Socket“ (NPS=1/2/4) beeinflussen, wie fein NUMA geschnitten wird. Mehr Nodes (NPS=4) erhöhen Locality, aber verlangen sauberes Pinning.
  • Intel: Sub-NUMA Clustering (SNC2/4) teilt LLC in Cluster. Gut für speichergebundene Lasten, sofern das OS und die Workload knotenbewusst agieren.
  • L3-Nähe: Ich binde Threads, die dieselben Heaps nutzen, in denselben L3-Verbund, um Coherence-Traffic und Cross-Cluster-Hops zu sparen.

Diese Optionen wirken wie ein Multiplikator: Richtig eingesetzt, heben sie Locality zusätzlich – falsch konfiguriert, erhöhen sie Fragmentierung und Remote-Traffic.

Schrittweise Einführung und Rollback-Plan

Ich führe NUMA-Tuning nie „Big Bang“ ein. Ein belastbarer Plan vermeidet Überraschungen:

  1. Baseline: Hardware-Topologie, P50/P95/P99-Latenzen, Throughput, numastat-Quote erfassen.
  2. Hypothese: Konkretes Ziel formulieren (z. B. Remote-Zugriffe -30%, P99 -20%).
  3. Ein Schritt: Nur eine Stellschraube ändern (z. B. VM-Zuschnitt, cpuset, THP-Policy, Scan-Intervalle).
  4. Canary: Auf 5–10% der Flotte unter Real-Last testen, Rollback parat halten.
  5. Bewertung: Messwerte vergleichen, Regressionsfenster definieren, Nebeneffekte protokollieren.
  6. Rollout: Welle für Welle ausrollen, nach jeder Welle erneut messen.
  7. Wartung: Quartalsweise neu vermessen (Kernel-, Firmware-, Workload-Updates ändern das Optimum).

So stelle ich sicher, dass Verbesserungen reproduzierbar sind und im Fehlerfall binnen Minuten rückgängig gemacht werden können.

Häufige Fehler – und wie ich sie vermeide

Ein typischer Fehltritt ist das Aktivieren von Node Interleaving im BIOS, was die NUMA-Topologie versteckt und Balancing erschwert. Ebenso ungünstig: VMs mit mehr vCPUs als ein Knoten bietet, dazu unsauber reservierte Huge Pages. Manche Admins pinnen alles hart und nehmen sich so jede Flexibilität, wenn Workloads sich verschieben. Andere verlassen sich komplett auf den Kernel, obwohl harte Hotspots klare Regeln erfordern. Ich halte Messreihen fest, erkenne Ausreißer früh und passe Setup und Policies schrittweise an.

  • THP „always“ ohne Kontrolle: Ungeplante Kompaktierung stört Latenz. Ich setze bevorzugt „madvise“ und reserviere Huge Pages gezielt.
  • vm.zone_reclaim_mode zu aggressiv: Lokaler Reclaim kann im falschen Moment mehr schaden als nutzen. Erst messen, dann schärfen.
  • irqbalance blind: Unkritische IRQs wandern quer über Knoten. Für Hotpaths setze ich Ausnahmen oder fixe Masken.
  • Mischung Interleave + hartes Pinning: Gegensätzliche Policies erzeugen Ping-Pong. Ich entscheide mich pro Dienst für eine klare Linie.
  • Unsaubere cpusets: Container sehen zwar einen Node, mappen Speicher aber in andere Nodes. „cpuset.mems“ immer konsistent zum CPU-Set setzen.
  • Sub-NUMA-Features aktiviert, aber nicht genutzt: Mehr Nodes ohne Planung erhöhen Fragmentierung. Erst nach Tests einschalten.

Kurz zusammengefasst

NUMA Balancing Server bringt Prozesse und Daten gezielt zusammen, wodurch lokale Zugriffe häufiger und Latenzen kürzer werden. Mit passender VM-Größe, sauberer BIOS-Konfiguration und Tools wie numactl entsteht eine klare Topologie, die der Kernel ausnutzt. Virtual NUMA, Huge Pages und Affinitäten ergänzen das automatische Balancing, statt es zu ersetzen. Wer I/O-Geräte knotennah anbindet und Hotpaths steckt, eliminiert teure Fernzugriffe. So skaliert Hosting-Hardware verlässlich, und jede CPU-Sekunde liefert mehr Nutzlast.

Aktuelle Artikel

CPU Hyperthreading in Hosting-Servern mit logical cores
Server und virtuelle Maschinen

CPU Hyperthreading im Hosting: Nutzen und Risiken

CPU Hyperthreading im Hosting steigert logical cores performance, birgt aber Risiken. Lernen Sie server tuning für optimale Webserver-Leistung.