CPU- und GPU-Kernbindungsstrategie basierend auf Affinität

CPU- und GPU-Kernbindungsstrategie basierend auf Affinität

Einführung in die Optimierung von CPU- und GPU-Leistung

Im Hochleistungsrechnen und bei der Verarbeitung großer paralleler Aufgaben sind GPUs zu unverzichtbaren Beschleunigern geworden. Um die GPU-Rechenfähigkeiten voll auszuschöpfen, ist es entscheidend, die Beziehung zwischen CPU und GPU zu optimieren, indem CPU-Kerne angemessen zugewiesen und an GPUs gebunden werden. Dieser Artikel befasst sich eingehend mit den Konzepten von Sockets und NUMA (Non-Uniform Memory Access) und erörtert, wie basierend auf diesen Hardwarearchitekturen eine CPU- und GPU-Kernbindung implementiert werden kann, um eine optimale Systemleistung zu gewährleisten.

Socket-Konzept

Was ist ein Socket?

Ein Socket bezieht sich in der Regel auf den physischen CPU-Installationssteckplatz auf dem Motherboard. Jeder Socket entspricht einer physischen CPU, die normalerweise mehrere Kerne und eine oder mehrere Cache-Ebenen (z. B. L1-, L2-, L3-Caches) enthält. In Systemen mit mehreren Sockets (z. B. Dual- oder Quad-Server) hat jeder Socket eine physische CPU, die über eine schnelle Verbindung (z. B. Intels QPI oder AMDs Infinity Fabric) verbunden ist.

Eigenschaften von Multi-Socket-Systemen

In Multi-Socket-Systemen kann die CPU jedes Sockets auf ihren lokalen Speicher zugreifen und auch auf Speicher von anderen Sockets zugreifen. Dieses Speicherzugriffsmuster führt das Konzept von NUMA ein, das darauf abzielt, die Speicherzugriffseffizienz zu optimieren.

NUMA-Architektur (Non-Uniform Memory Access)

Was ist NUMA?

NUMA steht für Non-Uniform Memory Access, was nicht einheitlichen Speicherzugriff bedeutet. Im Gegensatz zum traditionellen uniformen Speicherzugriff (UMA) wird der Systemspeicher in der NUMA-Architektur in mehrere Regionen unterteilt, die jeweils einer bestimmten CPU (Socket) zugeordnet sind. Der Zugriff auf lokalen Speicher (innerhalb desselben Sockets) ist schneller als der Zugriff auf entfernten Speicher (von einem anderen Socket), was zu einer höheren Latenz führt.

NUMA-Knoten und Speicherzugriffsverzögerung

In NUMA-Systemen bilden jeder Socket und sein direkt angeschlossener Speicher einen NUMA-Knoten. Der Speicherzugriff innerhalb desselben NUMA-Knotens ist schneller, während der knotenübergreifende Speicherzugriff aufgrund zusätzlicher Busübertragungen eine höhere Latenz aufweist. Die Optimierung der Speicher- und CPU-Affinität, um sicherzustellen, dass Aufgaben innerhalb desselben NUMA-Knotens ausgeführt werden, ist ein entscheidender Schritt zur Leistungsoptimierung.

Physische Beziehung zwischen CPU und GPU

GPU-Hardwarearchitektur

GPUs kommunizieren normalerweise über PCIe-Busse (Peripheral Component Interconnect Express) mit CPUs. In Multi-Socket-Systemen sind GPUs normalerweise nur mit einem Socket (und dem entsprechenden NUMA-Knoten) verbunden und nicht socketsübergreifend. Dies bedeutet, dass die GPU im tatsächlichen Betrieb eine höhere Bandbreite und geringere Latenz zu den CPU-Kernen und dem Speicher des verbundenen Sockets aufweist.

CPU- und GPU-Affinität

Die Kommunikation zwischen CPU und GPU beruht hauptsächlich auf Datenübertragung. Daten werden von der CPU zur GPU und zurück zur CPU übertragen, was Speicherzugriffsoperationen umfasst, die die Leistung erheblich beeinflussen. Wenn der an die GPU gebundene CPU-Kern sich im selben NUMA-Knoten wie die GPU befindet, wird die Datenübertragungslatenz erheblich reduziert. Daher ist das Binden von CPU-Kernen an GPUs ein entscheidender Schritt zur Leistungsoptimierung.

GPU- und CPU-Kernbindungsstrategie basierend auf Affinität

Bei der containerisierten Bereitstellung mit Docker ist die Implementierung der GPU- und CPU-Affinitätsbindung entscheidend für die Leistungsverbesserung containerisierter Aufgaben. Die CPU- und GPU-Ressourcensteuerungsfunktionen von Docker ermöglichen eine präzise Steuerung der von Containern verwendeten CPU-Kerne und GPUs.

Container-CPU- und GPU-Ressourcenzuweisung

Docker-Container ermöglichen eine präzise Steuerung der zugewiesenen CPU- und GPU-Ressourcen. Durch die Angabe der von einem Container verwendeten CPU-Kerne und GPU-Geräte kann eine Affinitätsbindung implementiert werden, um die Leistung zu optimieren.

Docker-CPU-Einstellungen

In Docker kann die CPU-Ressourcenzuweisung mit den folgenden Parametern gesteuert werden:

  • --cpuset-cpus: Gibt die physischen CPU-Kerne an, die vom Container verwendet werden können. Beispielsweise schränkt --cpuset-cpus="0-3" den Container auf die Verwendung der CPU-Kerne 0 bis 3 ein.
  • --cpu-shares: Steuert das CPU-Nutzungsgewicht des Containers, schränkt jedoch nicht die Nutzung bestimmter Kerne ein.
  • --cpus: Begrenzt die Gesamtzahl der vom Container verwendbaren CPU-Kerne (in virtuellen Kernen).

Docker-GPU-Einstellungen

Die GPU-Gerätebindung kann mit den folgenden Docker-Parametern erreicht werden:

  • --gpus: Gibt die GPU-Geräte an, auf die der Container zugreifen kann. Beispielsweise weist --gpus '"device=0"' GPU 0 dem Container zu.

Implementierung der GPU- und CPU-Kernbindung in Docker

Um die GPU- und CPU-Kernbindung in Docker-Containern zu implementieren, kombinieren Sie die CPU- und GPU-Einstellungen. Hier ein Beispiel, wie Sie beim Starten eines Docker-Containers bestimmte CPU-Kerne und eine GPU binden:

docker run --cpuset-cpus="0-3" --gpus '"device=0"' --memory="8g" my_gpu_container

In diesem Befehl:

  • --cpuset-cpus="0-3" bindet den Container an die CPU-Kerne 0 bis 3, die sich im selben NUMA-Knoten wie GPU 0 befinden sollten.
  • --gpus '"device=0"' weist GPU 0 dem Container zu.
  • --memory="8g" begrenzt die Speichernutzung des Containers auf 8 GB und stellt sicher, dass die Speicherzuweisung ebenfalls mit der CPU/GPU-Affinität abgestimmt ist.

Erreichen einer optimalen Bindung

Um eine optimale Bindung in Containern zu gewährleisten, bestimmen Sie zunächst die physische Topologie des Rechners. Verwenden Sie nvidia-smi topo -m, um die Topologie des Rechners anzuzeigen:

nvidia-smi topo -m

Aus der Ausgabe können Sie die NUMA-Konfiguration und die GPU-Zuweisungen des Rechners entnehmen. Wenn der Rechner beispielsweise zwei NUMA-Knoten hat, NUMA 0 mit 4 GPUs und NUMA 1 mit 4 GPUs, können Sie die GPU- und CPU-Affinität für jeden NUMA-Knoten identifizieren.

Pseudocode für die Affinitätsberechnung

Hier ist ein vereinfachter Pseudocode zur Berechnung der Affinität und Bestimmung der GPU- und CPU-IDs:

type Affinity struct {
}

// Berechnet, ob die Affinität erfüllt ist, und gibt die entsprechenden GPU- und CPU-IDs zurück
func calAffinity(affinity *Affinity, cpuUse []int, gpuUse []int, gpuReq int, cpuReq int) (bool, []int, []int) {
    return true, []int{}, []int{}
}

Diese Methode kann verwendet werden, um zu bestimmen, ob ein Knoten die optimalen Bindungsanforderungen erfüllt. Wenn mehrere Knoten vorhanden sind, kann diese Methode verwendet werden, um den am besten geeigneten Knoten zu bewerten und auszuwählen. Novita AI hat eine Container-Engine entwickelt, die speziell für die nächste Generation von KI-Berechnungen zugeschnitten ist und Algorithmen dynamisch anpasst, um die Hardwareauslastung in Echtzeit zu überwachen und eine End-to-End-Optimierung durchzuführen. Benutzer können die stärkste Rechenleistung nutzen, ohne sich um NUMA-technische Details kümmern zu müssen.

Benutzer können im Control Panel auch erweiterte NUMA-Einstellungen vornehmen. Wenn Sie weitere Anforderungen haben, kontaktieren Sie uns bitte auf Discord!

Besuchen Sie Novita AI für weitere Details!

single numa