In der Ära moderner Hochleistungs-Computing sind **Multi-Core-CPUs** das Rückgrat vieler Serversysteme, Workstations und sogar High-End-PCs. Sie versprechen enorme Rechenkraft durch die gleichzeitige Ausführung mehrerer Aufgaben. Doch die schiere Anzahl an Kernen und die riesigen Mengen an verarbeitetem Speicher bringen neue Herausforderungen mit sich. Eine dieser Herausforderungen, oft missverstanden oder ignoriert, ist das Konzept von **NUMA (Non-Uniform Memory Access)** – und insbesondere die zunehmende Bedeutung von **NUMA-Knoten pro Socket**. Wer die Leistung seiner Hardware wirklich maximieren möchte, muss NUMA verstehen und optimieren.
### Was ist NUMA überhaupt? Eine Einführung
Bevor wir uns den NUMA-Knoten pro Socket widmen, ist es entscheidend, das grundlegende Konzept von NUMA zu verstehen. In traditionellen Computerarchitekturen (UMA – Uniform Memory Access) haben alle Prozessoren gleich schnellen Zugriff auf den gesamten Systemspeicher. Mit dem Aufkommen von Systemen mit mehreren CPU-Sockets, von denen jeder seinen eigenen Speichercontroller und physisch angeschlossenen Speicher besitzt, wurde UMA unpraktisch und ineffizient.
Hier kommt NUMA ins Spiel. NUMA beschreibt eine Architektur, in der **CPUs** schneller auf ihren „lokalen” Speicher zugreifen können, der direkt an ihren Speichercontroller angeschlossen ist, als auf den „fremden” Speicher, der an einen anderen CPU-Socket angeschlossen ist. Dieser Geschwindigkeitsunterschied, bedingt durch die Notwendigkeit, über eine Interconnect-Verbindung (wie Intels UPI oder AMDs Infinity Fabric) auf den entfernten Speicher zuzugreifen, ist der Kern des Problems und der Optimierungsmöglichkeiten von NUMA. Jede dieser CPU- und Speicher-Kombinationen wird als **NUMA-Knoten** bezeichnet. Ein typisches Zwei-Socket-System hätte also zwei NUMA-Knoten.
### NUMA-Knoten pro Socket: Eine neue Dimension der Komplexität
Die Landschaft der Server-CPUs hat sich in den letzten Jahren drastisch verändert. Während NUMA lange Zeit primär ein Thema für Multi-Socket-Systeme war, hat sich die Komplexität mit modernen Prozessoren wie AMD EPYC oder bestimmten Intel Xeon-Varianten noch einmal erhöht. Heute können selbst **einzelne CPU-Sockets mehrere NUMA-Knoten** beherbergen.
Wie ist das möglich? Nehmen wir als Beispiel AMDs EPYC-Prozessoren. Diese CPUs basieren auf einem „Chiplet”-Design (Multi-Chip Module – MCM), bei dem mehrere kleinere Silizium-Dies (CCDs – Core Complex Dies) auf einem einzigen Prozessor-Package verbaut sind. Jeder dieser CCDs kann einen eigenen Speichercontroller und einen Teil der Gesamtanzahl der Kerne aufweisen. Obwohl sie im selben physischen Sockel sitzen, kommuniziert jeder CCD über die interne Infinity Fabric mit den anderen CCDs und dem restlichen System. Der Zugriff auf den Speicher, der an einen anderen CCD im selben Sockel angeschlossen ist, ist immer noch schneller als der Zugriff auf Speicher in einem anderen physischen Socket, aber langsamer als der Zugriff auf den direkt an den eigenen CCD angeschlossenen Speicher.
Das bedeutet, dass ein einzelner physischer CPU-Socket intern in mehrere logische NUMA-Knoten unterteilt sein kann. Ein System mit einem einzigen AMD EPYC-Prozessor könnte beispielsweise zwei, vier oder sogar mehr NUMA-Knoten aufweisen, je nach CPU-Modell und BIOS-Konfiguration (z.B. NPS – NUMA Nodes per Socket, wo NPS2 oder NPS4 die Anzahl der NUMA-Knoten pro Socket festlegt). Diese Entwicklung stellt eine zusätzliche Ebene der **Speicherhierarchie** und damit eine weitere Herausforderung für die Leistungsoptimierung dar.
### Warum ist NUMA so wichtig für die Leistung?
Die Auswirkungen von NUMA auf die Systemleistung können erheblich sein und sich in verschiedenen Bereichen bemerkbar machen:
1. **Latenz:** Der offensichtlichste und kritischste Faktor ist die **Speicherlatenz**. Das Abrufen von Daten aus lokalem NUMA-Speicher ist erheblich schneller als aus entferntem Speicher. Für latenzempfindliche Anwendungen (Datenbanken, Hochfrequenzhandel, HPC-Simulationen) kann jeder Millisekunde-Bruchteil entscheidend sein.
2. **Bandbreite:** Auch die **Speicherbandbreite** ist betroffen. Jeder NUMA-Knoten hat Zugriff auf die volle Bandbreite seines lokalen Speichers. Wenn Threads auf einem Knoten versuchen, große Datenmengen von einem anderen Knoten zu lesen oder zu schreiben, müssen diese Daten über die Interconnect-Links geleitet werden, was die Gesamtbandbreite des Systems sättigen und einen Engpass verursachen kann.
3. **Cache-Kohärenz:** Um sicherzustellen, dass alle Prozessorkerne die aktuellsten Daten sehen, müssen die CPU-Caches über die Interconnect-Verbindungen kohärent gehalten werden. Dies verursacht Overhead. Wenn Threads und die von ihnen benötigten Daten auf demselben NUMA-Knoten verbleiben, wird der Overhead für die **Cache-Kohärenz** minimiert.
4. **Skalierbarkeit:** Eine schlechte NUMA-Optimierung kann die **Skalierbarkeit** von Anwendungen auf Systemen mit vielen Kernen und hohem Speicherdurchsatz stark einschränken. Die Leistung steigt nicht linear mit der Anzahl der hinzugefügten Kerne, wenn NUMA-Engpässe bestehen.
### NUMA erkennen und verstehen: So finden Sie Ihre Konfiguration heraus
Bevor Sie optimieren können, müssen Sie wissen, wie Ihr System konfiguriert ist. Glücklicherweise gibt es Tools, um die NUMA-Topologie zu erkennen:
* **Linux:**
* `lscpu`: Zeigt eine Übersicht der CPU-Architektur, einschließlich der Anzahl der NUMA-Knoten.
* `numactl –hardware`: Dies ist das Standardwerkzeug. Es zeigt detaillierte Informationen über die NUMA-Knoten, die ihnen zugewiesenen CPUs und den verfügbaren Speicher pro Knoten.
* `hwloc-ls` (aus dem `hwloc`-Paket): Bietet eine hierarchische Ansicht der Hardware-Topologie, die NUMA-Knoten, Sockets, Cores und Caches umfasst.
* **Windows:**
* **Task-Manager:** Unter dem Reiter „Leistung” und dann „CPU” sehen Sie die logischen Prozessoren. Wenn Sie mit der rechten Maustaste auf die Diagramme klicken und „NUMA-Knoten” auswählen, werden Ihnen die einzelnen Knoten und deren Auslastung angezeigt.
* **Systeminformationen (msinfo32):** Kann auch grundlegende NUMA-Informationen liefern.
* **PowerShell:** `Get-WmiObject -Class Win32_Processor | Select-Object NumberOfCores, NumberOfLogicalProcessors, L3CacheSize, SocketDesignation` oder komplexere Skripte können NUMA-Details ausgeben.
Achten Sie besonders auf die Ausgabe, die die **Zuordnung von Cores zu NUMA-Knoten** und die **verfügbare Speichermenge pro Knoten** zeigt. Dies ist die Grundlage für jede Optimierungsstrategie.
### Strategien zur NUMA-Optimierung: Maximale Leistung freisetzen
Die Maximierung der Leistung auf NUMA-Systemen erfordert einen ganzheitlichen Ansatz, der sowohl die Software als auch die Hardware-Konfiguration berücksichtigt. Das Kernprinzip ist **Datenlokalität**: Halten Sie Threads und die Daten, auf die sie zugreifen, auf demselben NUMA-Knoten.
1. **Anwendungsdesign und Programmierung:**
* **NUMA-Awareness:** Im Idealfall sind Anwendungen NUMA-Aware programmiert. Dies bedeutet, dass sie bewusst **Speicher auf dem NUMA-Knoten allozieren**, auf dem die Threads ausgeführt werden, die diesen Speicher nutzen werden.
* **Thread-Affinität:** Weisen Sie Threads gezielt CPU-Kernen auf einem bestimmten NUMA-Knoten zu. Dies kann manuell über Bibliotheken (z.B. `libnuma` in Linux) oder durch spezielle APIs in anderen Betriebssystemen geschehen.
* **Datenstrukturen:** Entwerfen Sie Datenstrukturen so, dass sie möglichst lokal auf einem Knoten bleiben und nicht über Knoten hinweg verteilt sind.
* **Speicher-Allokation:** Nutzen Sie Funktionen wie `numa_alloc_local()` oder `VirtualAllocExNuma()` (Windows), um Speicher bevorzugt auf dem lokalen Knoten zu allozieren.
2. **Betriebssystem-Scheduling:**
* Moderne Betriebssysteme (Linux, Windows Server) verfügen über **NUMA-optimierte Scheduler**. Diese versuchen standardmäßig, Threads auf dem NUMA-Knoten zu halten, auf dem sie zuletzt liefen und Speicher alloziiert haben.
* Überprüfen Sie, ob diese Funktionen aktiviert sind. In manchen Fällen kann es sinnvoll sein, bestimmte Scheduler-Parameter anzupassen, um die NUMA-Lokalisierung zu fördern.
* Für kritische Workloads kann es nötig sein, die **CPU-Affinität** manuell zu setzen, um sicherzustellen, dass Prozesse oder Threads auf bestimmten Kernen eines bestimmten NUMA-Knotens laufen.
3. **`numactl` (Linux-spezifisch):**
* Das `numactl`-Dienstprogramm ist ein mächtiges Werkzeug unter Linux, um die NUMA-Einstellungen für Prozesse zu steuern, die nicht NUMA-Aware sind.
* `numactl –membind= –cpunodebind= `: Mit diesem Befehl können Sie festlegen, auf welchen NUMA-Knoten ein Prozess CPU-Kerne verwenden darf und auf welchen Knoten er seinen Speicher allozieren soll.
* Beispiel: `numactl –membind=0 –cpunodebind=0 my_application` würde `my_application` auf NUMA-Knoten 0 einschränken.
* Dies ist besonders nützlich für Serveranwendungen oder Datenbanken, bei denen Sie die Kontrolle über die Ressourcenzuweisung benötigen.
4. **Virtualisierung (VMware, Hyper-V, KVM):**
* In virtualisierten Umgebungen spielt NUMA eine noch größere Rolle, da hier zwei Ebenen der Ressourcenverwaltung hinzukommen: das Host-Betriebssystem und der Hypervisor.
* **VM-Topologie anpassen:** Konfigurieren Sie virtuelle Maschinen so, dass die Anzahl der zugewiesenen vCPUs und der Speicher in die Topologie der physischen NUMA-Knoten des Hosts passt.
* Weisen Sie einer VM nicht mehr vCPUs oder Speicher zu, als ein einzelner physischer NUMA-Knoten auf dem Host bereitstellen kann, es sei denn, die VM ist selbst NUMA-Aware und kann die verteilten Ressourcen effizient nutzen.
* Die meisten Hypervisoren bieten Einstellungen zur **NUMA-Transparenz** oder **NUMA-Lokalisierung** für VMs, um die bestmögliche Leistung zu erzielen. Achten Sie auf Optionen wie „NUMA spanning” oder „vNUMA”.
5. **BIOS-Einstellungen:**
* Manche Server-BIOSse bieten Einstellungen zur Konfiguration der NUMA-Topologie. Insbesondere bei AMD EPYC-Systemen ist die Einstellung der **NPS-Modi (NUMA Nodes per Socket)** von großer Bedeutung.
* NPS0 (ein NUMA-Knoten pro Socket) fasst alle CCDs eines Sockets zu einem logischen NUMA-Knoten zusammen, was die Latenz innerhalb des Sockets erhöht, aber die Komplexität für das OS reduziert.
* NPS2, NPS4 (oder mehr) unterteilen den Socket in mehrere NUMA-Knoten, was die Latenz für den lokalen Speicherzugriff innerhalb jedes CCD verbessert, aber die NUMA-Verteilung für das OS komplexer macht.
* Die Wahl des optimalen NPS-Modus hängt stark von der Arbeitslast ab. Für latenzempfindliche Anwendungen mit Threads, die auf die einzelnen CCDs verteilt werden können, sind höhere NPS-Modi oft vorteilhaft. Für Anwendungen, die einen großen Speicherbereich benötigen, der von mehreren Kernen im selben Socket gemeinsam genutzt wird, könnte NPS0 einfacher zu verwalten sein.
6. **Benchmarking und Überwachung:**
* Messung ist der Schlüssel zur Optimierung. Führen Sie Benchmarks durch, um die Auswirkungen von NUMA-Optimierungen zu quantifizieren.
* Nutzen Sie Systemüberwachungstools (z.B. `mpstat`, `vmstat`, `sar` unter Linux; Performance Monitor unter Windows), um Speicherzugriffsmuster, CPU-Auslastung pro NUMA-Knoten und Cache-Misses zu verfolgen. Tools wie `numastat` (Linux) liefern spezifische Metriken zu NUMA-Zugriffen.
* Analysieren Sie die Ergebnisse kritisch und passen Sie Ihre Strategie basierend auf den gewonnenen Daten an.
### Fazit: NUMA als Schlüssel zur Hochleistung
Das Verständnis und die effektive Verwaltung von **NUMA-Knoten pro Socket** sind keine optionalen Feinheiten mehr, sondern eine grundlegende Voraussetzung, um die volle Leistungsfähigkeit moderner Multi-Core-CPUs auszuschöpfen. Insbesondere mit der Zunahme von komplexen Prozessorarchitekturen, die selbst innerhalb eines einzelnen Sockels mehrere NUMA-Domains aufweisen, ist das Bewusstsein für die **Speicherhierarchie** und die **Datenlokalität** wichtiger denn je.
Indem Sie sich aktiv mit Ihrer NUMA-Topologie auseinandersetzen, Anwendungen entsprechend gestalten, Betriebssystem-Einstellungen optimieren und die richtigen Tools einsetzen, können Sie Engpässe vermeiden, die Latenz reduzieren und die Skalierbarkeit Ihrer Systeme drastisch verbessern. Investieren Sie Zeit in die NUMA-Optimierung – es ist ein entscheidender Schritt, um Ihre **Multi-Core-CPU-Leistung** wirklich zu maximieren und das volle Potenzial Ihrer High-End-Hardware freizusetzen.