Multicore

Effiziente Embedded-Multicore-Programmierung

Seite: 2/3

Firma zum Thema

ALMA Werkzeugkette & Workflow

Scilab/MATLAB sind Skriptsprachen, das bedeutet, dass die Befehle nacheinander von einer Laufzeitumgebung interpretiert und ausgeführt werden. Dies ermöglicht es, dass Eigenschaften wie beispielsweise die Größe oder der Typ von Variablen erst zur Laufzeit festgelegt werden. Eine Umsetzung dieses Verhaltens in C ist zwar möglich, aber Aufgrund der Performanz und des Speicherverbrauchs nicht sinnvoll. Aus dem allgemeinen MATLAB-Code wird als erstes C-Code erzeugt, der sich gut für die statische Analyse und die spätere Parallelisierung eignet. Dazu werden alle dynamischen Entscheidungen aufgelöst, so dass der statische Programmablauf besser analysiert werden kann. Im Falle von Variablen wird zunächst über die gesamte Laufzeit des Programms betrachtet, welcher Datentyp nötig ist, um alle Zahlen ohne Einschränkungen darstellen zu können. Im C-Code wird dieser Datentyp verwendet, um sowohl die dynamischen Entscheidungen zu reduzieren als auch den Speicherverbrauch zu minimieren. Ein weiterer Vorteil von MATLAB hinsichtlich der Parallelisierung ist das Fehlen von Pointern. Dies ermöglicht es, den Datenfluss in einem Programm eindeutig bestimmen zu können, was wichtig für den Datentransfer zwischen den verschiedenen Kernen ist.

Die abstrakte Hardwarebeschreibung setzt auf eine im Projekt entwickelte Architektur-Beschreibungssprache (engl. architecture desciption language, ADL) [4]. Als Besonderheit wird eine Beschreibung auf verschiedenen Abstraktionsebenen unterstützt. So können Hardware-Module sowohl rein funktional als auch detailliert mit Angabe von zyklen-akkuraten Instruktionen dargestellt werden. Auf diese Weise können sowohl Informationen, die zur Simulation der Hardware notwendig sind, als auch abstraktere Informationen, die für die Parallelisierungsentscheidung benötigt werden, dargestellt werden.

Bildergalerie
Bildergalerie mit 5 Bildern

Um eine gute Performanz einer parallelisierten Anwendung zu erreichen, muss die Parallelisierung auf zwei unterschiedlichen Ebenen ansetzen: auf einer feinen und einer groben. Dabei zielt die feine auf eine Optimierung der Ausführung auf einem Kern ab und die grobe optimiert die gleichzeitige Ausführung auf mehreren Kernen. Durch diese Kombination kann die gegebene Hardware möglichst effizient ausgenutzt werden.

Die feingranulare Parallelisierungsextraktion (engl. fine-grained parallelism extraction) analysiert zunächst die benötigten Datentypen hinsichtlich eines Kompromisses aus effizienter Ausnutzung der Hardware und der benötigten Genauigkeit der Ergebnisse. Dies erlaubt es, die SIMD (engl. single instruction, multiple data) Einheiten der Architektur auszunutzen, indem beispielsweise vier 8-Bit-Additionen gleichzeitig ausgeführt werden anstelle einer 32-Bit-Addition. Wie in [5] dargestellt, wird außerdem ermittelt, ob Zahlen in einer Festkommadarstellung repräsentiert werden können. Der Einsatz kann die Performanz bei der Ausführung erhöhen, hat aber Einfluss auf die Genauigkeit. Des Weiteren werden Schleifen transformiert, um Zugriffe auf Daten besser an die vorhandenen Caches anzupassen.

Die grobgranulare Parallelisierung, wie sie in [6] vorgestellt wurde, verteilt die Anwendung auf die einzelnen Kerne der Zielplattform. Ziel dieser Optimierung ist es, die Ausführungszeit des gesamten Programms zu reduzieren, indem möglichst viele parallele Recheneinheiten der Architektur gleichzeitig verwendet werden. Für die Parallelisierung wird auf eine hierarchische Task-Darstellung des Programms zurückgegriffen. Jedes Kontrollflusskonstrukt wie Schleife oder Bedingung innerhalb des Programmablaufs sorgt für das Hinzufügen einer neuen Ebene in der Hierarchie. Ein Beispiel ist in Bild 2 der Bildergalerie dargestellt. Die Darstellung erlaubt es, die Parallelisierung sowohl von oben herab als auch von unten herauf durchzuführen.

Auf diese Weise kann eine optimale Verteilung der Aufgaben auf die einzelnen Kerne für jede Ebene ermittelt werden und anschließend der optimale Gesamtablauf bestimmt werden.

Die parallele Codegenerierung [6] erzeugt schließlich parallelen C-Code, der auf der Hardware oder den zugehörigen Simulatoren ausgeführt werden kann. Dazu erstellt die Codegenerierung separaten Quellcode für die einzelnen Kerne der Hardware-Plattform, indem Datenabhängigkeiten zwischen den einzelnen Prozessoren über Kommunikationsinstruktionen aufgelöst werden. Die Kommunikationssynthese achtet dabei auf eine Reduzierung der anfallenden Wartezeiten und ist auf Systeme mit verteiltem Speicher optimiert, aber nicht auf sie beschränkt. Für die Kommunikation können sowohl verbreitete Modelle wie MPI (message passing interface) als auch spezielle Funktionen der jeweiligen Plattformen ausgenutzt werden.

Der erzeugte Code kann automatisch instrumentiert werden, um Laufzeitinformationen mit Hilfe eines Simulators zu ermitteln. Diese Informationen können verwendet werden, um die Parallelisierung und damit auch die Performanz iterativ zu verbessern. [7] Dazu fließen die Laufzeiten der einzelnen Codeteile und der Übertragung von Daten zurück in die grobgranulare Parallelisierung und ermöglichen eine Aufteilung, die besser an die tatsächliche Hardware angepasst ist.

(ID:44287984)