Parallele Programmierung Prof. Dr. R. Loogen 10. Januar 2008 Inhaltsverzeichnis Inhaltsverzeichnis i 1 Einführung 1.1 Programmieren von parallelen Rechnern . . . . . . . . . . . . . . 1.1.1 Erwartungen . . . . . . . . . . . . . . . . . . . . . . . . 1.1.2 Probleme: „grand challenges“ „scientific computing“ . . . 1.1.3 Algorithmen Kernidee: „Divide et impera!“ . . . . . . . . 1.1.4 Bewertungskriterium für parallele Systeme / Algorithmen 1.1.5 Parallelrechner . . . . . . . . . . . . . . . . . . . . . . . 1.1.6 Gegenüberstellung und Zusammenfassung . . . . . . . . 1.2 Parallele Programmiermodelle . . . . . . . . . . . . . . . . . . . 1.2.1 Parallelität vs. Nebenläufigkeit . . . . . . . . . . . . . . . 1.2.2 Prozesse vs. Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 1 1 1 3 4 5 5 6 6 2 Entwurf paralleler Programme 2.1 PCAM-Methode nach Foster (1995) . . . . . . . . . . . . . . 2.1.1 Partitionierung (P) . . . . . . . . . . . . . . . . . . . 2.1.2 Kommunikation (C) . . . . . . . . . . . . . . . . . . 2.1.3 Parallele Matrixmultiplikation nach Gentlement (1978) 2.1.4 Agglomeration (A) . . . . . . . . . . . . . . . . . . . 2.1.5 Mapping (M) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 8 9 10 11 12 16 . . . . . . . . . . 17 17 17 18 20 21 22 22 23 23 31 3 Grundkonzepte paralleler Programme 3.1 Synchronisation von Speicherzugriffen . . . . . . . . . 3.1.1 Synchronisationskonstrukte . . . . . . . . . . 3.1.2 Synchronisationsformen . . . . . . . . . . . . 3.1.3 Barrierensynchronisation . . . . . . . . . . . . 3.2 Monitore . . . . . . . . . . . . . . . . . . . . . . . . 3.2.1 Monitordeklaration . . . . . . . . . . . . . . . 3.2.2 Signalisierungsmethoden . . . . . . . . . . . . 3.3 Synchronisation und Kommunikation über Nachrichten 3.3.1 Kommunikationsmodelle . . . . . . . . . . . . 3.4 Verteilte Programmierung in MPD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i INHALTSVERZEICHNIS INHALTSVERZEICHNIS 4 Die Bibliothek MPI 4.1 Grundkonzepte . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2 Kommunikation . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3 Kommunikatoren, Prozessgruppen und Topologieinformationen 4.3.1 Prozessgruppen: Konstruktion, Analyse, Manipulation . 4.3.2 Kommunikatoren . . . . . . . . . . . . . . . . . . . . . 4.3.3 Virtuelle Topologien . . . . . . . . . . . . . . . . . . . 4.3.4 Zugriffsroutinen . . . . . . . . . . . . . . . . . . . . . 4.3.5 Interkommunikatoren . . . . . . . . . . . . . . . . . . . 4.4 Abgeleitete Datentypen . . . . . . . . . . . . . . . . . . . . . . 4.4.1 Spezifikation neuer Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 35 37 41 43 44 45 46 47 47 48 5 Parallele Algorithmen 5.1 Das PRAM- Rechnermodell . . . . . . . . . . . . . . . . . . . . . . 5.2 Rechnermodelle mit verteiltem Speicher . . . . . . . . . . . . . . . . 5.3 Paralleles Sortieren . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.1 Ein CRCW-Verfahren mit konstantem Zeitaufwand . . . . . . 5.3.2 Sortiernetze . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.3 Der Algorithmus von Cole . . . . . . . . . . . . . . . . . . . 5.4 Graphen Algorithmen . . . . . . . . . . . . . . . . . . . . . . . . . . 5.4.1 Bestimmung der Zusammenhangskomponenten eines Graphen 5.4.1.1 Algorithmus von Hirschberg (1976) . . . . . . . . 5.4.2 Kürzeste Wege . . . . . . . . . . . . . . . . . . . . . . . . . 5.4.3 Minimal spannende Bäume . . . . . . . . . . . . . . . . . . . 5.4.3.1 Algorithmus von Prim . . . . . . . . . . . . . . . . 5.4.3.2 Algorithmus von Sollin (1977) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 49 54 56 56 57 62 67 67 69 72 73 73 75 6 Algorithmische Skelette . . . . . . . . . . . . . . . . . . . . 78 Literaturverzeichnis I Abbildungsverzeichnis II ii 1 Einführung 1.1 Programmieren von parallelen Rechnern Problem ®¶ Paralleler Algorithmus KS kX XXXXX XXXXX XXXXX XX ParallelesKS Programm o ffff große Wechselwirkung f f f f f fffff sfffff ®¶ ®¶ Parallelrechner 1.1.1 Erwartungen • mehr Rechenleistung • besseres Preis-/ Leistungsverhältnis • bessere Verfügbarkeit durch Redundanzen • besser verständliche Programme durch mehr Information über die Problemstruktur 1.1.2 Probleme: „grand challenges“ „scientific computing“ • Probleme aus Naturwissenschaft und Technik, meist Simulationen technischer und natürlicher Vorgänge. • verteilte Datenbanksysteme • Telekommunikationsbereiche 1.1.3 Algorithmen Kernidee: „Divide et impera!“ Divide et / Zerlegung in unabhängige Teile impera \ paralleles lösen der Teile 1 KAPITEL 1. EINFÜHRUNG 1.1. PROGRAMMIEREN VON PARALLELEN RECHNERN Beispiele: Datenparallelität: Vektoraddition „Feingranulare Parallelität“ (a1 , ... , an ) + (b1 , ... , bn ) (a1 + b1 , . . . ,an + bn ) | {z } | {z } unabhängige Teile Kontrollparallelität: Summation von n Zahlen n−1 X ai , n = 2k i=0 sequentiell: n − 1 Additionen parallel: „rekursives Doppeln“ a0 @ @ a1 s ysss + II II $ a ...a aK2 + a 3 n−2 n−1 PPP KKK n PP( {www % vnnnn ... + o+ uu ooo u zu wooo + % + v n/2 Additionen n/4 Additionen 1 Addition k X n = . . . = n − 1 Additionen in k = log n parallelen Schritten bei n Ar2i i=1 beitseinheiten. Sortieren von n Zahlen: ā =< a1 , . . . , an >, n ≥ 1 paarweise verschiedene ai Ranksort: Definition: rangā (i) = |{aj |aj < ai }| Rang des i-ten Elementes von ā −→ Position in der sortierten Folge. Verfahren: Für jedes Element (Datenparallelität): • Bestimme den Rang durch Vergleiche mit allen übrigen Elementen • Ordne Element in sortierter Folge an entsprechender Position ein sequentieller Aufwand: n2 paralleler Aufwand: n Schritte (bei n Verarbeitungseinheiten) Beispiel: für ein nicht parallelisierbares Problem k Berechnung einer großen Potenz von x ∈ R : x2 mit k ≫ 1 sequentielles Vorgehen: sukzessi- 2 KAPITEL 1. EINFÜHRUNG 1.1. PROGRAMMIEREN VON PARALLELEN RECHNERN ves Quadrieren = x2 = x4 = x8 .. . (1) (2) (3) .. . x∗x x2 ∗ x2 x4 ∗ x4 (k) xk ∗ xk = xk 2 1.1.4 Bewertungskriterium für parallele Systeme / Algorithmen Parameter: • Problemgröße n ∈ N • Anzahl der Prozessoren p ≥ 1 1. Zeitkomplexität: Tp (n) , Tp (n, A) # Zeitschritte zur Durchführung von Algorithmus A mit Eingabedaten der Größe n auf p Prozessoren (Laufzeit auf p Prozessoren) Seq. Zeit: T1 (n) sequentieller Algoithmus. 2. Beschleunigung: (speedup) : Sp = TTp1 Maß für Zeitgewinn durch Parallelverarbeitung i. Allg. gilt : 1 ≤ Sp ≤ p • Slowdown (Sp ≤ 1) möglich wegen Zusatzaufwand für Parallelität • Superlinearen Speedup(Sp > p) Cache-Effekte, Suchverfahren / brach-and-bound Verfahren S 3. Effizienz: Ep = pp Maß für Prozessorauslastung bei identischer Anzahl von Berechnungsschritten im parallelen und sequentiellen Fall. 4. Skalierbarkeit: Abhängigkeit eines Verfahrens von der Rechnerkonfiguration 5. Kommunikationskomplexität: Datentransferaufwand Beispiel: • rekursives Doppeln: T1 (n) = n − 1 Tp (n) = log2 n mit p ≥ ³ ´ Ep ∈ O log1 n n 2 ¾ =⇒ Sp = n−1 log n ∈O ³ n log n ´ • Ranksort: ¾ T1 (n) = n2 (sequentieller Ranksort) =⇒ Sn = n „relativer Speedup“ Tn (n) = n 3 KAPITEL 1. EINFÜHRUNG 1.1. PROGRAMMIEREN VON PARALLELEN RECHNERN relativer Speedup: Vergleich – selben Algorithmus auf 1 und p Prozessoren absoluter Speedup: Vergleich – (optimalen) sequentiellen Algorithmus mit parallelen Algorithmus auf p Prozessoren T1opt (n) = n log n =⇒ Snopt = log2 n Amdahls Gesetz1 Sp ≤ 1 f Jeder Algorithmus hat eine sequentielle Komponente die den parallelen Speedup bremst. Berechnung mit n Schritten und p Prozessoren Sei f , 0 ≤ f ≤ 1, der Anteil an Schritten, die sequentiell ausgeführt werden müssen. =⇒ f · n sequentielle Schritte (1 − f ) · n parallel ausführbare Schritte T1 = n (1 − f ) · n p 1 T1 1 ≤ = (1−f ) Tp f f+ p Tp = f · n + Sp = 1.1.5 Parallelrechner MIMD: Multiple Instruction Multiple Data • speichergekoppelte Systeme – gemeinsamer Adressraum (logische Sicht) – Kommunikation und Synchronisation über gemeinsame Variablen • nachrichtengekoppelte Systeme – prozessorlokale Adressräume – Kommunikation und Synchronisation durch Austausch von Nachrichten 1 4 Gene Amdahl KAPITEL 1. EINFÜHRUNG 1.2. PARALLELE PROGRAMMIERMODELLE Speicher Adressräume gemeinsam global SMP: symmetrischer Multiprozessor physikalisch verteilt DSM: distributed shared memory UMA: uniform memory access NUMA: Non UMA Multicomputer, distributed memory verteilt 1.1.6 Gegenüberstellung und Zusammenfassung SMP gemeinsam global Kriterien Adressraum Speicher Komm. / Synch. gemeinsame Variablen Skalierbarkeit Lastverteilung begrenzt leicht mangelnde Skalierbarkeit und Konflikte beim Speicherzugriff Probleme DSM gemeinsam verteilt gemeinsame Variablen mit internen Nachrichten leicht „leicht“ Cache-Kohärenz DM verteilt verteilt Nachrichten leicht schwierig globale Synchronisation 1.2 Parallele Programmiermodelle gemeinsamer Adressraum2 (MPD, PRAM) Nachrichtenkopplung • Prozess-Kanal-Modell (Ian Foster) • Prozess-Nachrichten-Modell (MPI3 , PVM4 ) SPMD5 • statisches Prozesssystem • Nachrichtenkopplung 2 shared memory, logische Sicht Message Parssing Interface 4 Parallel Virtual Machine 5 Single Program Multiple Data 3 5 KAPITEL 1. EINFÜHRUNG 1.2. PARALLELE PROGRAMMIERMODELLE • Modellierung von Datenparallelität Datenparallelität (HPF6 , NESL7 ) • Bereitstellung von datenparallelen Grundoperationen – hoher Abstraktionsgrad 1.2.1 Parallelität vs. Nebenläufigkeit Nebenläufigkeit(concurrency, multithreading) • reaktive Systeme System Umgebung ←−−−−−−−→ Parallelität • transformationelle Systeme Input System Output −−−−→ −−−−−→ 1.2.2 Prozesse vs. Threads Definition von Prozess: • „sequenzielle“ Folge von Aktivitäten durch die eine in sich geschlossene Aufgabe bearbeitet wird oder • funktionelle Einheit aus – Zeitlich invarianten Programm – Satz von Daten – Zeitlich variantem Zustand Jeder Prozess besitzt Umgebung und Kontext Umgebung: geschützter Adressbereich eines Prozesses, d. h. • Codebereiche und Datenbereiche im Speicher • geöffnete Dateien • Ressourcenverweise Kontext: „Registerwerte“, d. h. • Befehlszähler • Zeiger auf Laufzeitkeller • Aktivierungsblock 6 7 6 High Performance Fortran A Nested Data-Parallel Language KAPITEL 1. EINFÜHRUNG 1.2. PARALLELE PROGRAMMIERMODELLE • usw. Prozesse: • eigene Umgebung und Kontext Threads: • eigener Kontext • teilen sich Umgebung mit anderen Threads 7 2 Entwurf paralleler Programme 2.1 PCAM-Methode nach Foster (1995) PCAM: Partitioning Communication Agglomeration Mapping P-Partitionierung problemabhängige Zerlegung der Berechnung und der Daten in Teile (tasks) ohne Berücksichtigung der Zielarchitektur −→ maximale inhärente Parallelität Skalierbarkeit beachten C-Communication Analyse der Datenabhängigkeit Festlegung der Kommunikationsanforderungen A-Agglomeration Zusammenfassung stark zusammenhängender Teile zu größeren Tasks. Ziel: Effizienzsteigerung durch Kostenminimierung M-Mapping (Abbildung) 8 KAPITEL 2. ENTWURF PARALLELER PROGRAMME 2.1. PCAM-METHODE NACH FOSTER (1995) Abbildung der resultierenden Struktur auf konkrete Zielarchitektur Ziel: Maximierung der Prozessorauslastung −→ statisch oder mit dynamischer Lastverteilung 2.1.1 Partitionierung (P) Ziele: • möglichst feinkörnige Zerlegung der Berechnung / Datenbasis • Vermeidung der Duplizierung von Daten und Berechnungen • maximal vorhandene Parallelität Methoden: • Bereichszerlegung (domain decomposition) • funktionale Zerlegung (functinal decomposition) Beispiel: Matrix-Multiplikation A = (aij )1≤i,j≤n B = (bij )1≤i,j≤n C = (cij )1≤i,j≤n mit cij = n X aik bkj k=1 mögliche Bereichszerlegungen: a) −→ Eingabematrizen 2n2 Tasks b) −→ Ausgabematrix n2 Tasks ¾ Datenparallelität funktionale Zerlegung: c) −→ n2 · n n2 (n − 1) Multiplikationen Additionen ¾ Kontrollparallelität Checkliste: • # Tasks ≫ # Prozessoren? – Flexibilität • keine redundanten Berechnungen, keine redundanten Speicheranforderungen? – Skalierbarkeit • vergleichbare Größe der Tasks? – Lastausgleich • alternative Partitionierung? – Flexibilität • # Tasks skaliert mit Problemgröße? (nicht Größe der Tasks) – Skalierbarkeit 9 KAPITEL 2. ENTWURF PARALLELER PROGRAMME 2.1. PCAM-METHODE NACH FOSTER (1995) 2.1.2 Kommunikation (C) Ziel: Identifikation der Kommunikationen, die erforderlich sind, um Tasks mit den von ihnen benötigten Daten zu versorgen. Kommunikationsmuster: • • • • lokal vs. global strukturiert vs. unstrukturiert statisch vs. dynamisch synchron vs. asynchron Checkliste: • reguläre Struktur, d. h. Anzahl der Kommunikation in allen Tasks etwa gleich? −→ Skalierbarkeit −→ Balancierung • möglichst lokale Kommunikation? −→ Effizienz • Kommunikation nebenläufig (zu den Berechnungen)? −→ Effizienz Beispiel: Matrix-Multiplikation aik Zerlegung A: bkj Jedes aik wird mit n bkj (1 ≤ j ≤ n) multipliziert Task Aik sendet Element aik zu den n Tasks Bkj (1 ≤ j ≤ n). Task Bkj erhält n Werte aik (1 ≤ i ≤ n) von Tasks Aik und berechnet n Produkte aik bkj (1 ≤ j ≤ n). Die zur Berechnung von cij notwendigen Produkte befinden sich in der j-ten Spalte der BTasks. Verwende etwa rekursives Doppeln zum Aufsummieren der n Produkte. Analyse: • sehr unausgeglichene Tasks • viele Kommunikationen • ungünstige Datenverteilung Zerlegung B: Task Cij benötigt i-te Zeile von A und die j-te Spalte von B. Mit diesen Daten kann ohne weitere Kommunikation cij berechnet werden. Problem: Die Eingangsmatrizen werden n-mal repliziert. Beobachtung: Task Cij kann zu jedem Zeitpunkt höchstens ein Produkt aik · bkj berechnen und benötigt dazu je ein Element von A und B 10 KAPITEL 2. ENTWURF PARALLELER PROGRAMME 2.1. PCAM-METHODE NACH FOSTER (1995) Idee: Rotiere Zeilen von A und Spalten von B so in Zeilen / Spalten von C, dass zu jedem Zeitpunkt passende, d. h. zu multiplizierende A/B-Werte in C-Tasks zur Verfügung stehen. Beispiel: n = 3 Abbildung 2.1: Matrix-Multiplikation n=3 a11 b11 a12 b12 a13 b13 a11 b11 a12 b22 a13 b33 a21 b21 a22 b22 a23 b23 a22 b21 a23 b32 a21 b13 a31 b31 a32 b32 a33 b33 a33 b31 a31 b12 a32 b23 Rotiere die Zeilen von A und die Spalten von B, so dass jede C-Task eine Multiplikation durchführen kann. i-te Zeile von A wird um i − 1 Positionen nach links rotiert (1 ≤ i ≤ n) j-te Spalte von B wird um j − 1 Positionen nach oben rotiert (1 ≤ j ≤ n) 2.1.3 Parallele Matrixmultiplikation nach Gentlement (1978) n2 Tasks Cij (1 ≤ i, j ≤ n) mit Elementen aij , bij der Eingangsmatrizen, die jeweils cij berechnen sollen. als Kommunikationsstruktur: Torusvernetzung der Größe n × n Analyse: • synchrones Verfahren • Tasks gleich komplex • Kommunikation: – lokal – nebenläufig zur Berechnung – nebenläufig in Zeilen / Spalten funktionale Zerlegung: Ausgangspunkt: n3 Tasks, die jeweils ein Produkt aik bkj berechnen sollen =⇒ 3- dimensionale Struktur (Würfeltopologie) 11 KAPITEL 2. ENTWURF PARALLELER PROGRAMME 2.1. PCAM-METHODE NACH FOSTER (1995) Listing 1 Parallele Matrixmultiplikation nach Gentlement – Rotiere i-te Zeile von Matrix A um i − 1 Positionen nach links – Rotiere j-te Spalte von B um j − 1 Positionen nach oben – Anschließend führt jeder Task folgende Anweisungen durch: var a, b, c : real; sum := 0; for i=1 to n do{ sende a an linke Nachbartask sende b an obere Nachbartask sum := sum + a * b; empfange a von rechter Nachbartask empfange b von unterer Nachbartask } od cij := sum; 1 2 3 4 5 6 7 8 9 10 11 i C A k j B Broadcast von A in Dimension j Broadcast von B in Dimension i =⇒ parallele Produktberechnung Aufsummieren aller Produkte in Dimension k 2.1.4 Agglomeration (A) Ziele: • Zusammenfassung von stark interagierenden Teilberechnungen −→ Reduktion der Kommunikation – Flexibilität bezüglich Skalierbarkeit – Reduktion der Software-Entwicklungskosten Methoden: • Dimensionsreduktion 12 KAPITEL 2. ENTWURF PARALLELER PROGRAMME 2.1. PCAM-METHODE NACH FOSTER (1995) Oberflächen ↑ Kommunikation Volumen - Effekt ↑ Berechnungen Beispiel: 8 × 8-Gitter Jeder Knoten verwaltet ein Element und schickt dieses an alle Nachbarn. y 64 ∗ 4 = 256 bidirektionale Kommunikationen von ebenso vielen Datenelementen. a) Dimensionsreduktion: Zeilenweise y 8 Tasks, die je 8 Datenelemente verwalten y 8 ∗ 2 = 16 bidirektionale Kommunikation mit Austausch von 16 ∗ 8 = 128 Datenelemente. b) Blockaufteilung in 2 × 2 Grid: y jeder Knoten verwaltet 16 Elemente und tauscht Randdaten mit allen Nachbarn. y 4 ∗ 4 = 16 bidirektionale Kommunikation mit Austausch von 16 ∗ 4 = 64 Datenelemente. Methoden: (Agglomeration) • Granularitätserhöhung durch a) Dimensionsreduktion b) Blockbildung −→ Teilblöcke in Mehrdimensionalen Gitterstrukturen Beispiel: Gentleman Verfahren • Granularität einzelner Tasks ist im Allgemeinen zu gering 13 KAPITEL 2. ENTWURF PARALLELER PROGRAMME 2.1. PCAM-METHODE NACH FOSTER (1995) – – – – Einteilung der Matrizen in m2 Submatrizen der Dimension Standartmatrixmultiplikation für Teilmatrizen Verhältnis Kommunikationsaufwand sinkt Berechnungsaufwand gute Skalierbarkeit n m c) Baumstrukturen Agglomeration der Baumstruktur für Reduktion Beispiel: für Replikation von Berechnungen in der Agglomerationsphase. Betrachte Reduktion mit anschließendem Broadcast des Reduktionsergebnisses =⇒ 2 log2 N Schritten bei N Prozessoren à Hypercube-Topologie (Abb. 2.2) Abbildung 2.2: Hypercube (a) k = 1 14 (b) k = 2 (c) k = 3 KAPITEL 2. ENTWURF PARALLELER PROGRAMME 2.1. PCAM-METHODE NACH FOSTER (1995) Abbildung 2.3: Butterfly-Vernetzung 4 Prozessoren ↑ Mehrfachberechnung Ein Hypercube der Dimension k (Abb. 2.4) enthält 2k Knoten und erlaubt Reduktionen / Broadcast in k-Schritten Abbildung 2.4: Hypercube der Dimension k i C A k j B −→ Matrixmultiplikation im Hypercube1 Verteile Matrixelemente so in einem Hypercube der Dimension 3q und q = ⌈log2 n⌉, so dass • alle n3 ≤ 23⌈log2 n⌉ Multiplikation gleichzeitig erfolgen können • Broadcast der Eingabematrizen in O (log2 n) • Summation der Produkte in O (log2 n) möglich =⇒ Gesamtaufwand O (log2 n) Beispiel: n = 3 y ⌈log2 n⌉ = 2 = q y Hypercube der Dimension 6 mit 26 = 64 Knoten Checkliste: (Agglomeration) • • • • • • 1 Reduktion der Kommunikationskosten durch Verbesserung der Lokalität? Mehraufwand durch Replikation von Daten / Berechnungen gerechtfertigt? Skalierbarkeit? Verhältnis Kommunikation – Berechnungen? Balancierung der Task-Komplexität? weitere Zusammenfassung? Deckel, Nassimi and Sahni 1981 15 KAPITEL 2. ENTWURF PARALLELER PROGRAMME 2.1. PCAM-METHODE NACH FOSTER (1995) 2.1.5 Mapping (M) (Abbildung auf Rechner) Ziele: • Verteilung der verbleibenden Tasks auf die Prozessoren des Zielrechners • Platzierung von häufig kommunizierenden Tasks auf den selben Prozessor und unabhängige Tasks auf unterschiedlichen Rechner Methoden: • statische vs. dynamische Lastverteilung • explizite Task-Verteilung −→ Master-Worker-Algorithmus Checkliste: • alle Alternativen berücksichtigt? • Implementierungskosten vertretbar? 16 3 Grundkonzepte paralleler Programme −→ MPD:1 Multithreaded, Parallel and Distributed 3.1 Synchronisation von Speicherzugriffen Problem: • Speicherzugriffe auf gemeinsame Variablen müssen koordiniert werden • nicht atomare Operationen (können unterbrochen werden) erfordern exklusiven Zugriff auf globale Daten 3.1.1 Synchronisationskonstrukte • Semaphore2 Ein Semaphor ist ein abstrakter Datentyp mit: – nicht negativer Integer-Variablen (Semaphorzähler) – zwei unteilbare Operationen: ∗ P (passieren) ∗ V (verlassen) in MPD: Semaphor-Deklaration: sem identifier[subscripts]= expression Operationen: P(identifier [subscripts]) V(identifier [subscripts]) 1 2 Gregory R. Andrews Edsger W. Dijkstra, 1965 17 KAPITEL 3. GRUNDKONZEPTE PARALLELER PROGRAMME 3.1. SYNCHRONISATION VON SPEICHERZUGRIFFEN 3.1.2 Synchronisationsformen a) Wechselseitiger Ausschluss (mutual exclusion) Beispiel: Geschützter Zugriff auf gemeinsame Ressourcen b) einseitige Synchronisation Ereignissynchronisation (events) Bedingungssynchronisation (conditions) Ein Prozess wartet auf Bedingung oder Ereignis, dass von einem anderen ausgelöst wird. Beispiel: Erzeuger-Verbraucher mit unbeschränktem Puffer c) Barrierensynchronisation Eine Gruppe von Prozessen muss an einer sogenannten Barriere warten, bis alle Prozesse der Gruppe die Barriere erreicht haben. Beispiel: Synchronisation von Schleifendurchläufen paralleler Prozesse. Synchronisationskonstrukte • Semaphoren (Dijkstra) Ein Semaphor S ist ein abstrakter Datentyp mit – nicht-negativer Integer Variable (Semaphorzähler) – zwei unteilbare Operationen P (passieren) , V (verlassen) P(S)(atomar): Wenn S > 0, dann S := S − 1; sonst wird der ausführende Prozess suspendiert V(S)(atomar): Wenn Prozesse bei Ausführung von P(S) suspendiert wurden, reaktiviere einen Prozess; sonst: S := S + 1 Beispiel: einseitige Synchronisation Erzeuger / Verbraucher - Problem: Verbraucher kann erst konsumieren, wenn Erzeuger produziert hat. Annahme: unbeschränkter Puffer ½ sem mutex = 1 Semaphorvariablen: sem full = 0 18 KAPITEL 3. GRUNDKONZEPTE PARALLELER PROGRAMME 3.1. SYNCHRONISATION VON SPEICHERZUGRIFFEN Listing 2 Erzeuger Verbraucher - Problem process producer() int item; while (true){ produce(item); P(mutex); enter(item); V(mutex); V(full); } 1 2 3 4 5 6 7 8 9 process consumer() int item; while (true){ P(full); P(mutex); remove(item); V(mutex); consume(item); } 11 12 13 14 15 16 17 18 19 Fallstudie: Leser- / Schreiberproblem Mehrere Prozesse arbeiten auf gemeinsamen Speicherbereich. Gleichzeitige Lesezugriffe sind erlaubt Schreibzugriffe müssen exklusiv erfolgen. =⇒ CREW3 Lösungsansatz: [P. J. C OURTOIS , F. H EYMANNS , D. L. PARNAS ACM 1971] Idee: Verwalte Zähler readcount für Leseranzahl Listing 3 Zwei Semaphoren sem writing = 1 −→ Sperre für exklusiven Schreibzugriff sem rcount_mutex = 1 −→ Schutz für readcount Korrektheit: Das Semaphor writing schützt den Speicherbereich. • Schreiber aktiv (in critical section) y writing ist gesetzt – kein weiterer Schreiber kann passieren – 1. Leser bei P(writing) blockiert – weitere Leser bei P(rcount_mutex) blockiert • Leser aktiv y writing ist gesetzt durch 1. Leser – weitere Leser passieren writing nicht, sondern erhöhen nur readcount – Schreiber werden bei P(writing) blockiert. 3 Concurrent Read Exclusive Write 19 KAPITEL 3. GRUNDKONZEPTE PARALLELER PROGRAMME 3.1. SYNCHRONISATION VON SPEICHERZUGRIFFEN Problem: Schreiber werden ausgehungert, falls readcount nie Null wird, weil ständig neue Leser hinzukommen, bevor aktive Leser den kritischen Bereich verlassen. Lösung: Blockiere neue Leser, sobald ein Schreiber wartet. −→ Zähler für Leser und für wartende Schreiber writecount. neue Semaphore: sem wcount_mutex = 1 sem reading = 1 (Sperre für Leser, falls Schreiber wartet) Wenn nicht bekannt ist, nach welcher Strategie bei ’reading’ blockierte Prozesse reaktiviert werden, können Schreiber immer noch ausgehungert werden, falls immer wieder wartende Leser reaktiviert werden. =⇒ Sorge dafür, dass bei reading höchstens ein Schreiber und keine weiteren Leser blockiert werden. =⇒ weiteres Semaphor: sem r_protect = 1; 3.1.3 Barrierensynchronisation Prozesse dürfen nur passieren, wenn alle Prozesse der Gruppe die Barriere erreicht haben. klassisches Beispiel: Iterationsverfahren zum Lösen partieller Differentialgleichungen Zweidimensional Temperaturverteilungsproblem Ermittle die Temperatur in Gitterpunkten im stabilen Zustand ϕx,y = 1 (ϕx−1,y + ϕx+1,y + ϕx,y−1 + ϕx,y+1 ) 4 −→ Iterationsverfahren (Jacobi, 1845) ϕ0x,y -geschätzter Anfangswert 20 KAPITEL 3. GRUNDKONZEPTE PARALLELER PROGRAMME 3.2. MONITORE ϕi+1 x,y = ¢ 1¡ i ϕx−1,y + ϕix+1,y + ϕix,y−1 + ϕix,y+1 4 einfache Zählbarriere hat Aufwand O (n) −→ lineare Barriere Optimierung: Turniertechnik −→ Aufwand O (log n) Die globale Barriere ist eine sehr starke und entsprechend teure Form der Synchronisation. Oft ist es möglich, eine globale Synchronisation durch eine lokale zu ersetzen. Beispiel: lokale Synchronisation eines Zeilenprozesses mit beiden Nachbarzeilenprozessen Symmetrische Barriere für zwei Prozesse mit zwei Semaphoren: sem b1=0; sem b2=0; 3.2 Monitore abstrakte Datenstruktur mit impliziten Synchronisationseigenschaften. Zugriffsoperationen werden im wechselseitigen Ausschluss ausgeführt −→ verborgene Implementierung der Zugriffsoperationen und der Synchronisation 21 KAPITEL 3. GRUNDKONZEPTE PARALLELER PROGRAMME 3.2. MONITORE 3.2.1 Monitordeklaration monitor <m_name> { <variable declaration> <initialization statements> <procedure declaration> } Aufruf eines Monitorprozesses: <m_name> . <procedurename> (<args>) Für Synchronisation innerhalb von Monitoren (bedingte Synchronisation) ist eine Erweiterung des Basiskonzeptes erforderlich: Bedingungsvariablen (condition variables): condvar <name> Operationen auf Bedingungsvariablen • wait(c) „Warte auf Erfüllt sein von c“ Der ausführende Prozess wird suspendiert und in die Warteschlange zu c eingereiht. Der Monitor wird freigegeben. • signal(c) „Signalisiere, dass c gilt“ Der ausführende Prozess reaktiviert den „ältesten“ Prozess in der Warteschlange zu c. Damit gegenseitiger Ausschluss gewährt bleibt, muss festgelegt werden, welcher Prozess den Monitor erhält. 3.2.2 Signalisierungsmethoden • signal_and_exit „Concurrent Pascal“ – Signal nur am Ende von Monitorproz. erlaubt • signal_and_continue „SR“ – signalisierender Prozess bleibt aktiv, reaktivierter Prozess muss sich neu um Monitorzugang bewerben. • signal_and_wait „Modula“ – signalisierender Prozess muss sich neu um Monitor bewerben. Beispiel: 22 KAPITEL 3. GRUNDKONZEPTE PARALLELER PROGRAMME 3.3. SYNCHRONISATION UND KOMMUNIKATION ÜBER NACHRICHTEN Listing 4 monitor bounded_buffer monitor bounded_buffer typeT buf[n]; int front = 0, rear = 0, count = 0; condvar not_full, not_empty; procedure enter (typeT data){ while (count == n) wait(not_full); buf[rear] = data; count++; rear = (rear+1) mod n; signal(not_empty); } 1 2 3 4 5 6 7 8 9 10 3.3 Synchronisation und Kommunikation über Nachrichten • meist bei verteiltem Speicher −→ kein gemeinsamer Speicher −→ keine globalen Variablen −→ keine zu schützenden Datenbereiche • Kommunikation über „Kanäle“ und Nachrichtenaustausch (message passing) Modell: Sender =⇒ Empfänger Statt schreiben / lesen gemeinsamer Variablen senden / empfangen von Nachrichten • Synchronisation indirekt dadurch, dass Nachrichten erst nach dem Senden empfangen werden können. • Kanäle sind Abstraktionen vorhandener Verbindungsnetzwerke • Kommunikationsaufwand bestimmender Faktor für die Leistung von Verfahren 3.3.1 Kommunikationsmodelle Basiskonzepte: • Prozesse & Kanäle • Sende- und Empfangsprimitiven sende „Nachricht“ an „Empfänger“ empfange „Nachricht“ (von „Sender“) Merkmale: (a) Bezeichnung von Quelle und Ziel der Infomationsübertragung – direkte Prozessbenennung4 vs. Kanäle5 (b) Anzahl der Kommunikationspartner / Art der Kanäle 4 5 „implizite Kanäle zwischen jedem Paar von Prozessen“ mehrere Kommunikationswege zwischen gleichen Prozessen möglich 23 KAPITEL 3. GRUNDKONZEPTE PARALLELER PROGRAMME 3.3. SYNCHRONISATION UND KOMMUNIKATION ÜBER NACHRICHTEN 1:1 1:n m:n m:1 Punkt-zu-Punkt-Kommunikation Broadcast, Multicast Briefkasten (mailbox), schwarzes Brett (black board) Briefkasten mit einem Empfänger (ports) (c) Synchronisation • asynchron: Sender braucht nicht auf Empfang der Daten zu warten – Kanalpuffer erforderlich ∗ beschränkter Puffer (evtl. Blockade durch vollem Puffer) ∗ unbeschränkter Puffer – gepuffertes Senden: Nachricht wird aus Sendepuffer in Systempuffer, bevor sie aufs Verbindungsnetzwerk geschrieben wird – ungepuffertes Senden: vom Sendepuffer ins Netz – nicht blockierendes Senden: Anstoß des Nachrichtenversandes mit direkter Weitergabe der Kontrolle an Nachfolgeinstruktionen – blockierendes Senden: wartet, bis Sendepuffer ausgelesen ist • synchron: Sender und Empfänger warten auf Kommunikation, keine Pufferung, direkte Übertragung (d) Sichtbarkeit und Direktionalität • symmetrisch: Kommunikationspartner kennen einander in gleicher Weise −→ meist datenorientierten Nachrichtenaustausch • asymmetrisch: Sender kennt Empfänger, aber nicht umgekehrt −→ meist aktionsorientierte Kommunikation Beispiel: Client / Server-Systeme C1 Au ftra Server braucht Client nicht zu kennen g S Cn Beispielsprachen: Occam (Vorläufer CSP Hoare 1978) • unidirektionale 1 - 1 Kanäle • symmetrische, synchrone Kanäle • statisches Kanalkonzept: Festlegung aller Kanäle zur Compilezeit • selektive Kommunikationskommandos −→ gleichzeitiges Warten auf mehreren Eingabekanälen 24 KAPITEL 3. GRUNDKONZEPTE PARALLELER PROGRAMME 3.3. SYNCHRONISATION UND KOMMUNIKATION ÜBER NACHRICHTEN ALT B1& I n p u t _ G u a r d _ 1 EXPR1 .. . Bn& I n p u t _ G u a r d _ n EXPRn in CSP: Sende- und Empfangsanw. in Guards P1 `B C1 BB B C3 P3 / P2 | | |~ | C2 P2 : ALT C1 ?X C2 !X EXPR1 EXPR2 Wie muss ein Protokoll aussehen, das diese Situation klärt und Verklemmungen vermeidet. Beispiel: Puffer in Occam • als Fließband Listing 5 Fließband Buffer 1 2 3 4 5 6 7 8 9 10 11 12 PROC buffer (CHAN OF INT source, sink) WHILE true INT local; SEQ source ? local sink ! local [n+1]CHAN OF INT stream; PAR producer(stream[0]) PAR index = 0 FOR n buffer (stream[n], stream[n+1]) consumer (stream(n+1)) • Paralleler Buffer 25 KAPITEL 3. GRUNDKONZEPTE PARALLELER PROGRAMME 3.3. SYNCHRONISATION UND KOMMUNIKATION ÜBER NACHRICHTEN Listing 6 Paralleler Buffer 1 2 3 4 5 6 7 8 9 10 11 12 13 PROC buffer ([n] CHAN OF INT source, sink) WHILE true INT local; PAR index = 0 FOR n SEQ source[index] ? local sink[index] ! local "TOP-LEVEL" [n]CHAN OF INT in, out PAR producer(in) buffer(in, out) consumer(out) Ada (1980, DoD) • synchrone Kommunikation • Rendezvous-Konzept • asymmetrisch • 1:1 SR / MPD • Modellierung verschiedener Konzepte • Kernkonzept: Operationen – Aufruf einer Operation ∗ asynchron send ∗ synchron call • Ausführung einer Operation – mittels proc −→ „eigener Prozess“ – mittels in −→ „bestehender Prozess“ Aufruf Ausführung proc in call Prozeduraufruf (auch remote) synchrone Komm. (rendezvous) Beispiel: Simulation von Semaphoren 26 send dynamische Prozesserzeugung (fork) asynchrone Kommunikation KAPITEL 3. GRUNDKONZEPTE PARALLELER PROGRAMME 3.3. SYNCHRONISATION UND KOMMUNIKATION ÜBER NACHRICHTEN Listing 7 Simulation von Semaphoren 4 sem s = e , op s(){send} int n=e for (i = 1 to n) send s(); 6 P(s) , receive s() 8 V(s) , send s() 1 2 3 #s nur mit send aufrufen #generiere n Eintrittskarten (tickets) #empfange Eintrittskarte Deklaration von Operationen in MPD op <name> (<params>) {<invocations>} send call send,call Deklaration von Prozess- und Prozedurrümpfen proc <name> ( <params>){ <body> } procedure → Operation mit call-Restriction process → Operation mit send-Restriction Auch Kanäle werden in MPD mit Operationen modelliert. Obige Operationsdeklaration ohne Implementierung wird als m : n Kanal betrachtet. Ein Aufruf „send <name> (<params>)“ entspricht dem nicht blockierenden Senden einer Nachricht. Mittels „receive <name> (<variables>)“ können Nachrichten aus dem Kanal <name> empfangen werden. Beispiel: (a) Mischen geordneter Folgen Listing 8 Mischen geordneter Folgen 1 2 3 4 5 6 7 8 9 10 11 12 13 send stream1(v) ... (ONE)\ send stream1(EOS) \ \ -----------(MERGE) receive stream1(x1) / receive stream2(x2) send stream2(v) / while(x1<EOS) or (x2<EOS){ ... (TWO)/ if(x1<=x2) { write(x1) send stream2(EOS) receive stream1(x1) } else{write(x2); receive stream2(x2);} } 27 KAPITEL 3. GRUNDKONZEPTE PARALLELER PROGRAMME 3.3. SYNCHRONISATION UND KOMMUNIKATION ÜBER NACHRICHTEN Beispiel: (b) Erzeuger- / Verbraucher Listing 9 Erzeuger- / Verbraucher ############ |−−−−−−−−−−−| ############ # p r o d u c e r # −−−−> | KANAL | −−−−> # c o n s u m e r # ############ |−−−−−−−−−−−| ############ | op b u f f e r ( i t e m _ t y p e ) { s e n d } | | | send b u f f e r ( item ) r e c e i v e b u f f e r ( item ) Fallstudie: Auflösen von Dreiecksgleichungssystemen Ax = b mit unterer Dreiecksmatrix A= a11 a21 .. . 0 a22 .. . ··· 0 .. . ··· ··· .. . 0 0 .. . an1 an2 an3 . . . ann , x = x1 .. , b = . xn Lösung: x1 = x2 = .. . xn = b1 a11 (b2 − a21 x1 ) a22 ³ bn − Pn−1 j=1 ann aij xj ´ yPipeline-Algorithmus Berechne x1 −→ Berechne x2 −→ . . . −→ Berechne xn −→ Ausgabe von x • elementares ’message passing’ , ’goto’ der parallelen Programmierung =⇒ Suche nach abstraktere Kommunikationskonstrukte typisches Kommunikationsmuster: P1(Client) P2(Server) send "Auftrag" to P2 < --> receive "Auftrag" from P1 > < < > > < < > > <-- send "Antwort" to P1 receive "Antwort" from P2 28 b1 .. . bn KAPITEL 3. GRUNDKONZEPTE PARALLELER PROGRAMME 3.3. SYNCHRONISATION UND KOMMUNIKATION ÜBER NACHRICHTEN RPC - remote procedure call P1 (Client) {call} service (input_args, result_args) (in MPD als proc) Aufruf einer Prozedur, die von einem anderen Prozess, der meist eigens generiert wird, ausgeführt wird. Implizit: • • • • Senden der Eingabeargumente / Parameter Ausführen des Auftrags (durch eigenen Prozess) Rücksenden der Ergebnisse Zuweisen an Variablenparameter. Beispiel: Stack Ressource, ggfs auf anderer virtueller / phys. Maschine: Listing 10 Stack Ressource 1 2 3 4 6 7 8 9 10 11 12 13 14 15 17 18 19 20 21 22 23 24 25 26 27 28 29 resource Stack{ type result= enum(OK, OVERFLOW, UNDERFLOW) op push(val int item) returns result r op pop(var int item) returns result r body Stack (int size) int store[1,...,size], int top = 0 proc push(item) returns r{ if (top<size){store[++top]=item, r=OK} else if (top==size){r= OVERFLOW} } proc pop (item) returns r{ ... item = ... } } resouce Stack_User() import Stack Stack.result x cap Stack s1, s2 int y s1 = create Stack(10); s2 = create Stack(20); [call] s1.push(25); s2.push(15); x=s1.pop(y) if(x != OK) {...} ... end Stack_User Beispiel: dynamische Pipeline zum sortieren durch Einfügen 29 KAPITEL 3. GRUNDKONZEPTE PARALLELER PROGRAMME 3.3. SYNCHRONISATION UND KOMMUNIKATION ÜBER NACHRICHTEN Jeder Worker generiert nach Bedarf zunächst seinen Nachfolger, der als Ergebnis seinen Eingabekanal zurückliefert. Anschließend wird die Liste der zu sortierenden Werte gelesen, der kleinste erhaltene Wert wird gespeichert und größere Werte werden weitergesendet. Nach Erhalt von i Werten sendet worker(i) den gespeicherten Wert mit der Position i über einen globalen Antwortkanal an den sort-Prozess zurück. Rückgabeanweisungen in MPD: - return Ende des Prozeduraufrufs Rückgabe der Resultate, Var.- par. - reply Kontroll- und Ergebnisrückgabe an aufrufenden Prozess; Fortsetzung der Prozedurbearbeitung Rendezvous: Bedienung des Clients durch bestehenden (Server-) Prozess; Vermeidung der Generierung eines separaten Prozesses P2 (Server): accept service (input_pars, var_params) → body →Verallgemeinerung synchroner Kommunikation Das Rendezvous-Konzept ist flexibler als der RPC. Beispiel: Bounded Buffer in Ada • Task Spezifikation – Deklaration des Namens und der Prozeduren – für alle Prozesse sichtbar Listing 11 Task Spezifikation 1 2 3 4 task buffer is entry Append (I: in Integer) entry Task (I: out Integer) end buffer; 30 (in MPD: in) KAPITEL 3. GRUNDKONZEPTE PARALLELER PROGRAMME 3.4. VERTEILTE PROGRAMMIERUNG IN MPD • Task Implementierung Definition der Prozeduren Listing 12 Task Implementierung 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 task body buffer is N: constant Integer := 100; B: array (0...N-1) of Integer; anfang (ende, anzahl : Integer := 0; begin loop select when anzahl < N => accept Append (I : in Integer) do B[ende]:= I end Append anzahl:= anzahl +1 ende:= (ende+1) mod N or when anzahl > 0 => accept Take (I : out Integer)... Die in-Anweisung in MPD verallgemeinert die select-/ accept- Konstrukte von Ada. Syntax in <op_command> [ ]. . .[ ] <op_command> ni mit <op_command> der From <operation> (<formal_id_list>) {return <result_id>} st <guard_expr> → <block> Ein Prozess, der eine in- Anweisung ausführt, wird suspendiert, bis eine der Operationen aufgerufen wird. Die Bedingungsausdrücke <guard_expr> dürfen Operationsparameter referenzieren. Die receive- Anweisung ist ein Spezialfall der in-Anweisung. receive op(v1, v2) wird impliziert durch: in op(p1, p2) → v1=p1; v2=p2 ni 3.4 Verteilte Programmierung in MPD Aufspaltung von Programmen in mehrere Addressräume sog. virtuelle Maschinen • dynamische Erzeugung 31 KAPITEL 3. GRUNDKONZEPTE PARALLELER PROGRAMME 3.4. VERTEILTE PROGRAMMIERUNG IN MPD • Platzierung auf spez. physikalischen Maschinen • transparente Komm. 1. Erzeugung virtueller Maschine cap vm c c = create vm()6 on exp78 Ressourcen müssen explizit auf VMen erzeugt werden create res_name(args) on c←−Verweis auf VM Globals werden implizit beim Importieren erzeugt. Jede VM erzeugt eigene Instanz importierter Globals. 2. Termination von VMs destroy expr destroy cap vm → Termination aller Ressourcen mit final code-Ausführung anschließend Terminierung aller Globals mit final code. 6 erzeugt Verweis auf virtuelle Maschine optional Platzierung 8 phy. Maschine als String (oder Integer) 7 32 KAPITEL 3. GRUNDKONZEPTE PARALLELER PROGRAMME 3.4. VERTEILTE PROGRAMMIERUNG IN MPD Listing 13 Termination von VMs 1 2 3 4 5 6 7 8 10 11 12 13 14 15 17 18 19 20 21 22 24 25 26 27 28 29 30 31 32 33 global glob int x=0; sem mutex=1; body glob final { write(x); } end resource test(int N, int n, cap () signal) import glob process p [i=1 to N] { P(mutex); x+=n; V(mutex); send signal() } end resouce main() import test const int N = 5; op done() cap vm vmcap cap test t1, t2 t1 = create test(N, 1, done) vmcap = create vm() on "oran" t2 = create test(N, 2, done) on vmcap for [i=1 to 2*N] { receive done() } destroy t1; destroy t2; destroy vmcap; end locate(n, hostname) mymachine myvm → Assoziation von Nummer mit Rechner hostname → liefert Nummer der eigenen Maschine → liefert Verweis auf virtuelle Maschine 33 KAPITEL 3. GRUNDKONZEPTE PARALLELER PROGRAMME 3.4. VERTEILTE PROGRAMMIERUNG IN MPD Listing 14 tin 1 2 3 4 5 6 7 8 9 11 12 13 14 resource main() import pipe const int n = 4 const int m = 10 const string[10] hosts[n] = ("maseru","harare","bamako","bukavu") cap vm vmcap[m] int inp op chan[1:m+1] (int) op ret (int) for [i=1 to n] { locate(i,hosts[i]) } for [i=1 to m] { vmcap[i] = create vm() on ((i-1) mod n)+1; write(hosts[((i-1) mod n)+1]," bereit") } 21 write("Bitte Werte eingeben") for [i=1 to m] { read(inp); send chan[1](inp) } for [i=1 to m] { create pipe(i,m,chan[i],chan[i+1],ret) on vmcap[i] } for [i=1 to m] { receive chan[m+1](inp); writes(inp," ") } # for [i=1 to m] { receive ret(inp); writes(inp," ") } write() 23 end main 16 17 18 19 20 Listing 15 tin2 1 2 3 5 6 7 resource pipe(int i, int m, cap(int) inp, cap(int) out, cap(int) result) int my_el int value process test { receive inp(my_el) for [j= 1 to m-i] { 8 9 10 12 write("Prozess ",i," ermittelte Element ",my_el) 14 for [j = 1 to i-1] { 15 16 17 send out(my_el) 19 # send result(my_el) } end 20 21 34 receive inp(value); if (my_el <= value) { send out(value) } else { send out(my_el); my_el = value } } receive inp(value) send out(value) } 4 Die Bibliothek MPI • MPI – Message Passing Interface (de-facto Standard) • MPI-Forum = ca. 40 Organisationen aus Industrie, Universitäten und Forschungslabors – 1992 Gründung auf Supercomputing Conference – 1994 MPI-1 – 1997 MPI-2 ∗ http://www.mpi-forum.org • Ziele: – – – – – – Quellcodeportabilität Kontinuität der parallelen Programmentwicklung Effizienz Flexibilität, Funktionalität ( > 128 Funktionen ) C, C++, Fortran Anbindung Unterstützung heterogener Architekturen • In Übungen: LAM-MPI 4.1 Grundkonzepte • SPMD-Programmstruktur • 6 Basisfunktionen → → → → → → MPI_Init MPI_Finalize MPI_Comm_rank MPI_Comm_size MPI_Send MPI_Recv - Initialisierung Terminierung Prozess-ID abfragen #Prozesse bestimmen Senden von Nachrichten Empfangen von Nachrichten Listing 4.1: The „skeleton“ of an MPI program in C 1 #include "mpi.h" 35 KAPITEL 4. DIE BIBLIOTHEK MPI 4.1. GRUNDKONZEPTE 3 4 5 main(int argc, char **argv) { int my_rank, nprocs MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); 7 8 9 . . . 11 12 13 MPI_Finalize(); 15 16 } int MPI_Init(int *argc, char **argv) z }| { Adressen der Argumente von main Initialisiert die MPI-Umgebung und definiert den Kommunikator MPI_COMM_WORLD. Ein Kommunikator definiert eine Gruppe von Prozessen und einen Kommunikationskontext. int MPI_Comm_size(MPI_Comm comm, int *size) ← in ← out ermittelt die Anzahl der Prozesse im Kommunikator comm int MPI_Comm_rank(MPI_Comm comm, int *rank) bestimmt Identifikation eines Prozesses innerhalb eines Kommunikators. Prozesse werden von 0 bis #P rozesse− 1 nummeriert. int MPI_Finalize() beendet MPI int MPI_Send(void *buf, int count, MPI_datatype datatypes, int dest, int tag, MPI_Comm comm) int MPI_Recv(void *buf, int count, MPI_datatype datatypes, int source, int tag, MPI_Comm comm, MPI_Status *status) 36 \ > zu übertragende / Daten → Rang des Empfängers → Kennung → Kommunikator \ > zu empfangende Daten / → Rang des Empfängers → Kennung → Kommunikator → Quelle, Tag, Anz. tatsächlich empfangener Daten KAPITEL 4. DIE BIBLIOTHEK MPI 4.2. KOMMUNIKATION Beim Nachrichtenempfang können mittels MPI_ANY_TAG und MPI_ANY_SOURCE Nachrichten mit beliebigem Tag bzw. von beliebigem Sender empfangen werden. −→ VORSICHT: Nichtdeterminismus In diesem Fall kann man mit dem Status Argument die tatsächlichen Werte abfragen. Felder: → status.MPI_SOURCE → status.MPI_TAG → in → in → out int MPI_Get_count(MPI_Status *status, MPI_DATATYPE type, int *count) liefert Anzahl tatsächlich empfangener Daten Listing 4.2: MPI pairwise interactions program 1 #include "mpi.h" /* Include file */ 3 main(int argc, char *argv[]) { int myid, np, ierr, lnbr, rnbr; real x[300], buff[300], forces[300]; MPI_Status status; /* Main program */ 4 5 6 16 ierr = MPI_Init(&argc, &argv); if(ierr != MPI_SUCCESS) { fprintf(stderr,"MPI initialization error\n"); exit(1); } MPI_Comm_size(MPI_COMM_WORLD, &np); MPI_Comm_rank(MPI_COMM_WORLD, &myid); lnbr = (myid+np-1)%np; rnbr = (myid+1)%np; 18 initialize(x, buff, forces); 20 for (i=0; i<np-1; i++) { /* Circulate messages */ MPI_Send(buff, 300, MPI_FLOAT, rnbr, 0, MPI_COMM_WORLD); MPI_Recv(buff, 300, MPI_FLOAT, lnbr, 0, MPI_COMM_WORLD, &status); update_forces(x, buff, forces); } 8 9 10 11 12 13 14 15 21 22 23 24 25 print_forces(myid, forces); MPI_Finalize(); 27 28 29 /* Initialize */ /* Check return code */ /* /* /* /* Number of procs */ My process id */ Id of left neighbor */ Id of right nbr */ /* Print result */ /* Shutdown */ } 4.2 Kommunikation MPI unterscheidet (a) Punkt-zu-Punkt-Kommunikation 37 KAPITEL 4. DIE BIBLIOTHEK MPI 4.2. KOMMUNIKATION (b) kollektive Kommunikation • ad (a) Kommunikationsmodi 4 Arten von Sendefunktionen Standard: gepuffert: synchron: ready: MPI_Send (globale Operation) MPI_BSend (lokale Operation) Nachricht wird gepuffert, damit das Senden unabhängig vom Empfangen abgeschlossen werden kann. MPI_SSend (globale Operation) Senden endet, wenn Datenempfang begonnen wurde. MPI_RSend (locale Operation) Sendender Prozess sendet sofort. Es muss gewährleistet sein, dass der Empfänger zum Empfang bereit ist. → Leistungssteigerung Diese 4 Sendearten können blockierend oder nicht-blockierend sein. nicht-blockierend: – der Sendevorgang wird initiiert – Überlappung von Kommunikation und Berechnungen MPI_ISend MPI_ISSend MPI_IBSend MPI_IRSend Es gibt nur eine Empfangsoperation MPI_Recv, die auch nicht-blockierend sein kann (MPI_IRecv) explizite Pufferverwaltung: Bereitstellen: MPI_Buffer_attach(void* buffer, int size) Freigabe: MPI_Buffer_detach(void* buffer, int size) Bei nicht-blockierenden Sende-/Empfangsroutinen wird über ein zusätzliches Ausgabeargument: MPI_Request *request getestet werden, ob die Anweisung abgeschlossen ist: MPI_Wait(request, status) ← warten bis abgeschlossen MPI_Test(request, flag, status) ← Kontrolle kommt zurück • ad (b) kollektive Kommunikationsoperatoren Typen: – globale Barrieren – globale Datenbewegungen – Reduktionen 38 KAPITEL 4. DIE BIBLIOTHEK MPI 4.2. KOMMUNIKATION MPI_Barrier(MPI_Comm comm) → globale Synchronisation aller Prozesse in comm MPI_Bcast(void* inbuf, int count, MPI_Datatype type, int root, MPI_Comm comm) → Broadcast von Prozess root an alle anderen Prozesse in comm MPI_Gather("Eingabepuffer", "Ausgabepuffer", int root, MPI_Comm comm) MPI_Scatter("Eingabepuffer", "Ausgabepuffer", int root, MPI_Comm comm) MPI_Reduce(void* inbuf, void* outbuf, int count, MPI_Datatypes type, MPI_Op op, int root, MPI_Comm comm) MPI_Allreduce(void* inbuf, void* outbuf, int count, MPI_Datatypes type, MPI_Op op, MPI_Comm comm) – Mögliche Operatoren MPI_SUM MPI_PROD MPI_MAX, MPI_MIN MPI_LOR, MPI_LAND, MPI_LXOR MPI_BOR, MPI_BAND, MPI_BXOR Synchronisierung am Ende Beispiel: Berechnung von π durch Integration Z 1 0 4 dx = π 1 + x2 Listing 4.3: Berechnung von π durch Integration 1 2 3 4 5 6 7 8 10 #include "mpi.h" #include <stdio.h> #include <math.h> int main( int argc, char *argv[] ) { int n, myid, numprocs, i; double PI25DT = 3.141592653589793238462643; double mypi, pi, h, sum, x; MPI_Init(&argc,&argv); 39 KAPITEL 4. DIE BIBLIOTHEK MPI 4.2. KOMMUNIKATION MPI_Comm_size(MPI_COMM_WORLD,&numprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myid); while (1) { if (myid == 0) { printf("Enter the number of intervals: (0 quits) "); scanf("%d",&n); } MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD); if (n == 0) break; else { h = 1.0 / (double) n; sum = 0.0; for (i = myid + 1; i <= n; i += numprocs) { x = h * ((double)i - 0.5); sum += (4.0 / (1.0 + x*x)); } mypi = h * sum; MPI_Reduce(&mypi, &pi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (myid == 0) printf("pi is approximately %.16f, Error is %.16f\n", pi, fabs(pi - PI25DT)); } } MPI_Finalize(); return 0; 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 } Beispiel: • eindimensionales Temperaturproblem • Glätten von Bilddaten allgemeine Beschreibung: Vektor X = (X0 , . . . , XN −1 ) Berechne X (t) mit X (0) = X (t+1) Xi (t) = (t) (t) Xi−1 +2Xi +Xi+1 4 mit 0 ≤ i ≤ N − 1 0 ≤ t ≤ T − 1 →Algorithmus: Iteration mit abwechselnden Kommunikationen und Berechnungen Listing 4.4: Outline of an MPI finite difference algorithm 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 40 main(int argc, char *argv[]) { MPI_Comm com = MPI_COMM_WORLD; MPI_Init(&argc, &argv); MPI_Comm_size(com, &np); MPI_Comm_rank(com, &me); if (me == 0) { read_problem_size(&size); buff[0] = size; } /* Global broadcast propagates this data to all processes */ MPI_Bcast(buff, 1, MPI_INT, 0, com); /* Extract problem size from buff; allocate space for local data */ lsize = buff[0]/np; local = malloc(lsize+2); /* Read input data at process 0; then distribute to processes */ KAPITEL 4. DIE BIBLIOTHEK MPI 4.3. KOMMUNIKATOREN, PROZESSGRUPPEN UND TOPOLOGIEINFORMATIONEN if (me == 0) { work = malloc(size); read_array(work); } MPI_Scatter(work, lsize, MPI_FLOAT, local+1, lsize, MPI_FLOAT, 0, com); lnbr = (me+np-1)%np; /*Determine my neighbors in ring */ rnbr = (me+1)%np; globalerr = 99999.0; while (globalerr > 0.1) { /* Repeat until termination */ /* Exchange boundary values with neighborts */ ls = local+lsize; MPI_Send(local+2, 1, MPI_FLOAT, lnbr, 10, com); MPI_Recv(local+1, 1, MPI_FLOAT, rnbr, 10, com, &status); MPI_Send(ls-2, 1, MPI_FLOAT, rnbr, 20, com); MPI_Recv(ls-1, 1, MPI_FLOAT, lnbr, 20, com, &status); compute(local); localerr = maxerror(local); /* Determine local error */ /* Find maximum local error, and replicate in each process */ MPI_Allreduce(&localerr, &globalerr, 1, MPI_FLOAT, MPI_MAX, com); } /* Collect results at process 0 */ MPI_Gather(local, lsize, MPI_FLOAT, work, size, MPI_FLOAT, 0, com); if (me == 0) { write_array(work); free(work); } MPI_Finalize(); 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 P0 P0 } −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− | lsize | lsize | . . . . . | lsize | −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− || || || \/ \/ \/ −−−−−− −−−−−− −−−−−−−−− | P0 | | P1 | | Pnp−1 | −−−−−− −−−−−− −−−−−−−−− || || || \/ \/ \/ −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− | lsize | lsize | . . . . . | lsize | −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− ( G r a f i k s t i m m t NICHT ) alternative Realisierung des Datenaustauschs MPI_Irecv(ls+1, 1, MPI_FLOAT, rnbr, 10, com, &r1) MPI_Rsend(local, 1, MPI_FLOAT, lnbr, 10, com) MPI_Irecv(local, 1, MPI_FLOAT, lnbr, 20, com, &r2) MPI_Rsend(ls, 1, MPI_FLOAT, rnbr, 20, com) ¾ MPI_Wait (r1, &status) MPI_WAITALL (2, req, status) | {z } MPI_Wait (r2, &status) Felder compute (local) 4.3 Kommunikatoren, Prozessgruppen und Topologieinformationen in fast allen Kommunikationsbibliotheken 41 KAPITEL 4. DIE BIBLIOTHEK MPI 4.3. KOMMUNIKATOREN, PROZESSGRUPPEN UND TOPOLOGIEINFORMATIONEN • Nachrichtenkennungen (tags) • Prozessgruppen neu in MPI: • Kommunikatoren – bessere Kapselung von Bibliotheksfunktionen – Erleichterung des Umgangs mit Prozessgruppen und virtuellen Topologie Ein Kommunikator • bestimmt eine Gruppe von Prozessen das heißt eine geordnete Menge von Prozessen mit lokalem Rang ∈ {0, . . . , #P rozesse − 1} • definiert einen Kontext für Kommunikationen → Einführung separater, sicherer d. h. sich nicht beeinflussender Universen zum Nachrichtenaustausch Beispiel: Was geschieht, wenn P2 verzögert wird? DEADLOCK 42 KAPITEL 4. DIE BIBLIOTHEK MPI 4.3. KOMMUNIKATOREN, PROZESSGRUPPEN UND TOPOLOGIEINFORMATIONEN Vordefinierte Kommunikatoren: MPI_COMM_WORLD MPI_COMM_SELF MPI_COMM_NULL "Gruppe aller Prozesse" "Prozess selbst" "ungültiger Kommunikator" 4.3.1 Prozessgruppen: Konstruktion, Analyse, Manipulation MPI_Comm_group(MPI_Comm comm, MPI_Group *group) liefert Prozessgruppe zu Kommunikator comm MPI_Group_union(MPI_Group group1, MPI_Group group2, MPI_Group *group) MPI_Group_intersection(MPI_Group group1, MPI_Group group2, MPI_Group *group) MPI_Group_difference(MPI_Group group1, MPI_Group group2, MPI_Group *group) Vereinigung und Schnitt sind assoziativ, aber wegen der Prozessordnung nicht kommutativ. → leere Gruppe MPI_GROUP_EMPTY MPI_Group_size(MPI_Group group, int *size) MPI_Group_rank(MPI_Group group, int *rank) Falls Prozess nicht in Gruppe, Ergebnis MPI_UNDEFINED MPI_Group_incl(group, n, ranks, newgroup) MPI_Group_excl(group, n, ranks, newgroup) ← ← ← ← in out in out erzeugt eine neue Gruppe mit n Prozessen, die in group die Ränge ranks [0] . . . ranks [n − 1] haben und in newgroup die Ränge 0 . . . n − 1 → auch Umordnung von Prozessen in Gruppe möglich Die excl-Variante streicht die durch ranks angegebenen Prozesse aus group. MPI_Group_free(group) deallokiert Prozessgruppe 43 KAPITEL 4. DIE BIBLIOTHEK MPI 4.3. KOMMUNIKATOREN, PROZESSGRUPPEN UND TOPOLOGIEINFORMATIONEN 4.3.2 Kommunikatoren Neue Kommunikatoren können aus bestehenden Kommunikatoren oder Prozessgruppen gebildet werden. MPI_Comm_dup(MPI_Comm comm, MPI_Comm *newcomm) → neuer Kommunikatoren mit derselben Prozessgruppe, aber neuem Kontext. MPI_Comm_create(comm, group, newcomm) ← in ← out group muss Teilmenge der Prozessgruppe von comm sein. MPI_Comm_split(comm, color, key, ← in newcomm) ← out Aufteilung der Prozessgruppe von comm in disjunkte Teilgruppen gemäß color, Ordnung innerhalb der Teilgruppen gemäß key, bei identischen Schlüsseln Ordnung aus comm. MPI_Comm_free(MPI_Comm *comm) (A) MPI_Comm comm, newcomm; int myid, color; MPI_Comm_rank(comm, &myid); color = myid % 3; MPI_Comm_split(comm, color, myid, &newcomm) (B) Master-/ Worker-Schema mit separatem Kommunikator für Worker-Prozess 44 KAPITEL 4. DIE BIBLIOTHEK MPI 4.3. KOMMUNIKATOREN, PROZESSGRUPPEN UND TOPOLOGIEINFORMATIONEN Abschlusskriterium: Genauigkeit der Approximation < ε1 → MPI_ALLREDUCE auf Worker P0 P1 l i b _ c a l l ( comm_b ) l i b _ c a l l ( comm_b ) MPI_RECV . . . <−−−−−−−− MPI_SEND . . . MPI_RECV . . . <−−\ MPI_BARRIER \ 2 MPI_BARRIER l i b _ c a l l ( comm_b ) \ l i b _ c a l l ( comm_b ) MPI_RECV . . . \ MPI_SEND . . . MPI_RECV . . . P2 l i b _ c a l l ( comm_b ) MPI_SEND . . . MPI_BARRIER l i b _ c a l l ( comm_b ) MPI_SEND . . . 4.3.3 Virtuelle Topologien Unterstützung von festen Kommunikationsstrukturen • effizientere Abbildung auf physikalische Zielmaschine • einfachere Benennung von Prozessen • Verbesserung der Lesbarkeit von Programmen zwei Arten: • kartesische Topologien – Gitter, Würfel, Torus, Hypercube • Graphentopologie feste Assoziation mit Kommunikator Erzeugung führt zu neuem Kommunikator MPI_Cart_create( MPI_Comm comm_old, int ndims, ←− #Dimensionen [int] dims, ←− Ausdehnung in den Dimensionen [bool] periods, ←− Boolsche Werte, die pro Dimensionen zyklische Verbindungen erlauben oder nicht bool reorder, ←− False verbietet die Umordung von Prozessen bzgl. der Ränge MPI_Comm *comm_cart) 1 2 Parameter back-masking Problem 45 KAPITEL 4. DIE BIBLIOTHEK MPI 4.3. KOMMUNIKATOREN, PROZESSGRUPPEN UND TOPOLOGIEINFORMATIONEN Beispiel: dims[0] = 4 ndims = 2 dims[1] = 3 periods[0]= periods[1]= false reorder = true Dim 0↓ 1→ −− (0, 0)0 (1, 0)3 (2, 0)6 (3, 0)9 (0, 1)1 (1, 1)4 (2, 1)7 (3, 1)10 (0, 2)2 (1, 2)5 (2, 2)8 (3, 2)11 Die Kommunikation wird nicht auf diese Topologie eingeschränkt Hypercube: n−dim Torus (zyklisch) mit je genau 2 Proz. pro Dimension 4.3.4 Zugriffsroutinen ←− in ←− out MPI_Cart_rank(comm, coords, rank) comm - Kommunikator mit kartesischer Topologie MPI_Cart_coords(comm, rank, maxdims, coords) ←− in ←− out coords - Koordinaten zu Prozess mit Rang rank MPI_Cart_shift(comm, direktion, disp, rank_source, rank_dest) ←− in ←− out direktion - Dimension für shift disp - #shif t − pos Beispiel: comm mit zwei dimensionaler Torusstruktur und Feld a, dass elementweise verteilt ist. Verschieben der i−ten Spalte um i Elemente (Rotieren) MPI_Comm_rank(comm, &rank); MPI_Cart_coords(comm, rank, maxdims, &coords); MPI_Cart_shift(comm, 0, coords[1], &source, &dest); MPI_Sendrecv_replace3 (a, 1, MPI_REAL, dest, 0, source, 0, comm, &status); Bestimmen einer balancierten Gitterstruktur zu einem Kommunikator MPI_Dims_create(nnodes, ndims, dims) 3 Senden und Empfangen auf dem selben Datenbereich 46 ←− in ←− out KAPITEL 4. DIE BIBLIOTHEK MPI 4.4. ABGELEITETE DATENTYPEN Im Feld dims können Felder vorbesetzt werden. Dabei muss nnodes Vielfaches von Y dims [i] i mit dims[i]6=0 sein. Beispiel: dims vor Aufruf (0,0) (0,3,0) Aufrufparameter (6,2,dims) (7,2,dims) (6,3,dims) (7,3,dims) dims nach Aufruf (3,2) (7,1) (2,3,1) ERROR 4.3.5 Interkommunikatoren bisher: Intrakommunikator −→ Kommunikator innerhalb von Prozessgruppen Interkommunikatoren dienen der Kommunikation zwischen disjunkten Prozessgruppen. Sie erlauben nur Punkt-zu-Punkt Kommunikation. Erzeugung über MPI_Intercomm_create 4.4 Abgeleitete Datentypen Nachrichten Abbildung 4.1: Zusammenhängender Puffer Um nicht zusammenhängende Speicherbereiche bzw. Objekte mit unterschiedlichen Teiltypen zu versenden, können eigene Datentypen definiert werden. 47 KAPITEL 4. DIE BIBLIOTHEK MPI 4.4. ABGELEITETE DATENTYPEN 4.4.1 Spezifikation neuer Datentypen Eine Typabbildung (type map) ist eine Folge von Paaren der Form: < typei , dispi |0 ≤ i < n > wobei typei Basistypen und dispi ganze Zahlen / relative Adressen (displacements) sind. Die Folge der Typen < typei |0 ≤ i < n > heißt Typsignatur der Typabbildung. Mit einer Basisadresse buf spezifiziert eine Typabbildung einen Komm.- Puffer mit n Einträgen. Eintrag i beginnt an Adresse buf + dispi und hat Typ typei . Beispiel: MPI_INT hat Typabb. <(int, 0)> <(int, 0), (char, 4)> bezeichnet [0 ---------- int ------------][4-char-] MPI_Type_Contignous(count, ←− ⇒ 0 oldtype, newtype) ←− MPI_Datatype MPI_Send(buf, count, type,. . .) MPI_Type_Contignous(count, type, newtype) MPI_Send(buf, 1, newtype,. . .) MPI_Type_vector(count, blocklength, stride, oldtype, newtype) Beispiel: count=2, blocklength=3, stride=5 oldtype [//] newtype [//][//][//][--][--][//][//][//][--][--] 1.Block 2.Block -----------------Schrittweite (Stride) Beispiel: Sende 5−te Spalte einer Zeilenweise gespeicherten Matrix. double results [IMAX][JMAX], MPI_Datatype col, MPI_Type_vector(IMAX, 1, JMAX, MPI_DOUBLE, &col) MPI_Send(&result4 [0][4], 1, col,. . .) 4 5te Spalte 48 5 Parallele Algorithmen 5.1 Das PRAM- Rechnermodell PRAM: Parallel Random Access Machine (F ORTUNE , W YLLIE 1978) einfach, unrealistisch, da Vernachlässigung von Interprozessorkommunikation, konstanter Zugriff auf globalen Speicher für alle Prozessoren → SIMD (Single Instruktion Multiple Data) Modell Abbildung 5.1: Aufbau einer PRAM Arbeitsweise: • Ein- / Ausgabe über den globalen Speicher • Die Berechnung beginnt mit einem einzelnen aktiven PE. • In einem Berechnungsschritt kann ein aktives PE ein anderes PE aktivieren – eine einzelne RAM Operation: ALU-Ops, Sprünge,. . . – einen lokalen oder globalen Speicherplatz lesen oder schreiben – Alle aktiven PEs führen dieselbe Instruktion aus. • Die Berechnung terminiert, wenn der letzte aktive Prozessor stoppt. Kosten einer PRAM-Berechnung #Proz ∗ parallele Zeitkomplexität z. B. Θ (p log p) Speicherorganisation: 49 KAPITEL 5. PARALLELE ALGORITHMEN 5.1. DAS PRAM- RECHNERMODELL • EREW (Exclusive Read Exclusive Write) keine Lese- / Schreibkonflikte erlaubt • CREW (Concurrent Read Exclusive Write) gleichzeitiges Lesen erlaubt, gleichzeitiges Schreiben nicht „Default-Modell“ • CRCW (Concurrent Read Concurrent Write) gleichzeitiges Lesen und Schreiben erlaubt Auflösen von Schreibkonflikten bei CRCW: • COMMON: Alle in eine Speicherposition schreibenden Proz. schreiben den selben Wert • ARBITRARY: zufällige Auswahl • PRIORITY: Der Proz. mit kleinstem Index darf schreiben. Satz 1.1: Ein p-Proz. CRCW-PRIORITY-PRAM kann durch eine p-Proz. EREW-PRAM simuliert werden. Dabei wird die Zeitkomplexität um einen Faktor (log p) erhöht. Beispiel: PRAM mit N = 2k Prozessoren In einem unsortierten Datenbereich mit n ≥ N Plätzen soll ein Element x gesucht werden. sequentieller Algorithmus: lineare Suche worst case: n Schritte average case: n 2 Schritte EREW-PRAM: Sei Pi der i-te Proz. 0 ≤ i ≤ N − 1 Vorphase: Aktivierung der N Proz. und Broadcast des Wertes x (wegen ER) → log N Schritte Berechnungsphase: Aufteilung des Datenbereichs in N Teilbereiche der Größe (n div N ) bzw. (n div N ) + 1. Jeder Prozessor Pi , 0 ≤ i ≤ N − 1, durchläuft im Gleichtakt mit den anderen Prozessoren seinen Teilbereich und sucht x. Was geschieht, wenn x von einem Prozessor gefunden wird? Jeder Prozessor schreibt sein Endsignal in einen eigenen Platz im Speicher. Nach jedem Vergleichschritt wird eine globale Reduktion mit logischer Oder-Verknüpfung der Endsignale durchgeführt. Im Schritt j, (0 ≤ j ≤ k − 1) wenden die Prozessoren Pi , 0 ≤ i ≤ 2k − 1 − j die Verknüpfung auf ihr eigenes Endsignal und das von Pi+2k−1−j an und schreiben das Ergebnis in die Speicherzelle von Pi 50 KAPITEL 5. PARALLELE ALGORITHMEN 5.1. DAS PRAM- RECHNERMODELL P0 P1 P7 Anschließend Broadcast des Ergebnis in k-Schritten → worst case Zeitkomplexität lnm log N + ∗ (1 + 2 log N ) N CREW-PRAM Anfängliches Broadcast des Wertes x Statt Reduktion / Broadcast der Endsignale integriertes Butterfly-Schema XXXXXXXXXXXXXXXXXXXXXXfX..fXfff fffff fffff fffff XXXXXXXXXXfXfXfXfXfXfXfX. fXfXfXfXfXfXfXffffffffff fXfXfXfX XfXfXfXf fXfXfXfX XfXfXfXf ffffffffffXfXfXfXfXfXfXfXfXfXfXfXfXfXfXXXXXXXXXX fQffffffQfffffffffffffffXfXfXXX XXQXXX XXQXXX XXXXX QQQ mQmQmQ mmm QQQ mQmQmQ mmm Qm Qm mQmQmQQQmmmQmQmQQQ m mmmQQQ mmmQQQQQ m Q mm mm Q mmm mmQmQ CC { CC { CC { CC { C{C{ {C{CC {C{CC {C{CC { { { { { { {{ C → worst case Zeitkomplexität 1+ lnm N ∗ (1 + log N ) CRCW-PRAM gemeinsames Schreiben der Endsignale in globalen Speicher. → worst case Zeitkomplexität lnm ∗ (1 + 1) 1+ N Elementare PRAM-Algorithmen • Broadcast / Reduktion – → ⌈log p⌉ Schritte für p Prozessoren Reduktion von n Werten mit assoziativen binären Operationen auf Θ (log n) Schritten n 2 Prozessoren mit • Präfixsummen (Scans) – für n Werte in ⌈log n⌉ parallelen Schritten (n − 1 Prozessoren) 51 KAPITEL 5. PARALLELE ALGORITHMEN 5.1. DAS PRAM- RECHNERMODELL Gegeben: n Werte a0 , . . . , an−1 und assoziative Operation ⊕ a0 a0 Bestimme n Werte: a0 ⊕ ⊕ a1 a1 .. . ⊕ a2 a0 ⊕ a1 ⊕ a2 ⊕ ··· ⊕ an−1 CREW-Verfahren: • globale Variablen: n, A [0 . . . (n − 1)] , j • Anfangsbedingung: A [0 . . . (n − 1)] enthält Eingabeliste • Endbedingung: A [i] enthalte a0 ⊕ . . . ⊕ ai 1 2 3 4 5 6 7 8 9 10 begin spawn(P1 , . . . , Pn−1 ) for all Pi where 1 ≤ i ≤ n − 1 do for j=0 to ⌈log n⌉ − 1 do `` ´ ´ if i − 2j ≥ 0 then ˆ ˜ A [i] ← A [i] ⊕ A i − 2j fi od od end Beispiel: A 0 P1 P2 P3 P4 P5 P6 1 2 3 4 5 6 0 EE 4 EE 2 EE 6 EE 5 1 EE EE EE EE EE EE BB EE BB EE EE EE EE j=0 EE BB ² EE ² EE ² EE ² EE ² ¹ " ² ! " " " " 11 3 QQQQ 4 RRRR 1 RRRR 4 RRRR 6 RRRR 8 RRR RRR QQQ RRR RRR Q R R R R Q R R RR RR QQQ R R j=1 QQQ ² RRRRR ² RRRRR ² RRRRR ² RRRRR ² ¹ Q( R( R( ( ( 3 XXXXXXX4XXXXXXXX4X XXXXXXX8X 12 17 7 XXXXX XXXXXX XXXXXX X X X X X X X X X X X X X X X j=2 XXXXX XXXXXXXXXXXX² XXXXXXXXXX² XXX ¹ XXX, XXX, XXX, ² 3 BB Schritt 1 Schritt 2 Schritt 3 3 4 4 8 10 16 21 Kosten: n − 1 Prozessoren, Θ (log n) Schritte Definition: Ein paralleler Algorithmus heißt kostenoptimal, wenn seine Kosten (#Proz. * parallele Laufzeit) in derselben Komplexitätsklasse liegen wie ein optimaler sequenzieller Algorithmus. Analyse der elementaren Verfahren • Reduktion – parallele Kosten: Θ (n log n) (nicht optimal!) 52 KAPITEL 5. PARALLELE ALGORITHMEN 5.1. DAS PRAM- RECHNERMODELL – optimaler sequenzieller Algorithmus: Θ(n) – Anzahl der Operationen: P n Plog(n−1) i −i = par. Algorithmus: log 2 =n−1 i=1 n · 2 i=0 seq. Algorithmus: n − 1 • Präfixsummen – par. Kosten Θ (n · log n) (nicht optimal) opt. seq. Alg. Θ (n) – Anzahl der Operationen ¢ P⌈log n⌉−1 ¡ n − 2i · 1 = . . . = Θ (n log n) par. Alg.: i=0 seq. Alg.: n − 1 Satz 1.2 (B RENT 1974): Sei A ein paralleler Algorithmus mit Ausführungszeit t. Falls A m-Operationen ausführt, so kann A mit p-Prozessoren in der Zeit t + (m−t) ausgeführt werden. p Beweis Satz Pt 1.2. Sei si die Anzahl der Operationen die im i-ten Schritt ausgeführt werden, 1 ≤ i ≤ t. Es gilt: i=1 si = m l m Mit nur p Prozessoren kann der i-te Schritt von A in Anzahl der Schritte mit p Prozessoren: t » ¼ X si i=1 p ≤ t X si + p − 1 i=1 p si p =t+ Schritten simuliert werden. Damit folgt für die t X si − 1 i=1 p =t+ m−t p • Um ein kostenoptimales Verhalten zur parallelen Reduktion zu bestimmen, kann man versuchen, die Anzahl Prozessoren zu reduzieren. n−1 • Um n − 1 Operationen in log n Schritten kostenoptimal auszuführen, sollten nur p = log n = ´ ³ Θ logn n Prozessoren eingesetzt werden. Mit Brents Theorem folgt, dass sich die parallele Laufzeit wie folgt erhöht: µ ¶ n − 1 − ⌈log n⌉ log n log2 n k j ⌈log n⌉ + = Θ log n + log n − − = Θ (log n) n n n log n =⇒ Die Komplexitätsklasse des parallelen Algorithmus wird durch j die k Reduktion der Pron zessorzahl nicht verändert, d. h. die parallele Reduktion auf log n Prozesse ist kostenoptimal. • Zur Herleitung eines kostenoptimalen Algorithmus für die Präfixsummenberechnung genügt es kaum, die Anzahl der Prozessoren zu reduzieren. Besser ist es den Berechnungen der Prozessoren auf den ihnen zugeordneten Datenseqmenten das optimale sequenzielle Verfahren einzusetzen. • Berechnung der Präfixsummen von n Werten mit p < n − 1 Prozessoren 53 KAPITEL 5. PARALLELE ALGORITHMEN 5.2. RECHNERMODELLE MIT VERTEILTEM SPEICHER – Aufteilung der n Werte in p Teilbereiche mit max l m n p Werten – Die p Prozessoren rechnen mit dem optimalen sequenziellen Verfahren die Präfixsummen auf ihrenlTeilbereichen m −→ np − 1 Schritte – Die ersten p−1 Prozessoren berechnen die Präfixsummen der Gesamtsummen ihrer Teilbereiche mit dem parallelen Verfahren: −→ ⌈log (p − 1)⌉ Schritte. – Die letzten p − 1 Prozessoren addieren die Gesamtsummen der niedrigeren Blöcke auf alle Elemente l m ihrer Teilbereiche: −→ np Additionen l m ´ l m ³ Gesamtaufwand: np − 1 + ⌈log (p − 1)⌉ + np = Θ np + log p Beispiel: n = 14, ⌈log n⌉ = 4, p = 4 A 2 1 4 −3 0 −2 5 1 −1 2 4 0 3 7 Schritt(iii) 2 3 7 0 −2 3 1 5 10 3 7 4 2 7 7 9 13 5 9 13 13 3 2 4 8 8 8 −1 (iv) 4 4 4 4 16 23 Gesamtzahl der Operationen: µ» ¼ ¶ » ¼ n n p· − 1 + Θ (p log p) + (p − 1) · = Θ (n + p log p) p p ³ ³ ´ dann folgt der Gesamtaufwand: Θ nn + log logn n = Θ (log n) log n ´ ³ =⇒ Kosten: Θ logn n · Θ (log n) = Θ (n) =⇒ Kostenoptimalität ´ ³ Gesamtzahl der Operationen: Θ n + logn n log logn n = Θ (n) Sei: p = Θ n log n ´ 5.2 Rechnermodelle mit verteiltem Speicher • bestimmendes Element: Verbindungsnetzwerk – feste Knoten-zu-Knoten-Verbindungen • Bewertungskriterien: – Durchmesser , längster Abstand zwischen zwei Knoten 54 KAPITEL 5. PARALLELE ALGORITHMEN 5.2. RECHNERMODELLE MIT VERTEILTEM SPEICHER – Halbierungsbreite , minimale Anzahl von Verbindungslinien, die durchtrennt werden, um das Netzwerk in zwei etwa gleichgroße Teile zu zerlegen. – Verbindungsgrad , Anzahl der Verbindungen pro Knoten Beispiel: • Gitter mit q Dim. und k Knoten pro Dim. −→ k q Knoten Durchmesser: (k − 1) ∗ q Halbierungsbreite: k (q−1) Verbindungsgrad: 2q • Torus , Gitter mit zyklischen Verbindungen in allen Dimensionen ¥ ¦ Durchmesser: q ∗ k2 Halbierungsbreite: 2 ∗ k (q−1) Verbindungsgrad: 2q • Binärbaum der Tiefe k → 2k+1 − 1 Knoten Durchmesser: 2k Halbierungsbreite: 1 Verbindungsgrad: 3 • Hypercube der Dimension k Durchmesser: k Halbierungsbreite: 2(k−1) Verbindungsgrad: k • Shuffle–Exchange–Netzwerk 2k Knoten mit Nummerierung von 0 bis 2k−1 , zwei verschiedene Verbindungen: Exchange-Verbindung: (bk−1 . . . b1 0)2 ↔ (bk−1 . . . b1 1)2 Shuffle-Verbindung: (bk−1 bk−2 . . . b0 )2 → (bk−2 . . . b0 bk−1 )2 Beispiel: Shuffle–Exchange–Netzwerk •o ´ /•l ( •o /•k +•o /•h ,•o ´ /• 000 001 010 011 100 101 110 111 / • 000 000 • 001 • YYYYYYYYYY pp8 • 001 YYpYpYY 010 • TTTTT ppppp , • 010 TTp 011 • NNpNpppp TTTTTjTjTjj5 • 011 p p NN jj TT) 100 • jjNjNjNjNjNj • 100 NNN jjj 2 e N e 101 • • e eeeee NNN& 101 eeeeee e 110 • • 110 / 111 • • 111 55 KAPITEL 5. PARALLELE ALGORITHMEN 5.3. PARALLELES SORTIEREN Shuffle-Exchange-Reduktion 000 010 001 011 100 101 110 111 • Basisidee: Reduktion von zwei Zwischenergebnissen pro Schritt −→ log viele Schritte Zu kombinierende Werte werden in aufeinander folgende Shuffle-Exchange-Schritten zusammengebracht. • allgemeine Verfahren: Parameter in #Prozessorelementen n = 2k 1 2 3 4 5 6 7 8 9 10 11 12 lokal val, tmp begin for j=0 to k-1 do for all Pi where 0 ≤ i < n do send val to Pi receive val from Pi send val to P<i> receive tmp from P<i> val := val + tmp od od end //shuffle //shuffle //exchange //exchange Beispiel: 6 −4 6−9 1.Schritt 2.Schritt 3.Schritt −3 6 26 2 19 −9 0 7 5 −4 6 26 9 20 26 9 20 26 24 20 26 24 20 26 −4 − 0 −3 6 26 −4 6 26 5.3 Paralleles Sortieren im sequentiellen Fall: Aufwand Ω (n log n) bei vergleichsbasierten Verfahren. Ziel im parallelen Fall: poly. log. Aufwand mit binearer Proz.-Zahl. 5.3.1 Ein CRCW-Verfahren mit konstantem Zeitaufwand n2 Proz. einer CRCW-PRAM können n Elemente in konstanter Zeit sortieren, falls • der Aktivierungsaufwand (O (log n)) vernachlässigt werden kann und • beim CW die Summe der Werte geschrieben wird Ansatz: „Ranksort“ 56 KAPITEL 5. PARALLELE ALGORITHMEN 5.3. PARALLELES SORTIEREN Vergleiche jedes Element mit jedem anderen und zähle die Anzahl der kleineren Elemente −→ Position in sortierter Liste Algorithmus: Parameter n = # zu sortierende Elemente Globale Vars: a [0 . . . (n − 1)] position [0 . . . (n − 1)] sorted [0 . . . (n − 1)] zu sortierende Elemente Position der Elemente in sortierter Liste Listing 16 „Ranksort“ 1 2 3 4 5 6 7 8 begin spawn Pi,j with 0 ≤ i, j < n for all Pi,j with 0 ≤ i, j < n do position\left[i\right]:=0 if a [i] > a [j] or (a [i] = a [j] and i > j) then position\left[i\right]:=1 fi od for all Pi,0 with 0 ≤ i < n do sorted[position[i]]:=a[i] par. Zeit O (1)¡ ¢ par. Kosten O n2 −→ nicht kostenoptimal 5.3.2 Sortiernetze BATCHER 1968: Netze zum Sortieren in polylogarithmischer Zeit hier: odd-even-merge-sort Beispiel: klassische sequentielle Verfahren zum Mischen sortierter Listen mit i bzw. j Elementen im worst case: i + j − 1 Beobachtung: Welche Elemente verglichen werden, hängt von vorherigen Vergleichen ab. −→ wissenabhängige Verfahren (non-oblivious) im folgenden: festes Vergleichsschema −→ wissensunabhängige Verfahren (oblivious) Beispiel: Tunierschema beim Tennis Sieger Sieger Ein Komparator erhält zwei Eingaben und produziert zwei Ausgaben, das Minimum und das Maximum der beiden Eingaben: 57 KAPITEL 5. PARALLELE ALGORITHMEN 5.3. PARALLELES SORTIEREN a b + min(a, b) max(a, b) Im folgenden nehmen wir an, dass gleichlange Folgen der Länge n = 2k zu Mischen sind. ¡ ¢ Induktive Konstruktion eines (n, n)-Mergers n = 2k (−→ odd-even-merger) Gegeben seien zwei sortierte Folgen: A = (a1 , . . . , an ) B = (b1 , . . . , bn ) n = 1: Komparator ist (1, 1)-Merger. n = 2: a1 min c1 a2 max c2 b1 min c3 b2 max c4 beliebiges n > 1 Voraussetzung: Es stehen x ¡n n 2, 2 ¢ -Merger zur Verfügung. Notation: [k] l bezeichne die Teilliste von Xi die mit dem k-ten Folgenglied beginnt und jedes l-te nachfolgende Glied wählt < Xk+i∗l | i ≥ 0 > A[1] B[1] 2 , 2 Teillisten der Elemente mit ungeradem Index. Satz 5.1.: Die Resultatfolge C ist sortiert. 58 KAPITEL 5. PARALLELE ALGORITHMEN 5.3. PARALLELES SORTIEREN Beispiel: (4, 4)-Merger (2,2)-Merger 1 5 6 2 3 7 8 4 5 1 2 6 7 3 4 8 (2,2)-Merger Es werden Folgen D und E hergestellt, die so ineinander geschoben werden (interleaving), dass die resultierende Folge bis auf eventuelle Nachbarvertauschungen sortiert ist. Interleaving-Schema d1 d2 OOO d3 OOO d4 PPP. . . . . . dn TTTT TTTT OOO OOO PPP TT) OO' OO' PP' ... en−1 e1 e2 e3 . . . en / d1 d2 e1 d3 e2 d4 e3 . . . . . . dn en−1 en Satz 5.2. (0-1-Prinzip): Ein Sortieralgorithmus bestehe nur aus vorherbestimmten, d. h. eingabeunabhängigen Vergleichen-Austausch-Anweisungen. Dann gilt: Sortiert der Algorithmus jede Eingabefolge, die nur aus Nullen und Einsen besteht, so sortiert er jede Eingabefolge. Beweis von Satz 5.2 durch Widerspruch: Annahme: Die Eingabefolge X1 , . . . , Xn wird von dem wissensunabhängigen Sortierverfahren nicht korrekt sortiert, d. h. nicht in die Reihenfolge Xπ(1) ≤ Xπ(2) ≤ . . . ≤ Xπ(n) gebracht. 59 KAPITEL 5. PARALLELE ALGORITHMEN 5.3. PARALLELES SORTIEREN • Sei k die erste Stelle, an der sich die sortierte Folge von der Ausgabe des Algorithmus Xσ(1) , . . . , Xσ(n) unterscheidet d. h. Xσ(k) > Xπ(k) Definiere zu X1 , . . . , Xn und k eine 0-1-Folge ½ 0, f alls xi ≤ xπ(k) yi := 1, f alls xi > xπ(k) Wird diese Folge dem wissensunabhängigem Sortieralgorithmus übergeben, so finden die gleichen Vergleichs-/ Austauschschritte statt, denn Xi ≥ Xj y Yi ≥ Yj . Insbesondere steht an der k-ten Stelle der Ausgabefolge yσ(k) = 1 und irgendwo rechts daneben der Wert yπ(k) = 0. Die 0-1-Folge wird nicht richtig sortiert. (W IDERSPRUCH) Beweis von Satz 5.1. mit dem 0-1-Prinzip. A und B seien sortierte 0-1-Folgen. Sei ak die letzte Null der Folge A und bl die letzte Null der Folge B, d. h. 0 1 . . . 1) A = (0 . . . |{z} 0 1 . . . 1) B = (0 . . . |{z} l k Es gilt: 0 ≤ k |{z} ,l≤ nur Einsen n |{z} nur N ullen Dann gilt für die Teilfolgen §k¨ §l¨ A[1] B[1] 2 hat 2 Nullen, 2 entsprechend 2 ¥k¦ ¥l¦ B[2] A[2] 2 hat 2 Nullen, 2 entsprechend 2 § ¨ § ¨ Damit hat die Folge D γ := k2 + 2l Nullen ¥ ¦ ¥ ¦ und die Folge E δ := k2 + 2l Nullen Für die Differenz ∆ := γ − δ gilt nach Definition der Gaußklammern: 1 2 0 z }| { z }| { z }| { ∆ ∈ beide gerade, eins gerade eins ungerade, beide ungerade Wir betrachten die Interleaving-Schemata für diese 3 Fälle: ∆=2 D ... 0 0 E ... ... ... 0 ... 0 0 \ 60 1 \ 0 0 0 ... ... 1 1 ... ... \ 1 1 1 KAPITEL 5. PARALLELE ALGORITHMEN 5.3. PARALLELES SORTIEREN ∆=1 D ... 0 0 0 \ E ... ... ... 0 ... 0 ... 0 0 1 ... \ 0 0 0 0 0 ... ... 1 ... ... \ 1 1 0 ∆=0 D 0 \ E ... ... ... 0 ... 0 1 \ 0 0 0 1 \ 0 0 0 ... ... \ 0 0 1 1 1 ... 1 ... Aufbau eines Sortiernetzes aus (n, n)-Merger O.B.d.A. n = 2k Durchlaufzeit: (Zahl vertikaler Komparatorstufen) t(n) = (n, n)-Mergers sei. Komparatorzahl: N (n) = k X Pk i i=0 τ (2 ), wobei τ (n) die Durchlaufzeit eines 2k−i ∗ ν(2i ), wobei ν(n) die Komparatoranzahl eines (n, n)-Mergers sei. i=0 Bestimme τ (n) und ν(n) durch Analyse der (n, n)-Merger: τ (1) = 1; ν(1) = 1 τ (2n) = τ (n) + 1; ν(2n) = 2ν(n) + n − 1 Mit vollst. Induktion zeigt man leicht: τ (n) = 1 + log n; ν(n) = 1 + n · log n Damit folgt: t(n) = k X (1 + i) = (k+1)(k+2) 2 ∈ O(k 2 ) = O(log2 n) i=0 k X ∈ O(n log2 n) 2k−i (1 + i ∗ 2i ) = . . . = 2k+1 − 1 + 2k k(k+1) 2 N (n) = i=0 =⇒ parallele Kosten: O(n log4 n) 61 KAPITEL 5. PARALLELE ALGORITHMEN 5.3. PARALLELES SORTIEREN 5.3.3 Der Algorithmus von Cole optimale Lösung auf CREW-PRAM, d. h. O(log n) Zeit mit O(n) Prozessoren. „paralleler Mergesort im vollständigem Binärbaum“ Hilfsmittel: Skelette von Folgen Definition. 5.3.: X und Y seien sortierte endliche Folgen ganzer Zahlen. [X] entstehe aus X durch Hinzunahme der Elemente −∞ und +∞. (a) Seien a < b und x ∈ X. x heißt zwischen a und b:y a < x ≤ b (b) X heißt Skelett von Y , in Zeichen X ∝ Y , falls für alle k ≥ 2 zwischen je k Elementen von [X] höchstens 2k − 1 Elemente von Y liegen. • X gemeinsames Skelett von Y und Z Y X∝ Z Notation: • X&Y sei die Verschmelzung. (merge) von X und Y Beispiel: Skelette von Folgen Y = (1, 4, 6, 9, 11, 12, 13, 16, 19, 20) X = (5, 10, 12, 17) Z = (2, 3, 7, 8, 10, 14, 15, 17, 18, 21) k = 2: Zwischen je zwei Elementen von X liegen −→ 2 ≤ 2k − 1 = 3 Elemente von Y −→ maximal 3 Elemente von Z k = 3: Zwischen je drei Elementen von X liegen −→ 4 ≤ 2k − 1 = 5 Elemente von Y −→ maximal 5 Elemente von Z k = 4: Zwischen je zwei Elementen von X liegen −→ 6 ≤ 2k − 1 = 7 Elemente von Y −→ maximal 6 Elemente von Z k = 5, 6 analog Listen dürfen maximal 11 Elemente haben =⇒ X ∝ 62 Y Z KAPITEL 5. PARALLELE ALGORITHMEN 5.3. PARALLELES SORTIEREN Haben zwei Folgen ein gemeinsames Skelett, so kann eine vereinfachte Mischung, das sog. Skelett-Merging, durchgeführt werden. Die beiden Folgen werden anhand des gemeinsamen Skelettes in Teilfolgen zerlegt, die in konstanter Zeit O(1) gemischt werden können. Lemma 5.4: Sei X, Y, Z sortierte endliche Folgen mit X ∝ Y Z Sei Y (i) := (y ∈ Y : xi−1 < y ≤ xi ) Z(i) := (z ∈ Z : xi−1 < z ≤ xi ) für 1 ≤ i ≤ |x| + 1 ½ x0 := −∞ x|x|+1 := ∞ Dann gilt: Y &Z = Y (1) &Z (1) , Y (2) &Z (2) , . . . , Y (|X| + 1) &Z (|X| + 1) Beispiel: −∞ Y (1) = 1, 4 Z(1) = 2, 3 ¾ 1, 2, 3, 4 5 Y (2) = 6, 9 Z(2) = 7, 8, 10 10 Y (3) = 11, 12 Z(3) = ¾ ¾ 11, 12 12 Y (4) = 13, 16 Z(4) = 14, 15, 17 17 Y (5) = 19, 20 Z(5) = 18, 21 ¾ 6, 7, 8, 9, 10 ¾ 13, 14, 15, 16, 17 18, 19, 20, 21 ∞ X heißt Skelett von Y , in Zeichen X ∝ Y , falls für alle k ≥ 2 gilt: Zwischen je k Elementen von X liegen höchstens 2k − 1 Elemente von Y . Grundidee des Algorithmus von Cole • Verschmelzung von Folgen mit gemeinsamen Skelett mit konstantem Aufwand. • Zu sortierende Liste ist zu Beginn auf Blattknoten eines vollst. Binärbaums verteilt. • die Verschmelzung von Teillisten erfolgt in mehreren Baumebenen fließbandartig gleichzeitig. Bezeichnungen: Gegeben sei der vollständige Binärbaum mit n = 2k Blättern 63 KAPITEL 5. PARALLELE ALGORITHMEN 5.3. PARALLELES SORTIEREN T (v) sei der Unterbaum mit Wurzel v. val(v) sei die momentane Folge des Knotens v. list(v) sei die sortierte Folge, die aus den Werten der Blätter in T (v) entsteht. Es gilt: val(v) ist stets geordnete Teilfolge von list(v). Ein Knoten v heißt vollständig, falls val(v) = list(v); sonst unvollständig (|val (v)| < |list (v)|). Algorithmus von Cole: Sei n = 2k die Anzahl der zu sortierenden Werte, die 1 : 1 den Blättern des vollständigen Binärbaums zugeordnet werden. Arbeitsweise eines beliebigen inneren Knotens v: Zu Beginn ist val(v) leer. Der Knoten v wird aktiv, wenn vom linken Kind eine Folge X1 der Länge 1 und vom rechten Kind eine Folge Y1 der Länge 1 erhält, die er zu einer Folge val(v) der Länge 2 verschmilzt. In jedem weiteren Schritt j erhält der Knoten von seinen beiden KindX Yj[1] knoten Folgen Xj und Yj , so dass Xj−1 = j[1] 2 , Yj−1 = 2 , Xj und Yj werden zu val(v) verschmolzen. In jedem Schritt wird die Länge von val(v) verdoppelt, bis list(v) erreicht wird −→ Knoten v wird vollständig. Ausgabevorschriften 1. Ist v unvollständig und val(v) ≥ 4, so sendet v die Folge val(v)[1] 4 = z an den Elternknoten. 2. Ist v vollständig, so bleibt v noch zwei Schritte aktiv. list(v)[1] und im letzten Schritt list(v). Im vorletzten Schritt sendet v 2 Danach wird der Knoten inaktiv. Abbildung 5.2: Beispiel: 16 Zahlen Zahlenfolge: 34, 81, 74, 92, 14, 31, 13, 97, 28, 36, 30, 80 34 81 \ / 34,81 \ 74 92 \ / 74,92 / 34,74 34,74,81,92 14 31 \ / 14,31 \ 13 97 \ / 13,97 / 13,14 13,14,31,97 28 36 \ / 28,36 \ 30 80 \ / 30,80 / 28,30 28,30,36,80 \ / 13,34 13,31,34,81 13,14,31,34,74,81,92,97 25 \ / 25,45 \ / 1,28 1,28,36,45 1,25,28,30,36,45,71,80 / 1,13 1,13,36,74 1,13,28,31,36,71,74,92 / \ 1, 13, 28, 31, 36, 71, 74, 92, 97 1 71 \ / 1,71 / 1,25 1,25,45,71 \ \ 64 45 KAPITEL 5. PARALLELE ALGORITHMEN 5.3. PARALLELES SORTIEREN Betrachte nun einen typischen Knoten, d. h. v mit |list(v)| > 4. Xi , Yi seien die Eingaben des Knotens im i-ten Schritt und Zi sei die Ausgabe im (i + 1)-ten Schritt. X1 = (28) Y1 = (1) \ / val1 (v) = (1, 28) Schritt 1 | ∅ X2 = (28, 36) Y2 = (1, 45) \ / val2 (v) = (1, 28, 36, 45) Schritt 2 | Z = (1) X3 = (28, 30, 36, 80) Y3 = (1, 25, 45, 71) \ / val3 (v) = (1, 25, 28, 30, 36, 45, 71, 80) Schritt 3 | Z2 = (1, 36) Schritt 4 vorletzter Schritt Z3 = (1, 28, 36, 71) Schritt 5 Z4 = (1, 25, 28, 30, 36, 45, 71, 80) y v wird inaktiv. Invariante des Algorithmus von Cole Satz 5.5.: Sei v ein Knoten mit |list(v)| ≥ 4. Seien Xi+1 , Yi+1 die Eingaben und Zi die Ausgabefolge von v in seinem (i + 1)-ten Schritt Dann gilt für alle i ∈ N: Xi+1 ∝ Xi+2 ∧ Yi+1 ∝ Yi+2 y Zi ∝ Zi+1 Lemma 5.6.: X ∝ X ′ ∧ Y ∝ Y ′ y X&Y 4 ∝ X ′ &Y ′ 4 Beweisgang. Gelte X ∝ X ′ , Y ∝ Y ′ Zeige (a) (b) X&Y ∝ X ′ , X&Y ∝ Y ′ Im allgemeinen gilt nicht: X&Y ∝ X ′ &Y ′ Jedoch liegen zwischen je k aufeinander folgenden Elementen von X&Y höchstens 2k + 2 Elementen. von X ′ &Y ′ Beweis von Satz 5.5. Sei |list(v)| = 2j−1 j = 3, 4, . . ., d. h. Knoten v ist nach seinem (j − 1)-ten Schritt vollständig. Mit der Ausgabevorschrift 1 erhält man mit Lemma 5.6 für alle i mit 1 ≤ i < j − 2: Zi = val(v)[1] Xi+1 &Yi+1 Xi+2 &Yi+2 = ∝ = Zi+1 4 4 4 65 KAPITEL 5. PARALLELE ALGORITHMEN 5.3. PARALLELES SORTIEREN Sobald v vollständig, tritt die Ausgabevorschrift (2) in Kraft und es gilt: Zj−1 = Zj = list(v) list(v) ∝ = Zj 4 2 list(v) ∝ list(v) = Zj+1 2 Für die Eingabefolge Xi+1 und Yi+1 steht stets ein gemeinsames Skelett Xi &Yi bereit. zeitlicher Ablauf: K Xi+1 ZiK ¡ K Yi+1 ¢ ↓werden immer parallelgeschickt seien Eingabefolgen eines Knotens des Levels K im sei Ausgabefolge Baum in seinem (i + 1)-ten aktiven Schritt Abbildung 5.3: Zeitlicher Ablaufplan Phase gesendete Folgen 1 Z1Blatt → X1A /1 ——————————————————————————————————————– 2 Z1A → X1B /1 3 Z2A → X2B /2 ——————————————————————————————————————– 4 Z1B → X1C /1 5 Z2B → X2C /2 6 Z3B → X3C /4 Z1C → X1D /1 ——————————————————————————————————————– 7 Z2C → X2D /2 8 Z3C → X3D /4 Z1D → X1E /1 ——————————————————————————————————————– 9 Z4C → X4D /8 Z2D → X2E /2 Beobachtung: • Wird Knoten v im Schritt i inaktiv, so wird der Elternknoten im Schritt i + 3 inaktiv y Laufzeit: t(n) = 3 log n = O(log n) • Prozessoren: O(n) ⇒ Kostenoptimalität: O(n log n) 66 KAPITEL 5. PARALLELE ALGORITHMEN 5.4. GRAPHEN ALGORITHMEN 5.4 Graphen Algorithmen Definition 5.7.: Ein endlicher Graph G = (V, E) besteht aus einer endlichen Menge von Knoten V (vertex) und einer endlichen Menge von Kanten E (edges), E ⊆ V × V Definition 5.8.: Sei G = (V, E) mit V = {v0 , . . . , vn−1 }. Die Einträge aij , 0 ≤ i, j ≤ n−1 der n×n Adjazenzmatrix zu G sind definiert durch ½ 1, f alls (vi , vj ) ∈ E aij = 0, sonst Definition 5.9.: a) Ein Graph G = (V, E) heißt ungerichtet, falls zu jedem (v, v ′ ) ∈ E auch (v ′ , v) ∈ E, ansonsten gerichtet. b) G heißt gewichtet, falls jeder Kante mittels einer Gewichtsfunktion w : E → R+ eine nicht negative reelle Zahl zugeordnet ist. w kann zu w̃ : V × V → R+ 0 ∪ {∞} ergänzt werden. ½ w(vi , vj ), f alls (vi , vj ) ∈ E w̃(vi , vj ) := ∞, sonst Definition 5.10.: a) Eine Folge von Kanten (vi1 , vi2 )(vi2 , vi3 ), . . . , (vik , vik+1 ) heißt Pfad, falls alle Knoten vi1 , . . . , vik+1 der Folge voneinander verschieden sind. b) Eine Kantenfolge (vi1 , vi2 )(vi2 , vi3 ), . . . , (vik−1 , vik ), (vik , vi1 ) heißt Zykel, falls alle Knoten vi1 , . . . , vik paarweise verschieden sind. c) Eine Kantenfolge (vi1 , vi2 ), . . . , (vik , vik+1 ) heißt Weg, falls alle Kanten voneinander verschieden sind. Ein Graph ohne Zykel heißt azyklisch. Definition 5.11.: Ein Graph (V ′ , E ′ ) heißt Subgraph von G = (V, E), falls V ′ ⊆ V und E ′ ⊆ E Definition 5.12.: Ein ungerichteter Graph heißt zusammenhängend, falls zu jedem Paar vi und vj in G ein Pfad von vi nach vj existiert. 5.4.1 Bestimmung der Zusammenhangskomponenten eines Graphen Zusammenhangskomponenten: minimale Menge von zusammenhängenden Subgraphen Definition 5.13.: Sei G = (V, E) mit V = {v0 , . . . , vn−1 } die n × n Zusammenhangsmatrix: C = (cij )0≤i,j≤n−1 wird definiert durch: 1 , falls vi und vj durch einen Weg der cij = Länge ≥ 0 verbunden sind 0 , sonst 67 KAPITEL 5. PARALLELE ALGORITHMEN 5.4. GRAPHEN ALGORITHMEN Abbildung 5.4: Beispiel – Zusammenhangskomponenten 0 1 0 0 0 0 0 0 1 1 0 1 2 3 4 5 6 7 8 1 0 1 1 0 1 1 1 0 0 2 0 1 1 0 1 1 1 0 0 3 0 0 0 1 0 0 0 0 0 4 0 1 1 0 1 1 1 0 0 5 0 1 1 0 1 1 1 0 0 6 0 1 1 0 1 1 1 0 0 7 1 0 0 0 0 0 0 1 1 8 1 0 0 0 0 0 0 1 1 C ergibt sich aus reflexivem, transitivem Abschluss der Adjazenzmatrix unter der boolschen Matrixmultiplikation, bei der als Multiplikation „∧“ und als Addition „∨“ verwendet wird. Statt dij = n−1 X aik bkj wird demnach dij = n−1 _ (aik ∧ bkj ) berechnet. k=0 k=0 Anstelle der Adjazenzmatrix wird die auf der Diagonalen modifizierte Matrix B verwendet. B = mit bij = (bij )0≤i,j≤n−1 ½ aij , 1, f alls f alls i 6= j i=j 1, falls es einen Weg von vi nach vj der Länge ≤ k gibt sonst ←− reflexiver Abschluss Für die Einträge von B k gilt bij = 0, Satz 5.14.: Für die Zusammenhangsmatrix C gilt: C = B n−1 wobei n die Anzahl der Knoten bezeichnet. 68 KAPITEL 5. PARALLELE ALGORITHMEN 5.4. GRAPHEN ALGORITHMEN Beweis Satz 5.14.: Falls zwei Knoten vi und vj überhaupt durch einen Weg verbunden sind, so existiert auch ein Weg der Länge ≤ n−1. Würde es nur Wege der Länge > n−1 geben, so würde mindestens 1 Knoten mehrfach vorkommen und der Weg enthielte einen Zyklus, der entfernt werden könnte. Sei O.B.d.A (n − 1) Zweierpotenz =⇒ log(n − 1) Boolsche Matrixmultiplikationen (sukzessives Quadrieren). Wird die Matrixmultiplikation auf einem Hypercube mit n3 Prozessen durchgeführt, läge der Gesamtaufwand bei O(log n) parallelen Schritten. 5.4.1.1 Algorithmus von Hirschberg (1976) Grundidee: Zusammenfassen von verbundenen Knoten zu Superknoten, bis jeder Superknoten einer Zusammenhangskomponente entspricht. ¡ ¢ ¡ ¢ Komplexität: O log2 (n) mit O n2 Prozessoren mit n = #Graphknoten Eingabe: Adjazenzmatrix eines ungerichteten Graphen G = (V, E) mit V = {1, . . . , n} Ausgabe: Vektor C der Länge n, so dass C (i) = C (j) = k, falls i und j in der selben Zusammenhangskomponente liegen und k der „kleinste“ Knoten in dieser Zusammenhangskomponente ist. Abbildung 5.5: Beispiel Ziel: i C(i) 1 1 2 1 3 3 4 1 5 1 6 1 7 1 8 1 Berechnung: i Initial C(i) T (i) 1 1 8 2 2 6 3 3 3 4 4 6 5 5 7 6 6 2 7 7 2 8 8 1 nach Phase 3: 1 2 3 4 1 2 3 2 5 2 6 2 7 2 8 1 i T (i) Der Algorithmus arbeitet in 3 Phasen, die ⌈log n⌉ mal iteriert werden. 1. Finde zu jedem Knoten den benachbarten Superknoten mit kleinstem Index. 2. Verbinde die Wurzel jedes Superknotens mit der Wurzel des benachbarten Superknotens mit kleinstem Index. Die Wurzel eines Superknotens ist der Knoten mit der kleinsten Nummer. 3. Alle neu verbundenen Superknoten werden zu einem neuen Superknoten zusammengefasst. Pseudocode: Initialisierung des C-Vektors: C(i) := i (1 ≤ i ≤ n) =⇒ Superknoten der Größe 1. zu Phase 1: 69 KAPITEL 5. PARALLELE ALGORITHMEN 5.4. GRAPHEN ALGORITHMEN 1 2 3 4 for all vertices i in parallel do T(i):= minj {C(j)|A(i,j)=1, C(j)6=C(i)} (falls diese Menge leer ist wird C(i) in T(i) gespeichert) zu Phase 2: 1 2 3 4 5 6 for all vertices i in parallel do T(i):= minj {T(j)|C(j)=i, T(j)6=T(i)} (wie oben) # "Finde aus allen benachbarten # Superknoten denjenigen mit # kleinstem Index" Abbildung 5.6: T-Graph zu Phase 3: 1 2 3 4 5 6 7 for all vertices i in parallel do B(i)←T(i) (1 ≤ i ≤ n) # "Umspeichern in Hilfsvektor B" repeat log(n) times for all vertices i in parallel do T(i)←T(T(i)) # "Setze T (i) ← T n (i)" for all vertices i in parallel do C(i)←min{(B(T(i)),T(i)} Bestimmung von Zusammenhangskomponenten Adjazenzmatrix A Hirschberg Vektor C mit C(i) = Index der Zusammenhangskomponenten mit i V = {1, . . . , n} −−−−−−−−→ Index einer Zusammenhangskomponente = Nummer des „kleinsten“ enthaltenen Knotens Algorithmus: ⌈log n⌉ Iterationen der folgenden 3 Phasen Phase 1: 1 2 for all vertices i in parallel do minj {C(j)|A(i, j) = 1, C(j) 6= C(i)} T (i) := C(i) f alls existent sonst Detaillierung: Sei ∞ eine Zahl > n. 1 2 3 4 5 6 7 8 9 10 70 (a) for all i,j, 1 ≤ i, j ≤ n in parallel do if A(i, j) = 1 and C(i) 6= C(j) then T emp(i, j) :=C(j) else T emp(i, j) := ∞ (b) for all i, 1 ≤ i ≤ n in parallel do T emp(i, 1) := minj T emp(i, j) (c) for all i, 1 ≤ i ≤ n in parallel do if T emp(i, j) 6= ∞ then T (i) :=Temp(i,1) else T (i) :=C(i) KAPITEL 5. PARALLELE ALGORITHMEN 5.4. GRAPHEN ALGORITHMEN Analyse: (a) (b) (c) O(1) mit O(n2 )Proz. O(log n)mit O(n2 )Proz. O(1) mit O(n)Proz. Phase 2: for all vertices i in parallel do minj {T (j)|C(j) = i, T (j) 6= i} T (i) := C(i) 1 2 f alls existent sonst „Verbinde Wurzel aller Superknoten mit Wurzel des Nachbar-Superknotens mit minimalem Index“ Detaillierung: analog zu Phase1 =⇒ Aufwand wie in Phase1 Beispiel: i 1 2 3 4 5 6 7 8 C(i) 1 2 3 4 5 6 7 8 T(i) 8 6 3 6 7 2 2 1 nach Phasen 1 u. 2 der 1ten Iteration Abbildung 5.7: T-Graph Der T-Graph, der in Phase 2 gebildet wird, besitzt in jedem neu zu bildenden Superknoten genau eine Schleife aus 2 Kanten, wobei der kleinste Knoten des neuen Superknotens einer der beiden Schleifenknoten ist. Da jeder Superknoten aus höchstens n Knoten besteht, kommt man beim Durchlaufen des T-Graphen nach n Schritten in die Schleife und ist somit höchstens 1 Schritt vom Minimum entfernt. Phase 3: 1 2 3 4 5 6 7 8 for all vertices i in parallel do B (i) := T (i) repeat} log n times for all vertices i in parallel do T (i) := T (T (i)) --setze T (i) := T n (i) for all vertices i in parallel do C (i) := min {T (i) , B (T (i))} 71 KAPITEL 5. PARALLELE ALGORITHMEN 5.4. GRAPHEN ALGORITHMEN 1 2 3 C(i) 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 4 2 2 2 2 5 2 2 2 2 6 6 6 6 2 7 6 6 6 2 8 8 8 8 1 Abbildung 5.8: C-Graph Analyse der 3.Phase: 1. Anweisung O (1) mit O (n) Proz. 2. Anweisung O (log n) mit O (n) Proz. 3. Anweisung O (1) mit O (n) Proz. y O (log n) mit O (n) Proz. ¡ ¢ Aufwand pro Iteration (3 Phasen) O (log n)mit O n2 Proz. ¡ ¢ ¡ ¢ ⌈log n⌉-Iterationen y O log2 n mit O n2 Proz. l 2 m ¡ 2 ¢ n Mit Brents Theorem kann man zeigen, dass log n Proz. ausreichen, um mit O log n - Schritten Zshgskomp zu ³ 2 ´ bestimmen. Es ist auch möglich mit O logn2 n Prozessoren. [C HIN ET AL 81/82] 5.4.2 Kürzeste Wege Zu G = (V, E) mit |V | = n sei die Kostenmatrix W gegeben mit wij wii := w̃ (vi , vj ) ∈ R0+ ∪ {∞} := 0 Gesucht ist die minimale Kostenmatrix D, deren Einträge dij , 0 ≤ i, j ≤ n − 1 die minimalen Kosten (d. h. die Summe der Gewichte) eines Weges von vi zu vj sind. Die Einträge der Matrix Dk seien definiert durch dkij := minimale Kosten des Weges von vi nach vj höchstens der Länge k y D1 = W , Dn−1 = D Wege der Länge ≥ n können nicht kürzeste Wege sein, da Kosten immer ≥ 0. O.B.d.A: n − 1 sei Zweierpotenz. Es gilt für k > 1 n o min dkil + dklj 2k dij = l 72 KAPITEL 5. PARALLELE ALGORITHMEN 5.4. GRAPHEN ALGORITHMEN =⇒ modifizierte Matrix-Multiplikation Multiplikation −→ Addition Addition −→ Minimumbestimmung =⇒ Dn−1 kann nach log (n − 1) modifizierten Matrix-Multiplikation (sukzessive Quadrierung) bestimmt werden ¡ ¢ Beispiel: auf n3 Proz. O log2 n (H YPERCUBE -M ATRIX -M ULTIPLIKATION) 5.4.3 Minimal spannende Bäume Definition 5.15.: Ein Baum ist ein zusammenhängender, ungerichteter, azyklischer Graph. Ein spannender Baum eines Graphen G ist ein Subgraph, der alle Knoten von G umfasst und ein Baum ist. In einem gewichteten Graphen ist ein minimal spannender Baum (MST) ein spannender Baum mit der minimalen Summe von Kantengewichten. Abbildung 5.9: minimal spannender Baum Falls |v| = n, so hat ein MST nach Def. n − 1 Kanten. Da jede der potentiellen n(n−1) Kanten mindestens einmal 2 betrachtet werden muss, ist die untere Grenze der Laufzeit eines sequentiellen Algorithmus zur Bestimmung eines ¡ ¢ MST Ω n2 Tabelle 5.1: 3 Klassische sequentielle Verfahren ¡ ¢ Kruskal 1956 O ¡n2 ¢ Prim-Dijkstra 1957/59 O ¡n2 ¢ Sollen 1977 O n2 log n 5.4.3.1 Algorithmus von Prim Invariante: Alle Knoten vi außerhalb des momentanen Baumes Ti kennen den Knoten in Ti mit minimalem Abstand zu ihnen. C (vi ) :=„nächster Nachbar von vi in Ti “ Bestimmung von MSTs Initialisierung: Ein Anfangsknoten v0 wird festgelegt. T0 besteht nur aus v0 . c (vi ) := v0 für alle vi 6= v0 for i := 1 to n − 1 do 73 KAPITEL 5. PARALLELE ALGORITHMEN 5.4. GRAPHEN ALGORITHMEN Suche unter den Knoten außerhalb von Ti einen Knoten ṽ mit w (ṽ, c (ṽ)) minimal und füge ṽ mit Kante (ṽ, c (ṽ)) zu Ti hinzu. Die Knoten vj , die außerhalb von Ti bleiben berechne c (vj ) neu: (i) (ii) 1 2 c (vj ) := if w (vj , ṽ) < w (vj , c (vj )) then ṽ else c(vj ) sequentielle Laufzeit: T (n) = 1 |{z} + v0 auswaehlen ¡ 2 = O n ¢ O (n) | {z } Initalisierung + (n − 1) O (n) {z } | Schleif e Implementierung des Verfahrens auf einer CREW-PRAM mit n Prozessoren mit 1-1-Zuordnung von Prozessoren zu Graphenknoten. Initialisierungsaufwand: O (1) Schleife: Phase 1: (Minimumsbildung) O (log n) Phase 2: O (1) y par. Laufzeit¡ O (n log¢n) mit n Proz. y par. Kosten n2 log n y nicht optimal. Beobachtung: Prozessoren werden untätig, sobald ihre Knoten im MST sind. y Rescheduling zur Kostenoptimierung Annahme: Es stehen N Prozessoren mit 1 ≤ N ≤ n bereit. Sei N = n1−x mit 0 < x < 1. Jeder Prozessor verwaltet jetzt n N = nx Knoten. Laufzeitanalyse: Initialisierungsaufwand: O (nx ) Schleife: 74 KAPITEL 5. PARALLELE ALGORITHMEN 5.4. GRAPHEN ALGORITHMEN Phase 1: Phase 2: O (nx ) für lokale Minimumbestimmung pro Prozessor + O (log N ) für globale Minimumbestimmung durch Reduktion + O (1) für Baumerweiterung O (nx ) lokale Updates von c (vj ) CREW-Aufwand: O (nx ) + O (n) (O (nx ) + O (log N )) = O (n) O ¢(nx ) ¡ x+1 = O n auf nx−1¡Prozessoren ¢ =⇒ parallele Kosten O n2 =⇒ kostenoptimal 5.4.3.2 Algorithmus von Sollin (1977) Arbeitsweise ähnlich zu Hirschberg-Algorithmus (Bestimmung von Zusammenhangskomponenten) anstelle des Knotens mit minimalem Index innerhalb der Superknoten wird jetzt die Kante mit minimalem Gewicht zu anderem Superknoten bestimmt. Initialisierung: Wald von n isolierten Knoten, die als Bäume betrachtet werden. Iteration: Für jeden Baum: bestimme die Kante mit dem kleinsten Gewicht, die diesen Baum mit einem anderen Baum verbindet. Alle diese minimalen Kanten werden hinzugefügt – dabei werden eventuell entstehende Zykel beliebig durchbrochen. Die Anzahl der Bäume wird pro Iteration mindestens halbiert. y ⌈log (n)⌉ Iterationen genügen. 75 KAPITEL 5. PARALLELE ALGORITHMEN 5.4. GRAPHEN ALGORITHMEN Abbildung 5.10: Beispiel (a) 1.Iteration (b) 2.Iteration Pseudo-Code(sequentiell) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 Parameter: n #Knoten Variablen: closest[ ] Abstand zu nächstem Baum edge[ ] Kante zu nächstem Baum T MST (als Kantenmenge) v,w Endpunkte der aktuellen Kante weight[ ] Kantengewichte Baum[ ] Wald als Knotenmenge begin for i:=1 to n do Baum[i]:={vi } od T := ∅ while |T | < n − 1 do für jeden Baum i setze closest[i]:=∞ für jede Kante (v,w) tue if FIND(v) 6= FIND(w) then if weight(v,w) < closest[FIND(v)] then closest[FIND(v)]:= weight(v,w) edge [FIND(v)]:= (v,w) fi fi für jeden Baum i true (v,w):= edge[i] / if FIND(v)6= FIND(w) then T:=T ∪ {(v, w)} (*) UNION(v,w) \ fi od end FIND 76 liefert zu einem Knoten, den Baum, in dem er enthalten ist. KAPITEL 5. PARALLELE ALGORITHMEN 5.4. GRAPHEN ALGORITHMEN UNION vereinigt die Bäume, in denen zwei Knoten v und w enthalten sind. FIND und UNION effizient realisierbar. Parallelisierung: • Die äußere while-Schleife ist nicht parallelisierbar. • 1te innere Schleife kann voll parallelisiert werden. • 2te innere Schleife: Jeder Prozessor kann für Anteil von inneren Knoten jeweils die von diesem Knoten ausgehenden Kanten untersuchen. • 3te innere Schleife: kritischer Bereich (*) erfordert Synchronisation. 77 6 Algorithmische Skelette Beobachtung: Viele parallele Algorithmen arbeiten mit festen Grundmustern für parallele Berechnung und Kommunikation. Durch algorithmische Skelette wird versucht, diese Grundmuster zu erfassen, effizient zu implementieren und zu analysieren. Ein algorithmisches Skelett besteht aus: 1. einer funktionalen Spezifikation „abstrakte Funktionsbeschreibung“ in fkt. Sprache: polymorphe Funktion höherer Ordnung (HOF) 2. parallele Implementierungen für verschiedene Zielarchitekturen 3. einem Kostenmodell zur Abschätzung der parallelen Ausführungszeit (für jede parallele Implementierung) typische Skelette • Divide & Conquer (Abb. 6.1) Listing 17 Divide & Conquer dc :: (a−→Bool)−→(a−→b)−→(a−→[a])−→([b]−→b)−→a−→b dc isAtom solve divide combine x = if isAtom x then solve x else combine(map dc’ (divide x)) where dc’ = dc isAtom solve divide combine Abbildung 6.1: Divide & Conquer • Master-Worker-Schema (Abb. 6.2) 78 KAPITEL 6. ALGORITHMISCHE SKELETTE Abbildung 6.2: Master-Worker-Schema • Pipeline (Abb. 6.3) Abbildung 6.3: Pipeline −→ ¤ −→ ¤ −→ . . . −→ ¤ −→ ¤ 1. funktionale Spezifikation pipe :: [[a]−→[a]]−→[a]−→[a] pipe [] xs = xs pipe(f:fs) xs = pipe fs (f xs) Beispiel: pipe (map map[*3,+5,*2])(1:2:...) 2. parallele Implementierung ...4 3 2 1["*3"]...6 3["+5"]...11 8["*2"]...22 16 −−−−−→ −−→ −−−→ −−−−→ 3. Kostenmodell: allgemein: Ausdruck mit Parametern, der parallele Ausführungszeit des Skelettes abschätzt. typische Parameter: • architekturabhängig – #Prozessoren – Kommunikationskosten tsend „Zeit für das Senden einer Nachricht“ δ „Übertragungsdauer einer Nachricht“ treceive Empfangszeit • Laufzeit systemabhängig – Prozesserzeugungskosten – sequentielle Ausführungszeiten • problemabhängig – Problemgröße Methode: Analyse eines kritischen Pfades (Abb. 6.4) — kritischer Pfad: Folge notwendiger Aktionen, die aufeinander aufbauen und damit die Gesamtausführungszeit bestimmen. Sehr oft: ’3’ Grundphasen 1. Hochfahren des Systems bis alle Prozessoren aktiv sind 79 KAPITEL 6. ALGORITHMISCHE SKELETTE Abbildung 6.4: kritischer Pfad 2. parallele Phase „längster“ Teil der Parallelausw. 3. Schlussarbeiten und Runterfahren des Systems tpipe = tinit + tpar + tf inal tinit = tpar = tf inal = #F ct ∗ (tcreateP rozess + tsend (size (Daten)) + δ) „ « #F ct max ∗ (tstartP rocess + #Daten ∗ treceive (size (Daten)) + {tcomp } + tsend (size (Daten)) F ct p δ + treceive (size (Daten)) < < < Folien Skeletal Programming > > > map f (xs + +ys) red ⊕ (xs + +ys) = = (map f xs) + + (map f ys) (red ⊕ xs) ⊕ (map ⊕ ys) falls scan ⊕ (xs + +ys) ⊕ = = assoziativ (scan ⊕ xs) + +map ((red ⊕ xs) ⊕) (scan ⊕ ys) (scan ⊕ xs) op (scan ⊕ ys) a op b where = a + + map ((last a) ⊕) b scanred (⊗, ⊕) (xs + +ys) = red (⊕) ◦ scan (⊗) (xs + +ys) = red (⊕) (scan ⊗ xs + +map ((red ⊗ xs) ⊗) (scan ⊗ ys)) 80 KAPITEL 6. ALGORITHMISCHE SKELETTE = (scanred (⊗, ⊕) xs) ⊕ (red ⊕ (map ((red ⊗ xs) ⊗) (scan ⊕ ys))) scanred′ (⊗, ⊕) (xs + +ys) = ((scanred (⊗, ⊕) xs) ⊕ (red ⊕ (map ((red ⊗ xs) ⊗) (scan ⊕ ys))) , (red ⊗ xs) ⊗ (red ⊕ ys)) Distributivität: a ⊗ (b ⊕ c) = (a ⊗ b) ⊕ (a ⊗ c) map (a⊗) ◦ red⊕ = red ⊕ ◦map (a⊗) = ((scanred (⊗, ⊕) xs) ⊕ (red ⊗ xs) ⊗ (scanred (⊗, ⊕) ys) , (red ⊗ xs) ⊗ (red ⊕ ys)) = scanred′ (⊗, ⊕) xs < ⊕, ⊗ > scanred′ (⊗, ⊕) ys < < < Folien Alternative Concepts: Parallel Functional Programming > > > 81 Literaturverzeichnis [1] A NDREWS , G REGORY R.: Foundations of Multithreaded, Parallel, and Distributed. Addison Wesley, 1999. [2] F OSTER , I AN: Designing and Building Parallel Programs: Concepts and Tools for Parallel Software Engineering. Addison Wesley, 1995. I Abbildungsverzeichnis II 2.1 Matrix-Multiplikation n=3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.2 Hypercube . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.3 Butterfly-Vernetzung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.4 Hypercube der Dimension k . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 4.1 Zusammenhängender Puffer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 5.1 Aufbau einer PRAM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 5.2 Beispiel: 16 Zahlen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 5.3 Zeitlicher Ablaufplan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 5.4 Beispiel – Zusammenhangskomponenten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 5.5 Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 5.6 T-Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 5.7 T-Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 5.8 C-Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 5.9 minimal spannender Baum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 5.10 Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 6.1 Divide & Conquer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 6.2 Master-Worker-Schema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 6.3 Pipeline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 6.4 kritischer Pfad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80