Hybrides Scheduling Ronald Moore, Melanie Klang, Bernd Klauer, und Klaus Waldschmidt, Technische Informatik, J. W. Goethe-University, Frankfurt, Germany Veröffentlicht in: ARCS ’99: Architektur von Rechensystemen 1999; Vorträge der Workshops im Rahmen der 15. GI/ITG Fachtugung, S. 211–218, Okt. 1999, Universität Jena, Institut für Informatik. Kurzfassung In dieser Arbeit wird ein hybrider Ansatz zur automatischen Parallelisierung von Computerprogrammen vorgestellt. Das Merkmal hybrid“ bezieht sich dabei auf das Scheduling, das teilweise zur Compilezeit und ” teilweise zur Laufzeit durchgef ührt wird. Die Verlagerung des Schedulings in das eine oder andere Zeitfenster wird über die Berechenbarkeit der Ausführungszeitpunkte getroffen. Das Scheduling z.B. von Iterationen mit a priori unbekannter Wiederholungszahl wird zur Laufzeit berechnet. Die Ausführungsreihenfolge von Operationen in zyklusfreien Programmbereichen wird demgegenüber bereits zur Compilezeit berechnet. Aus diesem Kriterium ergibt sich die Granularität der Module in den einzelnen Zeitfenstern: feingranulare Strukturen werden zur Compilezeit und grobgranulare zur Laufzeit in den Ablaufzeitplan eingetragen. Aufgrund der hohen Anzahl feingranularer Strukturen kann ein großer Teil des Aufwandes für das Scheduling von der Laufzeit in die Compilezeit verlagert werden. 1 Einleitung 1.1 Motivation und Überblick Die parallele Programmierung sowie allgemein die Parallelisierung von Algorithmen werden als schwierige Probleme angesehen (z.B.[13]). Unter dieser Tatsache leidet die Akzeptanz von Parallelrechnern bei Programmierern, Anwendern und dadurch auch bei der marktorientierten Rechnerindustrie. Das Parallelisierungsproblem besteht in der (korrekten) Verteilung von Daten und Programmteilen auf die verfügbaren Ressourcen. Dieser Beitrag stellt einen hybriden Ansatz zur automatischen Parallelisierung von Computerprogrammen vor, der das statische Generieren von Threads mit dem dynamischen Scheduling verbindet. Feingranulare Scheduling-Entscheidungen bei der Generierung der Threads werden zur Compilezeit und grobgranulare (Scheduling der Threads) zur Laufzeit getroffen. Der Ansatz besteht aus zwei Komponenten: Erstens aus der zur statischen Analyse (Threadgenerierung) notwendigen Compilertechnologie und zweitens aus einer Architektur, die das Scheduling und die Verteilung von Threads unterstützt. Das Architekturkonzept ist hierbei selbst verteilt: Jedem Prozessor ist ein Broker zugeteilt, der gemeinsam mit Diese Arbeit wurde von der Deutschen Forschungsgemeinschaft unter der Referenznummer WA 357/15-1 unterstützt. anderen Brokern die Verteilung der Threads erledigt. Siehe Bild 1. Hier werden 3 Strategien unterschieden: Die hier als attraktive Strategie bezeichnete Vorgehensweise versucht Threads von anderen Ressourcen abzuwerben“. Die dissipative Strategie versucht ” Threads auf andere Ressourcen zu verdrängen. Eine kooperative Strategie versucht gemeinsam mit anderen Brokern eine günstige Verteilung zu ermitteln. Die hier verwendete Strategie stellt eine Mischform dar, bei der die Broker grundsätzlich kooperativ arbeiten, sich aber mit zunehmender Belastung der von ihnen verwalteten Ressource dissipativ verhalten. Genauso agieren die Broker bei Entlastung zunehmend mit einer attraktiven Strategie. Zusammen verwalten diese Broker einen verteilten gemeinsamen Speicher DSM (Distributed Shared Memory), und einen verteilten gemeinsamen Scheduler. Jede Kombination eines Prozessors, eines Brokers und eines lokalen Speichers wird im folgenden Attraction Memory Site (kurz: Site) benannt, wie in einer Cache Only Memory Architecture (COMA), vergleiche [5]. Die in diesem Beitrag beschriebene Rechnerarchitektur SDAARC (Self Distributing Associative ARChitecture) stellt Strukturen bereit, die die Laufzeitkomponente des Schedulers unterstützen. Zusätzlich wurde auf die Skalierbarkeit der Architektur geachtet. Das wesentliche Ziel dieser Arbeit ist jedoch die Vereinfachung der Programmierung. Das hier dargestellte Konzept übernimmt die komplette Aufgabe der Verteilung von Daten und Berechnung und hat als solches das Potential das Programmieren paralleler Systeme nahezu so einfach wie das sequentieller Systeme zu gestalten. Ein Toolkit, das diese Zerlegung und Verteilung erledigt befindet sich in der Entwicklung. 1.2 Hintergrund Der Ansatz dieses Beitrags bringt zwei Konzepte bisher unabhängiger Bereiche zusammen: Multithrea” ded Architectures“ (Mehrfädige Architekturen) auf der einen und Cache Only Memory Architectures“ ” (COMAs) auf der anderen Seite. Die Konzepte der mehrfädigen Architekturen finden Verwendung, indem sie die grundlegenden Scheduling-Begriffe (siehe [2] oder [12]) definieren. COMAs stellen die Konzepte bereit, um einen DSM zu realisieren, so daß Daten frei von einem lokalen Speicher zu einem anderen wandern können [5, 14, 15]. Bisher waren die Fragen bezüglich Verteilung von Daten und Verteilung von Berechnungen strikt voneinander getrennt. Die mehrfädigen Architekturen sagten wenig über die Datenverteilung aus und COMAs ließen das Problem der Programmaufteilung sowie des Schedulings beim Programmierer. Diese Aufteilung vereinfachte die Entwicklung beider Seiten. Nun ist jedoch ein Punkt erreicht, indem durch das Aufheben der Grenze und der Optimierung beider Komponenten (Scheduling und DSM) signifikante Fortschritte erzielt werden können. Diese Kombination von COMA und Multithreading nennen wir SDAARC, nach Self Distributing Associative ARChitecture [9, 10]. Adaptive Verteilung von Berechnung wird auch in verschiedenen Forschungprojekten untersucht, die migrierende Threads implementieren, z.B. [4] oder [16]. Hier erfolgt die Datenverteilung — wenn überhaupt — über völlig andere Mechanismen als die Verteilung von Berechnungen. Weiter ist die Definition eines Threads wesentlich komplizierter. Dadurch entstehen Probleme, z.B. die Verteilung des Stacks, die in SDAARC nicht vorkommen. Der Rest dieses Beitrags ist folgendermaßen gegliedert: Abschnitt 2 gibt einen Überblick über die umfassende Strategie und die Hard- und Software (Compiler) Voraussetzungen von SDAARC. Abschnitt 3 stellt die Compilertechnologie und Abschnitt 4 das Protokoll der Laufzeitverteilung ausführlicher vor. Abschnitt 5 gibt eine Zusammenfassung. 2 Überblick 2.1 Strategie, Vorgehensweise In SDAARC werden sowohl Daten als auch Berechnungen als Bevölkerung wandernder Objekte betrachtet. Die zentrale Idee ist zum einen Objekte einer angemessenen Granularität zu bestimmen, zum anderen diese zur Laufzeit, entsprechend den aktuellen Gegebenheiten, zu verteilen. Dabei muß die Verteilung die Balance finden zwischen Lokalität (um Kommunikationskosten zu reduzieren) und Parallelität (um die Gesamtlaufzeit zu reduzieren). Aus dieser Vorgehensweise entstehen zwei Bereiche zur Lösung des Problems der Verteilung: Erstens müssen Objekte angemessener Größe zur Compilezeit aus dem Quellcode eines sequentiellen Programms extrahiert werden. Zweitens müssen diese Objekte zur Laufzeit auf die zur Verfügung stehenden Attraction Memory Sites abgebildet werden. Als solches besteht SDAARC aus zwei Hauptgebieten: der Compilertechnologie zum Extrahieren der Objekte und der Laufzeittechnologie für deren Verteilung. 2.2 Hardware Anforderungen SDAARC stellt nur geringe Anforderungen an die Hardware, um das Konzept zu implementieren. Wie schon angesprochen, ist die Hardware in eine Menge von Attraction Memory Sites zu organisieren, wobei jede Site aus einem oder mehreren Prozessoren, einem Broker, einem Speicher und einem Netzwerk besteht. Zusätzlich könnten mehrere Sites gruppiert, und durch einen zusätzlichen Gruppen-Broker repräsentiert werden, um hierarchische Systeme zu bilden. Die Implementierung der Broker in Hardware, Software oder Mischformen ist freigestellt. Auch für das Netzwerk sind unterschiedliche Topologien möglich, wie beispielsweise Baumstrukturen oder Kreuzschienenverteiler [10]. Die Broker und das Netzwerk sind zusammen maßgeblich für die Umsetzung der SDAARC Strategie verantwortlich. Da das Programm mit den zugehörigen Daten als Bevölkerung wandernder Objekte verstanden wird, werden fast alle Nachrichten, die über das Netzwerk ausgetauscht werden, an Objekte und nicht an Speicherzellen adressiert. Jede Nachricht muß daher an alle Broker versendet werden, deren Sites den Empfänger momentan enthalten könnten. Attraction Memory Site A Processor A Attraction Memory Site B Memory A Environment Processor B Broker A NETWORK Broker B Memory B Bild 1. Abstraktes Schemata für ein Architekturkonzept mit verteiltem gemeinsamen Speicher (DSM: Distributed Shared Memory) und verteiltem gemeinsamen Scheduler Compiler Front Ends C Front End Compiler Back End Thread Partition Neural Network Specifications Dataflow Graphs Emulator Code Generator Hardware Other Languages Bild 2. Compiler Technologie zur Repräsentation von Programmen als Threads 2.3 Compiler Anforderungen SDAARC setzt voraus, daß jedes Programm durch eine Menge von Microthreads repräsentiert wird. An dieser Stelle soll zuerst die Bedeutung des Begriffs Thread für diesen Beitrag genauer definiert — da dieser unglücklicherweise mit verschiedenen, oft widersprüchlichen Bedeutungen in der Literatur verwendet wird. Die Terminologie in diesem Beitrag entspricht der von [12]: Ein Microthread sei eine atomare Untermenge eines Datenflußgraphen, die niemals auf Daten oder Ereignisse wartet. Muß ein Prozeß z.B. auf ein Eingabedatum warten, wird dieser in mindestens zwei Microthreads geteilt. Der erste resultierende Microthread führt die Berechnung bis zu dem Punkt durch, an dem die Eingabe notwendig wird, der andere startet nach Ankunft des Datums. Microthreads werden vom Compiler, und nicht vom Programmierer, festgelegt. Die Compilertechnologie um sequentielle Programme in Threads zu übersetzen ist in Bild 2 dargestellt. Diese kann in Frontund Back-End aufgeteilt werden. Das Front-End ist für die Übersetzung eines sequentiellen Programms in einen Datenflußgraphen verantwortlich. Die Wahl der Quellensprache ist dem Programmierer überlassen, und kann entsprechend den benötigten Eigenschaften zur Problemlösung gewählt werden. FrontEnds für eine Untermenge von ANSI C und eine Sprache zur deklarativen Spezifikation Neuronaler Netzwerke sind in [11] und [8] beschrieben. Datenflußgraphen wurden aus zwei Gründen als Zwischenformat gewählt: erstens, um Unabhängigkeit zur Quellensprache zu erreichen, und zweitens, um den im sequentiellen Programm implizit enthaltenen Parallelismus sichtbar und greifbar zu machen. Das Back-End besteht aus zwei Schritten: Zunächst wird der Datenflußgraph in Microthreads aufgeteilt und danach für jeden dieser Threads separat Code generiert. Während letzteres weitestgehend mit konventionellen Techniken erfolgt, wird die Graphpartitionierung in Abschnitt 3 näher vorgestellt. 3 Die Compiler Technologie 3.1 Algorithmus zur Graphpartitionierung Die Autoren haben einen ersten Entwurf zur Partitionierung von Graphen realisiert, um das SDAARC Konzept empirisch bewerten zu können. Dieses Werkzeug akzeptiert Datenflußgraphen der in [11] und [8] beschriebenen Front-Ends und weist jedem Knoten einen Index zu, der die Position des Knotens in einem hierarchisch organisierten Baum von Untergraphen angibt. Der Algorithmus verfährt in zwei Schritten: Im ersten Schritt werden verschachtelte Kontrollstrukturen identifiziert und diesen Macrothreads“ zugewiesen. ” Das Resultat dieses Schritts ist ein hierarchischer Graphpartitionierungsbaum, dessen innere Knoten Macrothreads repräsentieren und dessen Blätter nun gerichtete, azyklische Datenflußgraphen enthalten. Im zweiten Schritt wird jeder gerichtete, azyklische Untergraph weiter in Microthreads zerteilt. Dies ist momentan durch einen greedy, bottom-up Packalgorithmus realisiert, der heuristisch versucht eine Kostenfunktion zu minimieren. Zu Beginn des zweiten Schritts wird jedem Knoten ein individueller Microthread zugewiesen (jeder Microthread besitzt also zu Beginn genau einen Knoten). Sodann werden Paare von Microthreads ausgewählt und zusammengefügt, dann und nur dann, wenn der Zusammenschluß die Kostenfunktion nicht verschlechtert und dadurch kein Zykel innerhalb des Macrothreads entsteht. Für die Kostenfunktion wird jedem Knoten ein statischer Kostenfaktor zugewiesen, der dessen Berechnungszeit repräsentiert. Analog erhält jeder Kante zwischen zwei Microthreads einen statischen Kostenfaktor entsprechend der Durchschnittszeit bezüglich Kommunikation und Kontextwechsel. Die Kosten des gesamten Untergraphen entsprechen somit den Kosten entlang des kritischen Pfades. Formaler ausgedrückt: Sei ein azyklische (Teil-) Datenflußgraph vor der Partitionierung, die Menge der Knoten und wobei die Menge der Kanten ist. Sei weiter eine Partitionierung von . Hier stellt ein Microthread eine Menge feingranulare Operationen dar: . In ähnliche Weise stellt eine Kante in der Partitionierung eine Menge feingranularer Kanten dar. Alle feingranularen Kanten in einer grobgranularen Kante gehen vom selben Microthread zum selben Microthread: !"#"%& $'."(-/")*$0+1-%,( 1-2 33-5-746481- -:9;3-=<> ? -:4@- "#"%&A$'"")*$ +B4 " C-"$ D- <E - 9F&()4G C- '+B4H DI J - ; ILK MK NPOQ I JWP OXILYJZ#[#Z#K \]RZ_S^3TVU` W"A"2- $ -ba W - 4G - ` OXILc Z#Md ^` "%"e- $ W - a W - 4f - ` I & A & I K MK N I YJZ#[#Z#K \]Z ILc Z#Md Zuerst definieren wir die Kosten eines Microthreads: wobei die Kosten für einen feingranularen Knoten sind; die Zeit darstellt, die für einen Thread-Wechsel notwendig ist; die Zeit darstellt, die notwendig ist, um einen Nachricht zu empfangen; und die Zeit darstellt, die notwendig ist, um eine Nachricht zum Netzwerk zu schicken. Anders gesagt sind die Kosten eines Microthreads gleich der Summe der Kosten, um alle Operanden zu empfangen, plus die Summe der Kosten für alle Rechenoperationen im Microthread, plus die Summe der Kosten, um alle Ergebnisse in das Netzwerk zu übertragen. 1[#- YJK N <!- 1 - <1#[ YJK N Nun können wir die Kosten des kritischen Pfades, und damit auch die Kosten des Graphen definieren. die Menge alle Microthreads am kriSei tischen Pfad, und die Menge alle grobgranularen Kanten am kritischen Pfad. Dann beträgt die minimale Rechenzeit für den Graphen I A1g I % [#YJK N IL[#hiji@^` [#- YJK N `O Q I A - T U lS k 2m U npo q IL[#hiji wobei die Zeit ist, die eine Nachricht im Netzwerk verbringt. Die Modellierung von Kommunikationskosten ist der LogP Methode ähnlich, vergleiche [3]. Der Algorithmus kann mit verschiedenen Werten für und auf gegebene Hardware angepaßt werden. Die Partitionierung bleibt jedoch für beliebige Werte korrekt. Empirische Ergebnisse mit verschiedenen Werten werden im nächsten Abschnitt (Abschnitt 3.2) dargestellt. Die Berechnung des kritischen Pfades wird hier drei Ansprüchen gerecht: Erstens werden über diesen die Knoten ausgewählt, deren Zusammenschluß diskutiert wird. Zweitens kann die Kostenabschätzung einfach über eine neue Berechnung aktualisiert werden. Zuletzt erkennt die Berechnung des kritischen Pfades, ob ein Zusammenschluß zweier Microthreads einen Zyklus erzeugt. Das Werkzeug zur Partitionierung ist ausführlicher in [7] beschrieben. I A3r I K MK N I YJZ#[#Z#K \Z ILc Z#Md I [#hiji 3.2 Erste Resultate Gezeigt wird das Resultat des zweiten Schritts für zwei Beispiele: zum einen für einen regelmäßigen Graphen (Matritzenmultiplikation) zum anderen für einen unregelmäßigen Graphen (zufällig generierter Graph mit 160 Knoten). Desweiteren werden zu jedem Beispiel zwei mögliche Partitionierungen dargestellt: eine unter der Annahme von extrem optimistischen (niedrigen) und eine mit realistischeren (höheren) Kommunikationskosten. Der regelmäßige Graph repräsentiert die Berechnung einer 3x3 Matritzenmultiplikation. Dieses Beispiel wurde nicht als absolut typisches Beispiel gewählt, sondern vielmehr weil die Struktur bekannt und der 18 69 65 67 12 51 15 47 10 49 8 75 9 71 7 73 29 31 1 33 59 5 61 6 63 19 13 57 16 53 11 55 21 41 2 20 43 14 3 45 17 35 4 37 39 23 25 66 48 72 30 60 54 42 36 24 68 50 74 32 62 56 44 38 26 70 52 76 34 64 58 46 40 28 83 80 84 77 82 81 79 78 22 27 Bild 3. Partitionierung einer 3x3 Matritzenmultiplikation unter der Annahme optimistischer Kommunikationskosten ergibt 36 Microthreads 7 6 5 53 19 16 13 47 12 2 15 35 3 18 4 1 11 29 41 14 17 8 23 21 71 9 20 10 65 59 55 54 49 48 37 36 31 30 43 42 25 24 73 72 67 66 60 61 57 56 51 50 39 38 32 33 45 44 27 26 75 74 69 68 63 62 58 52 40 34 46 28 76 70 64 81 80 78 77 79 22 84 83 82 Bild 4. Partitionierung einer 3x3 Matritzenmultiplikation unter der Annahme realistischerer Kommunikationskosten ergibt 9 Microthreads 5 22 17 72 34 30 63 32 73 99 86 49 82 150 130 119 1 158 48 42 101 53 75 140 124 104 112 131 125 31 28 71 46 109 129 143 16 87 19 21 111 44 68 133 78 77 51 80 94 70 91 105 4 113 93 36 126 148 6 26 38 52 56 41 69 92 64 76 145 12 90 128 116 88 154 134 149 9 7 18 25 24 110 153 (a) 160 159 13 50 33 81 120 11 35 37 123 144 58 146 114 40 74 106 27 100 139 59 122 118 98 54 136 107 65 39 83 14 10 8 2 102 147 29 61 55 47 108 95 79 142 97 141 127 138 121 15 23 3 18 66 62 57 132 20 115 84 43 85 19 114 55 45 142 118 96 151 111 95 96 124 132 156 135 89 157 137 67 152 103 60 117 155 156 139 144 14 155 30 54 32 31 62 71 112 133 135 21 80 116 126 65 131 149 84 134 41 127 100 88 98 44 69 81 122 146 143 113 83 136 90 154 9 13 66 64 57 109 20 38 52 104 129 7 29 34 70 63 105 73 99 12 102 61 79 108 93 141 147 148 130 89 36 115 78 22 24 107 151 91 37 123 121 87 86 1 16 72 157 138 67 77 35 94 106 153 159 125 33 74 51 39 49 11 28 40 110 97 117 5 27 45 47 17 82 119 158 25 68 46 48 92 101 128 140 8 150 120 23 50 10 4 85 58 42 76 2 6 15 43 26 3 56 59 53 75 160 137 152 145 103 60 (b) Bild 5. Partitionierung eines zufällig generierten Graphen mit 160 Knoten ergibt: (a) 52 Microthreads bei optimistischen Kommunikationskosten; (b) 39 Microthreads bei realistischeren Kommunikationskosten Kompromiß zwischen Parallelismus und Lokalität sehr gut zu sehen ist. Bild 3 partitioniert den Graphen unter der Annahme optimistischer Kantengewichte. Hier entschied der Partitionierungsalgorithmus das Maximum an Parallelität beizubehalten, indem alle Multiplikationen parallel ausgeführt und die abhängigen Additionen in 9 Microthreads zusammengefügt werden. Im Gegensatz dazu ist in Bild 4 zwar der gleiche Graph, aber realistischere Kommunikationskosten angenommen. Diesmal hat der Algorithmus etwas Parallelität zu Gunsten der Lokalität geopfert. Hier wird jedes der 9 Resultate der Matritzenmultiplikation in einem eigenen Microthread berechnet. Die Bilder 3 und 4 zeigen also die Fähigkeit des Algorithmus die Struktur eines regelmäßigen Datenflußgraphen zu erforschen und nutzbar zu machen. Um zu sehen wie der Algorithmus mit unregelmäßigen Datenflußgraphen umgeht, wurde der Partitionierungsalgorithmus mit zufällig erzeugten Datenflußgraphen getestet. Die Bilder 5 (a) und (b) zeigen die Partitionierung eines zufällig erzeugten Graphen mit 160 Knoten. Abbildung 5 (a) geht wiederum von optimistischen Kommunikationskosten aus, und der Algorithmus produziert 52 Microthreads. Bei realistischen Kommunikationskosten (Abbildung 5 (b)) werden 39 Microthreads erzeugt. Die Kommunikationskosten müssen nicht genau abgeschätzt werden. In Bilder 3 und 5 (a) war ILK MK N sI YJZ#[#Z#K \Z I c Z#Md Is[#h(iji I A3 tuL4f d.h. die Kosten für eine Nachricht waren den Kosten für eine Rechenoperation gleichgesetzt. Das Partitionierung-Programm erzeugte dieselbe Graphzerlegung wie in Bilder 4 und 5 (b) für alle Werte im Bereich vw vww I Jxzy I K MK N I [#hiji |{ I A3 4 Dynamische Verteilung 4.1 Anpassung von COMA für SDAARC Wie in Abschnitt 1 und 2 angesprochen, basiert SDAARC auf der COMA Technologie. COMAs (z.B. [5] oder [14]) implementieren einen transparent verteilten, gemeinsamen Adreßraum für parallele Prozessoren. Datenblöcke bewegen sich von lokalen Speichern zu lokalen Speichern als Antwort auf Lese- und Schreiboperationen. Wie bei allen Cache-Technologien, repräsentiert diese Strategie ein Glücksspiel um Lokalität: Solange die an- genommene Lokalität besteht, stehen die Gewinnchancen gut, daß das Datum welches von einer Site benötigt wird, dort in naher Zukunft wieder benötigt wird. Ist die Annahme bezüglich der Lokalität falsch, kann es passieren, daß Daten zwischen Sites unnötiger Weise hin und her geschaufelt werden (PingPong Effekt). Der erste Eindruck von SDAARC vermittelt die Vorstellung, daß durch die Verbindung eines jeden Threads mit einem Datenobjekt wiederum Objekte entstehen, die man durch COMA unter den Attraction Memory Sites verteilen lassen könnte. Tasks für jeden Prozessor entsprechen den Thread-Objekten, die sich momentan in seinem lokalen Speicher befinden und könnten so geschedult werden. Man muß nicht lange suchen, bis man solche Objekte finden kann: in der Literatur zu Multithreaded“ (Mehrfädi” gen) Architekturen existiert zu jedem Thread ein Frame, der die Argumente und Rückgabeadressen des entsprechenden Threads beinhaltet. Diese Frames ähneln stark den Stack-Frames in konventionellen (sequentiellen) Architekturen, bis auf den Unterschied, daß diese nicht in einem Stack organisiert sind. Statt dessen werden die Frames wirksam in einem verteilten, gemeinsamen Heap gespeichert. Gewöhnlich können mehrere Threads einen Frame teilen [12] [2], doch die jüngere Literatur merkt an, daß Performancesteigerungen zu erreichen sind, falls jeder Thread einen eigenen Frame, genannt Framelet, besitzt [1]. Hat man ein Datenobjekt für jeden Thread gefunden und erlaubt die Verwendung der COMA Technologie um sowohl die Berechnung als auch die Daten zu verteilen, ist offensichtlich weitere Optimierung möglich. So haben wir nun ein Konzept, in dem nicht nur Daten wandern können, um sich der Programmaufteilung anzupassen, sondern in dem sich die Verteilung des Programms simultan zur Datenverteilung anpaßt. Dies stellt somit eine Lösung des Ping-Pong Effekts dar. In einer konventionellen COMA gibt es keinen effektiven Weg für die Daten sich einer schlechten Programmverteilung anzupassen: wird ein Datenblock von zwei Sites gleichzeitig benötigt, muß dieser Block zwischen diesen hin und her wandern. Im Gegensatz dazu ist es in SDAARC möglich, daß die konkurrierenden Berechnungen auf die selbe Site verteilt werden. 4.2 Die Broker und das Protokoll Die Broker in SDAARC sind eine Erweiterung der Cache-Kohärenz-Controller aus der COMA Literatur. Sie sind verantwortlich für den Netzwerkverkehr, den zugehörigen Prozessor mit Frames (Threads) zu versorgen und sicherzustellen, daß der transparent verteilte, gemeinsame Adreßraum konsistent und kohärent bleibt. Entsprechend einer herkömmlichen COMA, befolgen die Broker ein Cache-Kohärenz-Protokoll. Durch die Verbindung von Berechnung und Daten ist es möglich das Protokoll im Vergleich zu anderen COMAs wesentlich einfacher zu gestalten. In diesem Protokoll [9] können Objekte in einem von fünf Zuständen sein: Exklusiv, Cloned, Original, Leaving oder Invalid (ECOLI). Dies kann mit dem weit verbreiteten SMP (nicht-COMA) MESI (Modified, Shared, Exclusive und Invalid) Protokollen oder dem 7 Zustände COMA Protokoll aus [6] verglichen werden. Der gemeinsame Adreßraum in SDAARC ist in drei Bereiche aufgeteilt. Entsprechend einer herkömmlichen Harvard Architektur, gibt es Daten- und Berechnungsbereiche. Zusätzlich existiert ein dritter Bereich für Frames, um eine spezielle, optimale Handhabe der wichtigen Datenobjekte zu erlauben. 4.3 tet. Wird ein Objekt in eine Menge eingefügt die schon voll ist, muß ein anderes Objekt abwandern um Platz zu schaffen. 2. Ist der Broker untätig, kann er Mengen identifizieren, die fast voll sind und spontan Objekte von diesen Mengen ausweisen. Entsprechend gibt es zwei Gruppen von anziehenden Verteilungsereignissen: 1. Berechnet ein Thread ein Argument für einen anderen Frame, der sich auf einer anderen Site befindet, kann der empfangende Broker entscheiden, ob er einfach das Argument akzeptiert oder statt dessen den empfangenden Frame zu der Site schickt, die das Argument sendet. 2. In SDAARC produziert das Laden oder Speichern eines Datums immer eine Bestätigung um Synchronisation von Datenobjekten zu ermöglichen. Wenn Daten vom Datenbereich geladen oder gespeichert werden, kann der Broker entscheiden, ob der komplette Datenblock zur Site geschickt wird, die auf die Bestätigung wartet. Verteilungsereignisse Wie oben beschrieben, müssen die Broker die Balance zwischen den entgegengesetzten Forderungen nach Parallelität und Lokalität finden (um die Kommunikationskosten zu reduzieren). Um dies zu erreichen, müssen diese eine Sammlung von mehr oder weniger abhängigen Threads und Datenobjekten zusammenfügen. Das Ziel ist stets die kürzeste Laufzeit und nicht notwendigerweise ein gleichmäßiges Load-Balancing zu realisieren. Tasks sollten lediglich auf so viele Prozessoren verteilt werden, wie es auch effizient genutzt werden kann (insbesondere in Multitasking Umgebungen). Um diesen Spagat zu erreichen, definieren wir eine Anzahl von Kräften, die das Wandern von Objekten und Threads bewirken. Diese Kräfte fallen in zwei Kategorien: abstoßende Kräfte, verteilen Objekte und maximieren somit Parallelität (eventuell auf Kosten der Lokalität); wohingegen anziehende Kräfte verwandte Objekte zusammenbringen und die Lokalität maximieren (eventuell auf Kosten der Parallelität). Diese Kräfte wirken zu präzise definierten Verteilungsereignissen. Wenn diese Ereignisse eintreten, treffen die Broker die Entscheidung welche Objekte wandern und wohin sie wandern. Es gibt zwei abstoßende Verteilungsereignisse: 1. Angenommen die Attraction-Speicher sind mengenassoziativ: Jedes Objekt ist statisch auf eine Menge abgebildet, die eine kleine, konstante Anzahl (z.B. 4 oder 8) von Objekten beinhal- 5 Zusammenfassung In diesem Beitrag wurde ein hybrides System zur Partitionierung und Verteilung von Daten- und Berechnungslast in einem parallelen verteiltem System vorgestellt. Feingranulare Entscheidungen werden hier zur Compilezeit getroffen: Ein Programm wird in einen Datenflußgraphen übersetzt, aus dem durch einen heuristischen, gierigen Packalgorithmus Microthreads generiert werden. Grobgranulare Entscheidungen werden zur Laufzeit getroffen: Jedem Prozessor ist ein Broker an die Seite gestellt, dessen Aufgabe es ist: erstens, ein Cache-KohärenzProtokoll auszuführen, welches einen transparenten verteilten, gemeinsamen Adreßraum implementiert; und zweitens, aus dem lokalen Anteil gemeinsamer Frames genau solche, basierend auf den Informationen über die dynamische Last, auszusuchen, die für den lokalen Prozessor geeignet sind. Dieses Konzept, genannt SDAARC (SelfDistributing Associative ARChitecture) ist eine Erweiterung der COMA (Cache Only Memory Architecture) Techniken. Während sich COMAs mit dem Bereitstellen eines verteilten gemeinsamen Speichers begnügen, geht SDAARC weiter und bezieht die automatische Verteilung der Berechnung mit ein. Literatur [1] A NNAVARAM, M. ; NAJJAR, W. A.: Comparison of Two Storage Models in Data-Driven Multithreaded Architectures. In: Eighth IEEE Symposium on Parallel and Distributed Processing (SPDP). New Orleans, LA : IEEE Computer Society Press, Oktober 1996, S. 122–129 [2] C ULLER, D. E. ; G OLDSTEIN, S. C. ; S CHAU SER, K. E. ; VON E ICKEN , T.: TAM — A Compiler Controlled Threaded Abstract Machine. In: Journal of Parallel and Distributed Computing, Special Issue on Dataflow, 1993 [3] C ULLER, D. E. ; K ARP, R. ; PATTERSON, D. ; S AHAY, A. ; S CHAUSER, K. E. ; S ANTOS, E. ; S UBRAMONIAN, R. ; VON E ICKEN, T.: LogP: Towards a Realistic Model of Parallel Computation. In: Proc. of Fourth ACM SIGPLAN Symp. on Principles and Practice of Parallel Programming. San Diego, CA, Mai 1993 [4] D ENNEULIN, Y. ; NAMYST, R. ; M ÉHAUT, J.-F.: Architecture virtualization with mobile threads. In: Parallel Computing (ParCo’97) (1997), September. – <http://www.ens-lyon.fr/ ˜rnamyst/ps/parco97.ps.gz> [5] [6] [7] [8] [9] H AGERSTEN, E. ; L ANDIN, A. ; H ARIDI, S.: DDM — A Cache-Only Memory Architecture. In: IEEE Computer 25 (1992), Nr. 9, S. 44–54 H ARIDI, S. ; H AGERSTEN, E.: The Cache Coherence Protocol of the Data Diffusion Machine. In: Proceedings of the PARLE 89 Bd. 1, Springer-Verlag, 1989, S. 1–18 K LANG, M.: Hierarchische Zerlegung von Datenflußgraphen für mehrfädige Architeckturen. Fachbereich Informatik, Frankfurt am Main, Johann Wolfgang Goethe Universität, Diplomarbeit, Oktober 1997 M OORE, R. ; K LAUER, B. ; WALDSCHMIDT, K.: Compiler Technology for Two Novel Computer Architectures. In: 14th ITG/GIFachtagung Architektur von Rechensystemen 1997 (ARCHS ’97). Rostock, Germany, September 1997. – <http://www.ti. informatik.uni-frankfurt.de/ Papers/Adarc/rostock.pdf> M OORE, R. ; K LAUER, B. ; WALDSCHMIDT, K.: Automatic Scheduling for Cache Only Memory Architectures. In: Third International Conference on Massively Parallel Computing Systems (MPCS ’98). Colorado Springs, Colorado, April 1998. – <http://www. ti.informatik.uni-frankfurt.de/ Papers/Adarc/colosprings.pdf> [10] M OORE, R. ; K LAUER, B. ; WALDSCHMIDT, K.: A Combined Virtual Shared Memory and Network which Schedules. In: International Journal of Parallel and Distributed Systems and Networks 1 (1998), Nr. 2, S. 51–56. – <http://www.ti.informatik. uni-frankfurt.de/Papers/Adarc/ barcelona.pdf> [11] M OORE, R. ; Z ICKENHEINER, S. ; K LAUER, B. ; H ENRITZI, F. ; B LECK, A. ; WALDSCHMIDT, K.: Neural Compiler Technology for a Parallel Architecture. In: International Conference on Parallel and Distributed Processing Techniques and Applications (PDPTA ’96). Sunnyvale, CA, August 1996. – <http://www.ti. informatik.uni-frankfurt.de/ Papers/Adarc/camready.pdf> [12] N IKHIL, R. S.: A Multithreaded Implementation of Id using P-RISC Graphs. In: Proceedings of the Sixth Annual Workshop on Languages and Compilers for Parallel Computing. Portland, Oregon : Springer Verlag LNCS 768, August 1993, S. 390–405 [13] PATTERSON, D. A.: Microprocessors in 2020. In: Scientific American Special Issue: The Solid-State Century 8 (1997), Nr. 1, S. 86–88 [14] S AULSBURY, A. ; W ILKINSON, T. ; C ARTER, J. ; L ANDIN, A.: An Argument For Simple COMA. In: First IEEE Symposium on High Performance Computer Architecture. Rayleigh, North Carolina, Januar 1995, S. 276–285 [15] S TENSTR ÖM, P. ; J OE, T. ; G UPTA, A.: Comparative Performance Evaluation of CacheCoherent NUMA and COMA Architectures. In: Proceedings of the 19th Annual International Symposium on Computer Architecture. Gold Coast, Australia, Mai 1992, S. 80–91 [16] T HITIKAMOL, K. ; K ELEHER, P.: Thread Migration and Communication Minimization in DSM Systems. In: Proceedings of the IEEE 87 (1999), März, Nr. 3, S. 487– 497. – <http://mojo.cs.umd.edu/ papers/ieee98.pdf>