- Da Moores Gesetz an seine Grenzen stößt, entwickelt sich Hardware weg von der Beschleunigung einzelner Kerne hin zu Multicore- und Parallelverarbeitung
- Parallelverarbeitung lässt sich in mehrere Formen einteilen, darunter Datenparallelität, Modellparallelität und Pipeline-Parallelität, und wird in modernen Deep-Learning-Systemen häufig kombiniert eingesetzt
- Parallelisierung findet auf verschiedenen Ebenen statt, etwa als SIMD (datenparallele Verarbeitung auf Befehlsebene), Thread-/Kern-Parallelität und massive GPU-Parallelität
- Sprachen, Bibliotheken und Werkzeuge für Parallelverarbeitung sind meist als an bestehende sequenzielle Sprachen "angehängte" Erweiterungen konzipiert; daher erhält der Trend Aufmerksamkeit, Parallelität nativ in die Sprache zu integrieren (z. B. Mojo)
- Praktische Leistungsoptimierungen wie die Optimierung von Cache-Line-Sharing (unnötige Wechselwirkungen), effiziente Speicheraufteilung und automatische Vektorisierung sind zentrale Herausforderungen
Motivation für Parallelität und Hardware-Evolution
- Anfangs stieg die Leistung durch Transistorverkleinerung und höhere Taktraten quasi automatisch, doch durch Grenzen bei Abwärme und Fertigungsprozessen wurden physikalische Limits erreicht
- Danach wurde die Multicore-Architektur zum Standard, und eine CPU enthält heute mehrere bis hunderte Kerne
Allgemeine Formen der Parallelität
- Datenparallelität: Dieselbe Operation wird gleichzeitig auf viele Daten angewendet (z. B. Vektoraddition)
- Modellparallelität: Ein Modell wird verteilt auf mehrere Geräte platziert
- Pipeline-Parallelität: Berechnungen werden in mehrere Stufen aufgeteilt, die gleichzeitig arbeiten
SIMD (Single Instruction, Multiple Data) und Vektorisierung
- SIMD verarbeitet mehrere Datenwerte (Vektoren) mit einem einzigen Befehl und wird von verschiedenen ISAs wie ARM NEON und x86 SSE/AVX unterstützt
- Über C/C++-Intrinsics lassen sich Vektoroperationen explizit steuern; auch die automatische Vektorisierung durch Compiler wird unterstützt, hat jedoch Grenzen
- In der Praxis wird oft zunächst in Schritten der Vektorlänge iteriert, danach werden verbleibende Daten mit Skalaroperationen verarbeitet
Parallelisierung auf der CPU
- Mit Threads lässt sich auf Multicore-Systemen parallel ausführen, unterstützt durch sprachspezifische APIs und den OS-Scheduler
- Da das Erzeugen und Beenden von Threads teuer ist, ist es effizient, die Arbeit mit einer zur Datenmenge passenden und ungefähr der Kernanzahl entsprechenden Thread-Zahl aufzuteilen
- Wichtig ist die Optimierung von Cache-Line-"False Sharing" (Leistungseinbußen, wenn verschiedene Threads auf unabhängige Variablen innerhalb derselben Cache Line zugreifen); dafür kann etwa
std::hardware_destructive_interference_size aus C++17 genutzt werden
- Dafür sind Padding/Ausrichtung nötig, damit jeder Thread in einen separaten Datenbereich schreibt
Parallelisierung auf der GPU
- GPUs sind mit Tausenden kleiner Kerne auf massive datenparallele Verarbeitung spezialisiert
- CUDA/OpenCL: Kernel-Funktionen laufen in Einheiten von Dutzenden bis Zehntausenden Threads/Blöcken; intern folgt dies dem SIMT-Modell (Single Instruction, Multiple Threads)
- Die Ausführung erfolgt in Workgroups/Warps; das Minimieren von Verzweigungsdivergenz ist für die Leistung äußerst wichtig
- Speicherhierarchie: Register pro Thread, Shared Memory pro Block und globaler Speicher erfordern hierarchische Optimierung
Triton: Python-basiertes GPU-Kernel-DSL
- Triton ist ein in Python eingebettetes DSL und unterstützt JIT-Kompilierung sowie mehrere Backends (MLIR/LLVM/PTX usw.)
- Kernel-Code wird in High-Level-Python geschrieben; automatische Parallelisierung, Aufteilung und Maskierung werden unterstützt
- Bei 75–90 % der Leistung von NVIDIA cuDNN sinkt die Entwicklungskomplexität deutlich
Parallelität als natives Sprachelement: Mojo
- Mojo ist eine neue Sprache des LLVM/MLIR-Entwicklers Chris Lattner und unterstützt Parallelität sowie hardwarespezifische Kompilierung auf Sprachebene
- SIMD-Vektortypen, Vektorisierungsfunktionen und die Unterscheidung zwischen Host- und Device-Speicher zeigen, dass Parallelität in Typsystem und Sprachstruktur eingebaut ist
- Auch Python-artige Schleifen können automatisch vektorisiert werden, sodass Leistung ohne explizite Low-Level-Steuerung erreichbar ist
Fazit und Ausblick
- Moderne parallele Programmierung besteht aus der Kombination verschiedener Hardware- und Parallelitätsmodelle, wobei die Unterstützung durch die Sprache selbst immer wichtiger wird
- Mit dem Aufstieg von Parallelsprachen und -werkzeugen der nächsten Generation wie Mojo, Triton und JAX wird Parallelisierung intuitiver und produktiver
- Parallele Programmierung kann die tatsächliche Leistung nur dann maximieren, wenn Hardwarearchitektur, Speicheroptimierung und Sprachunterstützung organisch zusammenspielen
Noch keine Kommentare.