Lösung_DICWS0809

Werbung
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 )

pn
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();
}
Herunterladen