OpenMP auf Embedded-Systemen mit mehreren Kernen Spezifikation paralleler Programme

OpenMP auf Embedded-Systemen mit mehreren Kernen
Spezifikation paralleler Programme

Mehrkernprozessoren verändern die Anwendungsentwicklung in allen Bereichen der Datenverarbeitung. Da Mehrkernprozessoren höhere Leistung bei geringerem Stromverbrauch bieten, werden sie in mehr Embedded-Anwendungen als je zuvor eingesetzt und ermöglichen sogar völlig neuartige Anwendungen.
Die Software für Embedded-Systeme wird immer komplexer, da mit Mehrkern-Hardware eine größere Zahl an Funktionen auf demselben System implementiert werden können. Diese Komplexität führt dazu, dass die Erstellung der Software zum kritischen Pfad in der Entwicklung von Embedded-Systemen wird. Ein abstraktes Programmiermodell wie OpenMP hat das Potenzial, die Produktivität der Programmierer beträchtlich zu steigern, was im Gegenzug die Entwurfs- und Entwicklungskosten von Embedded-Systemen reduziert und eine frühere Marktreife ermöglicht. In diesem Artikel wird der erste Arbeitsschritt erläutert, der zur Spezifikation paralleler Programme für einen Embedded-Mehrkernprozessor erforderlich ist: die Anpassung einer vorhandenen Shared-Memory-Programmierschnittstelle, in diesem Fall OpenMP.

OpenMP-Standard

OpenMP ist ein verbreiteter Standard für parallele Shared-Memory-Programmierung in C, C++ und Fortran. Er stellt portierbare abstrakte Programmierkonstrukte zur Verfügung, mittels derer Anwender einfach und auf inkrementelle Weise die Parallelität eines Programms auf Aufgaben- und Schleifenebene darstellen können. Mit OpenMP kann der Entwickler die Parallelisierungsstrategie für ein Programm auf übergeordneter Ebene angeben, indem er den Programmcode um Compilerdirektiven erweitert, die festlegen, auf welche Weise ein Codebereich durch eine Gruppe von Threads ausgeführt werden soll. Anschließend ermittelt der Compiler die genaue Aufteilung der Berechnungen auf das System. Wie aus Abbildung1 ersichtlich ist, handelt es sich bei OpenMP um eine Thread-basierte Programmiersprache. Der Master-Thread führt die sequenziellen Bestandteile eines Programms aus. Wenn der Master-Thread einen parallelen Codebereich erreicht, erzeugt er eine Gruppe von Arbeits-Threads, welche diesen Codebereich zusammen mit dem Master-Thread parallel abarbeiten. Mithilfe der OpenMP-Programmierschnittstelle kann der Programmierer folgende Vorgänge ausführen:

Threads erstellen und verwalten

Arbeitslasten (Aufgaben) auf Threads verteilen bzw. diesen zuweisen

Festlegen, welche Daten von allen Threads gemeinsam genutzt werden und welche Daten privat

sind

Koordination der Thread-Zugriffe auf gemeinsam genutzte Daten

Es gibt eine relativ einfache Möglichkeit zur Migration der vorhandenen Codebasis – C/C++-Direktiven (#Pragma), mit denen die Parallelität ausgedrückt wird. Wie in Abbildung 2 dargestellt, geben die OpenMP-Direktiven an, dass ein wohlstrukturierter Codebereich durch eine Gruppe von Threads ausgeführt werden soll, die sich die Arbeit teilen. Um die Aufteilung der Arbeit zwischen den beteiligten Threads zu erreichen, stellt OpenMP Direktiven für die Arbeitsteilung zur Verfügung. Der Programmierer fügt einer vorhandenen, sequenziellen Anwendung inkrementell OpenMP-Pragmas hinzu und kann den Code dadurch schnell auf eine Mehrkernplattform portieren.

Direktiven für die Arbeitsteilung

Die OpenMP-Spezifikation wird vom OpenMP ARB (Architecture Review Board) festgelegt, das mit Experten aus Regierungsorganisationen, der Wissenschaft und der Industrie besetzt ist. OpenMP blickt auf eine lange Tradition in der Hochleistungsdatenverarbeitung zurück und wird auf allen wichtigen Plattformen und ISA-Plattformen einschließlich GCC unterstützt. Der aktuelle Standard OpenMP 3.0 erweitert das OpenMP-Programmiermodell um integrierte Tasks. Mit dem neuen Taskmodell können explizite Tasks und Konstrukte für die Arbeitsteilung erstellt werden, die implizite Tasks erzeugen. OpenMP-Tasks können an Scheduling Points (Zeitplanungspunkten) unterbrochen werden. Die gegenwärtige Weiterentwicklung der Sprache verfolgt das Ziel, Beschleunigungssysteme und heterogene Systeme zu unterstützen.

OpenMP-Implementierung

Compiler übersetzen OpenMP in Multi-Thread-Code, der Funktionsaufrufe einer benutzerdefinierten Laufzeitbibliothek enthält. Die Effizienz der Laufzeitbibliothek ist dabei von entscheidender Bedeutung, da sie die Verwaltung und Planung von Threads, die Shared-Memory-Nutzung und die detailgenaue Synchronisierung der Ausführung unterstützen muss. OpenMP unterstützt gemeinsam genutzte und private Variablen. Jeder Thread verfügt über eigene Kopien privater Variablen, auf die andere Threads nicht zugreifen können. Es gibt jedoch nur eine Kopie einer gemeinsam genutzten Variablen, auf die alle Threads zugreifen können. OpenMP definiert ein lockeres Konsistenzmodell für den gemeinsam genutzten Speicher. Parallel ausgeführte Threads verfügen über eine temporäre Ansicht des gemeinsam genutzten Speichers bis in der Ausführungsreihenfolge ein Punkt erreicht wird, an dem der Speicher synchronisiert oder geleert wird. Beim Leeren des Speichers müssen die Threads ihre temporäre Speicheransicht zurückschreiben und anschließend diese Ansicht verwerfen. Nachdem der Speicher synchronisiert wurde, erhalten die Threads erneut eine temporäre Ansicht des Speichers.

Speichermodell

Obwohl eine Vielzahl von Embedded-Mehrkernprozessoren über einen gemeinsam genutzten Speicher verfügt, wird die Konsistenz dieses Speichers nicht automatisch durch die Hardware sichergestellt. In diesem Fall ist die OpenMP-Laufzeitbibliothek dafür verantwortlich, die entsprechenden Cache-Verwaltungsvorgänge durchzuführen, um die Konsistenz des gemeinsam genutzten Speichers zu gewährleisten, wenn dies erforderlich ist. Für gemeinsam genutzte Variablen wurde eine per Software verwaltete Cache-Kohärenz implementiert. Wenn ein Thread eine gemeinsam genutzte Variable aktualisiert, wird der aktualisierte Wert zunächst im L1-Cache gespeichert. Beim Leeren des Speichers muss die Laufzeitbibliothek dafür sorgen, dass die Cache-Verwaltungsvorgänge ausgeführt werden, die den lokalen L1-Cache mit dem gemeinsam genutzten Speicher synchronisieren. Das Programmabbild wird in den gemeinsam genutzten Speicher geladen und alle Kerne können mithilfe ihres Programmcaches darauf zugreifen.

Ausführungsmodell für parallele Bereiche

Bei allen parallelen Bereichen teilt der OpenMP-Compiler die Arbeitslast in mehrere Mikrotasks auf, die zur Laufzeit den Arbeits-Threads zugewiesen werden. Der Mechanismus für das Aufteilen und Zusammenführen eines parallelen Bereichs (Fork-Join-Mechanismus) besteht aus folgenden Schritten:

Nach der Initialisierung warten die Arbeits-Threads auf die Benachrichtigung zur Ausführung einer

Mikrotask.

Der Master-Thread weist den Arbeits-Threads Mikrotasks zu, indem er ihnen über eine

Nachrichtenwarteschlange eine Nachricht sendet, die einen Funktionszeiger und einen

Datenzeiger enthält.

Nachdem er die Nachricht erhalten hat, führt jeder Arbeits-Thread die durch den Funktionszeiger

angegebene Mikrotask aus. Der Datenzeiger wird als Argument an die Mikrotask übergeben.

Nach Abschluss der Mikrotask sendet der Arbeits-Thread eine Beendigungsnachricht an die

Nachrichtenwarteschlange des Master-Threads.

Nachdem der Master-Thread seine eigene Mikrotask abgeschlossen hat, wartet er, bis er die

Beendigungsnachrichten aller Arbeits-Threads empfangen hat.

OpenMP implementiert ein Thread-basiertes, Shared-Memory-Programmiermodell für die parallele Programmierung, das Parallelität sowohl auf Datenebene als auch auf Aufgabenebene unterstützt. Frühe Erfahrungen haben gezeigt, dass OpenMP ein geeignetes und produktives Programmiermodell für Embedded-Systeme darstellt. Eine der Herausforderungen bei Embedded-Prozessoren besteht darin, dass ihnen einige der in Universalprozessoren (General-Purpose-Processor) üblicherweise vorhandenen Funktionen fehlen, zum Beispiel Speichermanagementeinheiten (MMU), kohärente Cachesysteme und eine einheitliche Speicherzugriffszeit. Texas Instruments (TI) hat sich vorgenommen, diese Probleme im Rahmen der Erweiterung des C6X-Compilers von TI, bei der der Compiler OpenMP-Unterstützung erhalten soll, zu lösen.

Autor: Dr. Eric Stotzer, PhD, Senior Member Technical Staff Texas Instruments (leitendes Mitglied des Compiler-Teams der Abteilung Software Development von TI)

Texas Instruments Deutschland GmbH
www.ti.com

Das könnte Sie auch Interessieren

Bild: PiBond Oy
Bild: PiBond Oy
PSI Institut und PiBond kooperieren

PSI Institut und PiBond kooperieren

PiBond, Hersteller von Materialien für die Halbleiterindustrie, hat mit dem Paul Scherrer Institut PSI, Forschungsinstitut für Natur- und Ingenieurwissenschaften in der Schweiz, eine Vereinbarung über Technologielizenzen und strategische Zusammenarbeit unterzeichnet, um die Entwicklung von lithografischen Werkstoffen der nächsten Generation sowie zukünftige Halbleiterinnovationen voranzutreiben.