eBook „Compiler-Welten“ Compiler-Infrastrukturen und ihre Geheimnisse

Von Filipe Martins & Anna Kobylinska 7 min Lesedauer

Performante Code-Ausführung erfordert einen leistungsstarken Compiler – oder mehrere. Bei dieser Entscheidung haben Entwicklerinnen und Entwickler ohnehin schon die Qual der Wahl. Die Konvergenz aktueller Trends stellt nebenbei noch bewährte Arbeitsabläufe auf den Kopf.

Das Aufkommen von Künstlicher Intelligenz (KI) und Machine Learning (ML) wälzt gerade die Compiler-Szene um.
Das Aufkommen von Künstlicher Intelligenz (KI) und Machine Learning (ML) wälzt gerade die Compiler-Szene um.
(Bild: © alisaaa - stock.adobe.com_631699732)
eBook Compiler-Welten
eBook „Compiler-Welten“
(Bild: Dev-Insider)

E-Book zum Thema

In diesem eBook erfahren Sie alles über die neuesten Trends in der Compiler-Szene und wie Sie diese für sich nutzen können.


Das Aufkommen revolutionärer Programmierparadigmen und neuer Technologien geht auch an Compiler-Infrastrukturen nicht spurlos vorbei. Das Aufkommen von Künstlicher Intelligenz (KI) und Machine Learning (ML) wälzt gerade die Compiler-Szene um. Es verspricht, die Qualität und Performance von ausführbarem Code exponentiell zu steigern.

Zwei beeindruckende Werkzeuge, die diese Fusion von Alt und Neu verkörpern, sind der quelloffene ML-Compiler XLA (Accelerated Linear Algebra) von TensorFlow und TorchScript, eine Teilmenge von Python für den PyTorch JIT Compiler. Die Fähigkeiten dieser beiden Lösungen gehen über die reine Kompilierung hinaus. Das Resultat ist eine Kaskade von Leistungsverbesserungen und optimierter Ressourcennutzung.

Compiler spielen zudem eine Schlüsselrolle als Vermittler zwischen ML-Modellen und der Hardware, und adressieren auch leistungsarme Geräte an der Edge. Dieser Trend verstärkt die ohnehin schon fortgeschrittene Fragmentierung der Infrastruktur im Bereich des maschinellen Lernens über verschiedene Frameworks, Compiler und Laufzeiten hinweg.

Diese Entwicklung erschwert die Integration von Toolchains. Quelloffene Projekte wie PJRT (Portable JIT Runtime) und MLIR (Multi-Level Intermediate Representation), eine Infrastruktur für ML-Compiler, wollen dieses Problem mildern.

API-zentriert: Die Bereitstellung der PJRT-API zum Verpacken eines Compiler-Plug-ins ermöglicht eine nahtlose Integration im ML-Ökosystem – Framework, Compiler, Laufzeit, Hardware – und erleichtert die Portabilität von ML-Workloads über verschiedene Hardware-Plattformen hinweg. Die PJRT-API ist rückwärtskompatibel.
API-zentriert: Die Bereitstellung der PJRT-API zum Verpacken eines Compiler-Plug-ins ermöglicht eine nahtlose Integration im ML-Ökosystem – Framework, Compiler, Laufzeit, Hardware – und erleichtert die Portabilität von ML-Workloads über verschiedene Hardware-Plattformen hinweg. Die PJRT-API ist rückwärtskompatibel.
(Bild: Google)

Das MLIR-Framework will der explodierenden Komplexität der Softwareentwicklung im Bereich des maschinellen Lernens Einhalt gebieten, indem es die Kosten für den Aufbau von domänenspezifischen Compilern senkt, die Kompilierung für heterogene Hardware verbessert und die Optimierung der Leistung von ML-Modellen über ein breites Spektrum an Rechenumgebungen erleichtert.

Dieses revolutionäre Framework stellt einen bedeutenden Fortschritt in der Integration von hochentwickelten maschinellen Lernmodellen und anspruchsvollen Hardware-Optimierungen dar. Dank der Unterstützung seitens AMD, ARM, Intel, Nvidia, Qualcomm und anderer Marktakteure großen Kalibers kann MLIR bereits mehr als 95 Prozent der KI-Beschleuniger in Rechenzentren weltweit abdecken.

Mit seinen vielschichtigen Abstraktionsebenen bietet MLIR den Entwicklern und Entwicklerinnen die Möglichkeit, nahtlos zwischen algorithmischen Entwürfen und granularen Hardware-Anweisungen zu navigieren. Dies erleichtert wiederum die mühsame Aufgabe, die Leistung von ML-Modellen für verschiedene Rechenumgebungen zu optimieren.

Zukunftsträchtig: Die verschiedenen Bausteine des Open CIM Compilers.
Zukunftsträchtig: Die verschiedenen Bausteine des Open CIM Compilers.
(Bild: cfaed Center For Advancing Electronics, Technische Universität Dresden)

Auf dem Unterbau von MLIR entstand unter anderem der Open CIM Compiler (OCC), eine leistungsstarke Kompilierungsinfrastruktur und eine Suite von Tests für einen Computing-In-Memory- oder kurz CIM-Gerätesimulator am Center For Advancing Electronics der Technischen Universität Dresden.

KI/ML-gestützt optimieren und kompilieren

Durch die KI-gestützte Analyse großer Datenmengen lassen sich Zusammenhänge aus der realen Welt ableiten und die Nachwirkungen von Entscheidungen und anderen Ereignissen vorausschauend modellieren – auch in einem Compiler.

Die Forschung an Compilern läuft auf Hochtouren. Fortgeschrittene Techniken der Parallelisierungs- und Schleifenoptimierung, der Datenflussanalyse und Automatisierung von Compiler-Optimierungsaufgaben gewinnen immer mehr an Bedeutung unter Einbezug von Techniken des Tiefen Verstärkungslernens und anderer ML-Methoden.

Einige Forscher schlagen maschinelles Lernen vor, um Probleme der Compiler-Instabilität zu mildern und so effektivere Code-Optimierungen zu ermöglichen. Die Integration von KI/ML-Technologien in Compiler-Infrastrukturen soll nebenbei auch eine vorausschauende Fehleranalyse ermöglichen. Projekte wie OCC und Apache TVM haben schon in dieser Hinsicht einiges vollbracht.

  • Der Open CIM Compiler erweitert eine Compiler-Infrastruktur um eine Sammlung von Tests für einen CIM-Gerätesimulator (kurz für Computing-In-Memory), um mehrstufiges IR-Rewriting zu ermöglichen.
  • Der TVM-Compiler (Tensor Virtual Machine) führt ausgefuchste Optimierungen auf Graphen- und auf Operator-Ebene durch, um die Leistungsfähigkeit von Deep-Learning-Workloads auf verschiedenen Hardware-Backends zu gewährleisten. Es kompiliert Deep-Learning-Modelle in minimale, bereitstellungsfähige Module (Engl. minimum deployable modules); diese lassen sich sehr effizient auf verschiedenen Hardwareplattformen ausführen, von CPUs über GPUs, FPGAs bis hin zu einem Webbrowser. TVM macht sich unter anderem ML-basierte Optimierungen zu Nutze, um in einem großen Suchgefilde von OpenCL-Kernels die leistungsstärksten Implementierungen ausfindig zu machen.

Compiler für probabilistische Programmierung

eBook Compiler-Welten
eBook „Compiler-Welten“
(Bild: Dev-Insider)

E-Book zum Thema

In diesem eBook erfahren Sie alles über die neuesten Trends in der Compiler-Szene und wie Sie diese für sich nutzen können.


Probabilistische Programmierung zielt darauf ab, Software zu erzeugen, die mit Unsicherheit und Wahrscheinlichkeiten umgehen kann (Stichwort: ML auf leistungsschwacher Edge-Hardware). Probabilistische Modelle machen sich eine Zufallsstichprobe von Werten aus Wahrscheinlichkeitsverteilungen zu Nutze, konditionieren die beobachteten Daten und führen auf dieser Basis die Inferenz durch. Der Ansatz bewährt sich in KI-nahen oder KI-getriebenen Anwendungsszenarien in der Finanzbranche, im Gesundheitswesen, in der Bioinformatik und in der Cybersicherheit.

TensorFlow Probability (TFP) ist ein Beispiel für eine Bibliothek, die probabilistische Programmierung in einem Tensor-Fluss-Computing-Framework integriert. TFP nutzt TensorFlows Fähigkeiten zur automatischen Differenzierung, um probabilistische Modelle zu optimieren und Inferenz zu ermöglichen. Sie erweitert Compiler-Infrastrukturen und Laufzeitumgebungen, um Unterstützung für probabilistische Programmierung zu gewährleisten.

Jetzt Newsletter abonnieren

Täglich die wichtigsten Infos zu Softwareentwicklung und DevOps

Mit Klick auf „Newsletter abonnieren“ erkläre ich mich mit der Verarbeitung und Nutzung meiner Daten gemäß Einwilligungserklärung (bitte aufklappen für Details) einverstanden und akzeptiere die Nutzungsbedingungen. Weitere Informationen finde ich in unserer Datenschutzerklärung.

Aufklappen für Details zu Ihrer Einwilligung

Compiler (und Laufzeitumgebungen) für probabilistische Sprachen müssen mit spezialisierten Optimierungen für statistische Modelle aufwarten. Probabilististische Programmierung spielt sich hauptsächlich in domänenspezifischen Sprachen wie Pyro, Stan und Church ab. Compiler-Infrastrukturen für probabilistische Programmierung müssen zur Durchführung hochspezialisierter Optimierungen unter anderem erweiterte Typsysteme und automatische Differenzierung beherrschen.

Quantenprogrammierung und -kompilierung

Quantencomputer können bestimmte Probleme bekannterweise schneller lösen als klassische Systeme, indem sie sich die Prinzipien der Quantenmechanik zu Nutze machen.Quanten-Programmierer entwickeln Quantenalgorithmen als Quantenschaltkreise, bestehend aus Quantengattern, Schaltern, Operatoren und dergleichen anderen Komponenten.

Quantenkompilierer transformieren hochstufige Quantenalgorithmen in ausführbare maschinelle Anweisungen für spezifische Quantenprozessoren. Sie übersetzen hierzu High-Level-Quantenalgorithmen (zum Beispiel aus Quantum Assembly Language, QASM) in die sogenannten Quantenbefehlssätze für eine ganz bestimmte Quantenarchitektur (Engl. quantum instruction sets). Erst diese kompilierten, niedrigstufigen Anweisungen lassen sich dann auf einem bestimmten Quantenprozessor ausführen.

Quantenkompilierbar: Vorschlag für den Ablauf der Ausführung des Simon-Algorithmus mit dynamisch ausgewählter Quantenhardware einer Forschungsgruppe der Universität Stuttgart.
Quantenkompilierbar: Vorschlag für den Ablauf der Ausführung des Simon-Algorithmus mit dynamisch ausgewählter Quantenhardware einer Forschungsgruppe der Universität Stuttgart.
(Bild: B. Weder, J. Barzen, F. Leymann, M. Salm, Automated Quantum Hardware Selection for Quantum Workflows)

Quantenkompilierer analysieren und optimieren den Quantenschaltkreis für die Ausführung auf der betreffenden Quantenhardware. Da Qubits für Rauschen und Dekohärenz anfällig sind, müssen sie auch spezielle Quantenfehlerkorrekturen durchführen.

Klassische Compilerinfrastrukturen auf dem Weg in eine (ungewisse) Zukunft

In der Zwischenzeit müssen auch althergebrachte Compiler-Infrastrukturen mit der Zeit gehen und neue Programmierparadigma angemessen unterstützen – zum Beispiel reaktive Programmiermuster.

Die meisten traditionellen Compiler von Hochsprachen wie C++, Java oder Python – darunter GCC und Clang/LLVM – sind primär darauf ausgerichtet, statischen Code in Maschinencode oder Bytecode zu übersetzen, ohne dabei spezifisch auf reaktive Muster oder asynchrone Datenflüsse Rücksicht zu nehmen.

Die Reaktive Programmierung ging aus der Erkenntnis hervor, dass Software auf Änderungen in Datenströmen und andere Ereignisse reagieren könnte (oder besser gesagt: sollte), ohne explizit für jedes erdenkliche Szenario programmiert zu werden – alles Andere wäre natürlich Schnee von gestern.

Dieser Ansatz bewährt sich insbesondere in Anwendungen, die in Echtzeit eine Vielzahl von unvorhersehbaren Datenaktualisierungen handhaben müssen. Komplexe Benutzerschnittstellen in Web- oder sonstigen Anwendungen sind nur ein Beispiel von vielen.

Reaktive Programmierung und die Verwaltung von Datenströmen werden typischerweise durch Bibliotheken oder Laufzeitumgebungen bereitgestellt. Einige der bekanntesten Bibliotheken und Frameworks für die reaktive Programmierung sind RxJava für Java, Reactor für die JVM, ReactiveX für verschiedene Sprachen (wie RxJS für JavaScript) und Akka Streams für Scala und Java.

Gewisse Sprachfeatures und die zugehörigen Compiler-Fähigkeiten können die Entwicklung reaktiver Anwendungen erleichtern, darunter:

  • Asynchronität und nicht blockierende E/A-Operationen (Engl.: asynchrony and non-blocking IO): Sprachen, die native Unterstützung für Asynchronität bieten (z.B. durch async/await in C# und JavaScript oder Coroutines in Kotlin) vereinfachen die Implementierung reaktiver Logik.
  • Funktionale Programmierkonzepte: Programmiersprachen mit ausgereifter Unterstützung für Konzepte wie die Unveränderlichkeit (Engl.: immutability) und Funktionen höherer Ordnung (Engl.: higher-order functions, z.B. map, filter, reduce) wie Scala, Kotlin oder JavaScript schaffen eine geeignete Basis für reaktive Programmiermuster.
  • Typsystem: Ein starkes, ausdruckfähiges Typsystem erleichtert die Entwicklung reaktiver Anwendungen, insbesondere wenn es um die Handhabung komplexer Datenstrukturen und die Vermeidung von Fehlern geht; Konzepte wie Typinferenz und Generics unterstützen die Erstellung flexibler und wiederverwendbarer reaktiver Abstraktionen.
  • Metaprogrammierung und Annotationsverarbeitung: Einige reaktive Frameworks erleichtern gezielt das Erstellen von Boilerplate-Code zur Laufzeit oder zur Kompilierzeit, was die Entwicklung vereinfacht und die Performance verbessern kann.
  • Laufzeitumgebung und JIT-Optimierungen: Während dies nicht direkt eine Anforderung an den Compiler darstellt, ist eine Laufzeitumgebung, die JIT-Kompilierung (Engl. Just-In-Time) oder ähnliche Optimierungstechniken unterstützt, eine wichtige Voraussetzung dafür, um die Performance reaktiver Anwendungen zu maximieren. Insbesondere jene reaktiven Anwendungen, die eine große Menge an kleinen, asynchronen Aufgaben verarbeiten, ziehen großen Nutzen aus JIT-Optimierungen.

Die besten Compiler für Reaktive Programmierung gehen über diese Features hinaus.

Fazit

Vor dem Hintergrund dieser umwälzenden Trends durchläuft die Compiler-Szene tiefgreifende Veränderungen. Das wachsende Interesse an innovativen Compiler-Infrastrukturen stellt die ganze Dev-Landschaft auf den Kopf. Developer „stimmen mit den Füßen ab“: Auf der Suche nach der besten Toolchain betreten sie auch in Sachen Compiler, wohl oder übel, unerforschtes Terrain – mehr dazu im eBook „Compiler-Welten“.

E-Book zum Thema

Compiler-Welten

eBook Compiler-Welten
eBook „Compiler-Welten“
(Bild: Dev-Insider)

Auf der Suche nach der besten Toolchain prägen aktuelle Trends in der Compiler-Welt die Zukunft der Softwareentwicklung. In diesem eBook erfahren Sie alles über die neuesten Trends in der Compiler-Szene und wie Sie diese für sich nutzen können.

Folgende drei Kapitel umfasst das eBook:

  • Aktuelle Trends in der Compiler-Szene
  • Compiler-Infrastrukturen und ihre Geheimnisse
  • Übersicht: Platzhirsche und Herausforderer

(ID:49969818)