Klausur WS 08/09 Fach: Semester: Dozent: Tag: Bearbeitungszeit: Raum: Name: Distributed Computing 1IMund 2IM (Master) G. Bengel 05.02.2009 15:45 – 17:45 120 Minuten 01/211 ............................................................... Matr.Nr.: ................................................................ Punkte: .............. Note: .................. Hilfsmittel: Vorlesungsskripte und Lehrbücher DIC Hinweise: Die Aufgaben sind auf den vorgelegten Aufgabenblättern zu lösen. Die Aufgabenblätter sind vollständig abzugeben. Bitte nicht mit Bleistift schreiben! Ich habe zusätzlich zu den Aufgabenblättern......... Blätter abgegeben. Unterschrift: ................................................................ Aufgabe Punkte: 01 02 03 04 05 DICWS0809.doc Name :....................................... Aufgabe 01: 2 6 = 3 + 3 Punkte a) Konkretisieren und vergleichen Sie bitte die beiden Begriffe - nebenläufige Prozesse und kooperierende Prozesse. Nebenläufig (concurrent) bedeutet, dass zwei Aktions- oder Aktivitätenstränge oder Prozesse gleichzeitig ablaufen, diese aber nicht notwendigerweise etwas miteinander zu tun haben. Die Aktionen sind kausal voneinander unabhängig und können somit unabhängig voneinander ausgeführt werden. Bei der Ausführung der nebenläufigen Prozesse stehen sie in Konkurrenz zueinander bei der Belegung und anschließenden Benutzung der Betriebsmittel eines Rechners, Multiprozessors, Multi-Computers, Clusters oder Grids. Von kooperierenden (cooperating) Prozessen spricht man, 1. wenn die nebenläufigen Abläufe (Prozesse) zu einem übergeordneten Programm gehören oder die verschiedenen Aktivitätenstränge und somit die Prozesse eine gemeinsame Aufgabe lösen und 2. die Abläufe so logisch miteinander verknüpft sind, dass eine Synchronisation zwischen den Abläufen erfolgen muss. Bsp: Erzeuger- und Verbraucherproblem: Pipe bzw. Queues b) Geben Sie bitte die Ziele des Autonomic Computing an. Selbst-Konfiguration (Self-Configuration): Automatische Konfiguration und Management der Komponenten; Selbst-Heilend (Self-Healing): Automatische Entdeckung von Fehlern und Korrektur der Fehler; Selbst-Optimierend (Self-Optimization): Automatische Überwachung und Kontrolle der Ressourcen zur Sicherstellung der optimalen Funktionstüchtigkeit in Bezug auf die vorgegebenen Anforderungen; Selbst-Schützend (Self-Protection): Auf Eigeninitiative basierende Identifikation und Schutz vor willkürlichen Angriffen. Name: ............................................... Aufgabe 02: fork/join-Parallelismus 3 10 = 3 + 2 + 2 + 3 Punkte a) Erläutern Sie, was man unter dem fork/join-Parallelismus versteht? Unix ermöglicht, mit den Systemcalls fork und join, nebenläufige Prozesse (Kinder) zu vergabeln und an dem Ende wieder mit dem Vater zu vereinigen. Zur nachrichtenbasierten Kommunikation zwischen Prozessen dienen Pipes und Warteschlangen. Beim Programmstart läuft ein Prozess, der Hauptprozess oder das main. Werden nebenläufige Prozesse benötigt, so „vergabelt“ (fork) sich der Hauptprozess in ein oder mehrere Kindprozesse. Der Hauptprozess und alle Kindprozesse arbeiten nebenläufig. Kommt ein Kind zu Ende (exit oder quit) oder wird es supendiert, so „vereinigt“ (join) sich der Kontrollfluss der Kinder mit dem des Hauptprozesses. Umschließt man das fork mit einer Schleife, so erzeugt man in der Schleife beliebig viele Kindprozesse, die alle nebenläufig zum Vaterprozess ablaufen. Bei Beendigung der Kindprozesse kann dann der Kontrollfluss von allen Kindprozessen mit von einer Schleife umgebenen join vereinigt werden mit dem Kontrollfluss des Vaters. Mit wait oder waitpid kann der Vater ebenfalls auf die Beendigung eines Kindes warten. b) Auf welchem Programmier-Architekturmodell basiert der fork/joinParallelismus? Das Standardprogrammiermodell für parallele Prozesse ist für Einund Multiprozessoren gedacht. Eng gekoppelte Multiprozessoren und Multicore-Prozessoren: Alle Prozessoren bei einem eng gekoppelten Multiprozessor können den Adressraum des gemeinsamen Speichers gemeinsam benutzen. Der Verkehr zwischen Prozessor und dem Hauptspeicher bildet einen leistungsbegrenzenden Flaschenhals in einem Rechner, der von-Neumann-Flaschenhals heißt. Zur Reduktion dieses Flaschenhalses besitzt jeder Prozessor einen Cache, der Kopien von Teilen des Hauptspeichers enthält. c) Auf welchem Programmier-Architekturmodell basiert das Message Passing Modelll (MPI)? Lose gekoppelte Multiprozessoren und Multicore-Prozessoren: Jeder Prozessor hat seinen eignen Speicher hat und es ist kein gemeinsamer Speicher vorhanden. Die Synchronisation, Name :....................................... 4 Koordination und Kommunikation der parallelen Prozesse auf den verschiedenen Prozessoren ist nur durch Nachrichtenaustausch zu bewerkstelligen, da kein gemeinsamer Speicher existiert. d) Vergleichen sie bitte Barrierensynchronisation. den Unix-Systemcall join mit der Barrierensynchronisation: Befehl: MPI_Barrier(MPI_COMM comm) // comm. ist der Gruppenname und beinhaltet jeden erzeugten Prozess, der zuvor definierten Prozessanzahl. Wartet bis jeder Kindprozess der Gruppe comm an einem bestimmten Punkt angekommen ist. Diese Prozesse leben danach weiter. Erst wenn alle Prozesse diese Barriere erreicht haben, geht der Programmablauf weiter. Dann erst kehrt MPI_Barrier(comm) zum Aufrufer zurück. Join: Im Gegensatz zur Barr-synchr. muss join() für jeden einzelnen Kindprozess aufgerufen werden. Der Programmablauf geht erst dann weiter, wenn jeder Kindprozess auf dem der join-Befehl wartet, sich selbst durch exit() terminiert hat. Aufgabe 03: Google–Cluster 10 = 4 + 2 + 2 + 2 Punkte Gegeben seien die drei Texte (Dokumente) T0 = “Mr. Bengel’s first name is Günther” T1 = “the first name of Mr. Bengel’s wife is Monika” T2 = “the first name of Mr. Bengel’s daughter is Isabel” a) Erstellen Sie den invertierten Fileindex: Mr. = {T0, T1, T2} Bengel’s = {T0,T1, T2} first = {T0,T1, T2} name = {T0,T1, T2} is = {T0,T1, T2} Günther = {T0} the = {T1, T2} of = {T1, T2} wife = {T1} Name: ............................................... 5 Monika = {T1} daughter = { T2} Isabel = { T2} b) Für die Suchanfrage „first“ geben sie bitte die Dokumente an: first = {T0,T1,T2} c) Für die Suchanfrage „first“ und „Monika“ geben sie bitte die Dokumente an: first = {T0,T1,T2} Monika = {T1} {T0,T1,T2} und {T1} = {T1} d) Erläutern Sie bitte die Aufgabe des Google Web Service (GWS) und das von ihm durchgeführte zwei Phasen-Protokoll. Der Google Web Server nimmt die HTTP-Suchanfrage vom Browser eines Benutzers entgegen und sendet diese parallel an den Spell Checker, der eine Überprüfung der Rechtschreibung vornimmt, und an den Ad Server, der passende Werbung aussucht. Die u.U. rechtschreibkorrigierte Anfrage wird danach in einer ersten Phase an Index-Server un in einer zweiten Phase an Document-Server gesendet. Index-Server enthalten einen Eintrag für jedes Wort im Web un den invertierten Index, wobei es sich um eine Indexstruktur handelt, die beschreibt, in welchen Dokumenten ein Wort auftaucht. Die zur Suchanfrage gefundenen Dokumente werden nach Rang sortiert. Die Suche ist hoch parallelisierbar, da der Index in viele kleine Teile, Shards genannt, zerlegt ist. Das Ergebnis dieser Phase ist, dass eine Liste von docids (document identifiers) beim GWS vorliegen. In der zweiten Phase erfolgt eine Zusammenstellung der Dokumente gemäß der docids. Zu diesen docids extrahieren die Document-Server aus den per docid referenzierten Dokumenten den Titel, URL und Textausschnitte in der Nähe des Suchbegriffes. Aufgabe 04: fork join-Parallelismus (Unix, Threads, MPI) 12 = 4 + 4 + 4 Punkte a) Geben Sie bitte ein Programmiergerüst unter Unix an, für den fork/joinParallelismus mit einem Master und 10 Worker. Name :....................................... 6 main(){ for (int i = 0; i < 10; i++){ if (pid[i] = fork() == 0){ // Kind soll etwas tun }else{ //Vater tut etwas } } for (int i = 0; i < 10; i++){ join(pid[i]); } } b) Geben Sie bitte ein Programmiergerüst mit Threads an, für den fork/join-Parallelismus mit einem Master und 10 Worker. #define length 10 int field [length] [2]; // Feld von Paaren, die addiert werden void op_thread(pthread_addr_t arg) // Operation zum Addieren { //irgendwas berechnen // Rückgabe Ergebnis und Terminieren pthread_exit((pthread_addr_t) output); } main (void)// main thread, Erzeugerthread { // zu erzeugende Bearbeitungsthreads pthread_t thread[length]; // Laufvariable, Einzelergebnis int i, result; // Erzeugung der Bearbeitungsthreads for (i=0; i < length; i++) pthread_create ( // thread Datenstruktur &thread[i], // thread-Attribut NULL, // aufzurufende Funktion op_thread, Name: ............................................... 7 // Zeiger auf Parameter (pthread_addr_t)field[i]); for (i=0; i < length; i++) { // Warten auf Ergebnis der einzelnen Threads pthread_join( // Identifikation des Thread thread[i], // Ergebnis der Bearbeitung &result); // Ergebnisse verarbeiten sums[i] =result; // Löschen der Threads pthread_detach(thread[i]); } } c) Geben Sie bitte ein Programmiergerüst mit dem Message Passing Interface (MPI) an, für den fork/join-Parallelismus mit einem Master und 10 Worker. main(arg){ int Me, Nodes = 10; MPI_INT(arg); MPI_COMM_SIZE(...&Nodes); MPI_RANK(...&Me); // Falls aktueller Prozess nicht der Vaterprozess ist if(Me != 0){ // Empfangen der Daten vom Vater MPI_Recv(von Master-Knoten 0 ); // Kind soll mit diesen Daten etwas berechnen // Ergebnis an Master zurücksenden MPI_Send(an Master-Knoten 0 ); } // Falls aktueller Prozess der Vaterprozess ist else{ for (int i = 1; i < Nodes; i++){ // Master sendet zu bearbeitende Daten an die Kinder MPI_Send(an Kind-Knoten i ); // Empfangen der Ergebnisse von den Kindern MPI_Recv(von Kind-Knoten i ); } } Name :....................................... 8 Aufgabe 05: Parallelisierungstechniken, MPI 12 = 3 + 3 + 2 + 4 Punkte Nehmen Sie an es liegt ein sehr großes Feld von Zahlen vor. Für dieses Feld soll die Summe seiner Elemente berechnet werden. a) Parallelisieren sie die Summenberechnung der Elemente des Feldes nach Divide and Conquer. Der Vater, also der Hauptknoten erstellt erstmal das zu sortierende Array. Dieses wird geteilt in zwei Hälften und an die unteren zwei Kindknoten geschickt. Dort findet wieder eine Zerteilung des Arrays statt und eine Weiterleitung der beiden Array-hälften an die unteren zwei Kindknoten. Diese Prozedur wird solange durchgeführt, bis alle nach unten gereichten Array-hälften nur noch aus zwei Elementen bestehen. Dann wird die Summe von beiden Array-Elementen ermittelt und diese an dessen Vaterknoten weitergegeben. Es wird solange die Summe von beiden Elementen nach oben gereicht bis man letztendlich wieder beim Master-Knoten angekommen ist. Dort wird nun ein letztes mal die Summe aus beiden Elementen ermittelt und letztendlich auf dem Bildschirm ausgegeben. b) Erläutern sie das Master Worker Schema an dem durch das Divide und Conquer gewonnene Schema für die Summenberechnung der Elemente eines Feldes. Das große Feld mit den Zahlen wird in mehrere Unterfelder von ungefähr gleicher Größe zerlegt. Die Unterfelder sind unabhängig voneinander. Dieser Prozess wird rekursiv auf die Unterfelder angewandt. Die Rekursion endet, wenn das Unterfeld nicht weiter zerlegt werden kann. In der untersten Ebene werden zwei Zahlen in jedem Knoten miteinander addiert. Die Summe übergibt man dann eine Ebene höher und addiert wieder die zwei Zahlen miteinander, die sich im Knoten befinden. Dieser Prozess wird solange ausgeführt bis man am Vaterknoten angekommen ist. Dort werden die letzten zwei Zahlen wieder miteinander addiert und erhält somit die Summe des ganzen Feldes. Name: ............................................... 9 c) Berechnen Sie den theoretisch erreichbaren Speedup für das unter b) erstellte Master Worker Schema zur Summenberechnung der Elemente eines Feldes. n = Größe der einzelnen Datenzerlegungen p = Anzahl der Worker Laufzeit des schnellsten bekannten sequentiellen Algorithmus: T ' ( n) p n Laufzeit des parallelen Programms: Tp ( n ) n Speedup: T ' ( n) Sp (n) Tp ( n ) pn n p d) Erstellen Sie bitte eine Programmgerüst für das unter b) erstellte Master Worker Schema zur Summenberechnung mit dem Message Passing Interface (MPI). main(int argc, char *argv[]) { int groessteZahl = -1; int array[N]; Name :....................................... 10 MPI_Init(&argc,&argv); MPI_Status Status; MPI_Comm_size(MPI_COMM_WORLD,&NNodes); MPI_Comm_rank(MPI_COMM_WORLD,&Me); // Der Vater, also der Hauptknoten erstellt erstmal das zu sortierende Array if (Me != 0){ MPI_Recv(Array von Vater-Knoten); if (array.length > 2){ // Nach unten senden MPI_Send(erste Array-Hälfte zum linken Ast senden); MPI_Send(zweite Array-Hälfte zum rechten Ast senden); // von unten die Ergebnisse empfangen MPI_Recv(Summe vom linken Ast empfangen); MPI_Recv(Summe vom rechten Ast empfangen); summeBeiderAeste = summeLinkerAst + summeRechterAst; // Summe der beiden Summen nach oben senden MPI_Send(summeBeiderAeste zum Vaterknoten des aktuellen Knoten senden); }else{ // Wenn das empfangene Array nurnoch zwei Elemente hat // Summiere direkt die Array-Elemente summeVomBlatt = array[0] + array[1]; // Größeres Element von beiden nach oben senden MPI_Send(summeVomBlatt zu dessen Vaterknoten schicken); } } // ansonsten, wenn der aktuelle Knoten der Hauptknoten ist else{ MPI_Send(erste Array-hälfte zum linken Ast senden); MPI_Send(zweite Array-hälfte zum rechten Ast senden); // von unten die Ergebnisse empfangen MPI_Recv(Summe vom linken Ast empfangen); MPI_Recv(Summe vom rechten Ast empfangen); summeBeiderAeste = summeLinkerAst + summeRechterAst; printf("Maximum ist %d \n", summeBeiderAeste); } MPI_Finalize(); }