Parallel Programming Condition Queues klauserc/FS10/PP

Post on 06-Apr-2015

105 views 0 download

transcript

Parallel Programming

Condition Queues

http://n.ethz.ch/~klauserc/FS10/PP/

Heute

1. Lösung zu Assignment 2

2. Condition Queues

Was sind Condition Queues?

Wann sind Condition Queues sinnvoll?

Wie verwendet man Condition Queues?

3. Ausblick auf Assignment 3

1. – ASSIGNMENT 2

Teil 2 – Fragen

Frage: Wieso reicht es nicht, die Methoden read und write `synchronized` zu machen?

Antwort: Dadurch wird nur verhindert, dass Producer und Consumer gleichzeitig schreiben/lesen. Hat keinen Einfluss auf die Reihenfolge der Operationen.

Teil 2 – Fragen

Frage: Würde es ausreichen in den Methoden read und write anstelle von synchronized eine Bool’sche Variable als “Guard” zu verwenden?

Antwort: Nein! read und write sind keine atomaren Operationen!

Bonus: Warum ist i++ nicht atomar?

Teil 3 – Fragen

Frage: Reicht synchronized(this) in den jeweiligen run-Methoden von Producer und Consumer?

Antwort: Nein, denn this ist jeweils an ein anderes Objekt gebunden. Zwei unabhängige locks Schliessen sich gegenseitig nicht aus

Teil 3 – Fragen

Frage: Welches Objekt sollte stattdessen als gemeinsamer “Monitor” verwendet werden?

Antwort: Egal, solange beide das selbe Objekt verwenden. Am einfachsten nimmt man die gemeinsame Instanz von UnsafeBuffer.

Source Code

Teil 3 – FragenVorteile/Nachteile von Synchronisation der Producer/Consumer gegenüber Synchronisation des Buffers?

Vorteile:

Man kann beliebige Buffer verwenden (auch UnsafeBuffer)

Ermöglicht zusätzliche Aktionen, die ausgeführt werden müssen, bevor andere Threads den Buffer verwenden

Nachteile

Mehr Aufwand

Fehleranfällig (besonders wenn neue Prozesse hinzukommen)

2. – CONDITION QUEUES

Condition queue ≡ Intrinsic queue

• Eine Erweiterung des Monitor-Locks in Java

• Situation: Thread fährt nur unter folgenden Bedingungen fort

1. Exklusiven Zugriff auf eine Resource (Lock)

2. Preconditions erfüllt

Wie geht man mit Situationen um, wo (2.) nicht erfüllt ist?

Conditiont Queue (Abstrakt)

void run() { //aquire lock while(!BEDINGUNG) { //release lock //let other threads fix precondition //re-aquire lock } //lock && precondition yay! … }

Condition Queues (Konkret)

void run() { synchronized(this) { while(!BEDINGUNG) { wait(); //≡ this.wait() } //lock && precondition yay! … } //end-synchronized} //end-run

Condition Queues - Kommunikation

synchronized void write(int d) { while(isFull) { wait(); } data = d;

isFull = true; notifyAll();}

synchronized int read() { while(!isFull) { wait(); } int d = data; isFull = false; notifyAll(); return d;}

notify versus notifyAll

a.notifyAll weckt alle Threads, die auf a warten (via a.wait())

Die geweckten Threads verlangen alle exlusiven Lock auf a.

Konkurrieren auch mit allen anderen Threads, die diesen Lock wollen

Jeder Thread wird einmal die Chance haben, fortzufahren (Deadlock/Livelock ausgenommen)

notify versus notifyAll

a.notify weckt einen (!) der Threads, die auf a warten (via a.wait())

Welcher der wartenden Threads aufgeweckt wird, hängt von der Implementierung ab (könnte auch zufällig sein)

Verlangen Lock auf a; konkurrieren mit allen anderen Threads, die ebenfalls Lock auf a wollen.

Andere wartende Threads bleiben wartend bis jemand sie “notified”

notify vs. notifyAll - Empfehlung

• Einfacher Grundsatz: Immer notifyAll

• Erweiterter Grundsatz: Wenn ihr darüber nachdenken müsst, ob notify anstatt notifyAll funktioniert, nehmt notifyAll

• Szenario für notify: Es ist egal welcher Thread aufgeweckt wird.

Beispiel: Pool von Workerthreads. Es spielt keine Rolle welcher der Worker aufgeweckt wird. Jeder kann die Arbeit verrichten.

ProducerFlagBuffer buf;void run() { for(int counter = 0;

counter < 10000; i++) { if(buf.isFull) { buf.wait(); } synchronized(buf) { buf.write(counter); buf.isFull = true; } }//end-for}//end-run

ConsumerFlagBuffer buf;void run() { while(true) { if(!buf.isFull) buf.wait(); synchronized(buf) { int counter = buf.read)); buf.isFull = false; if(counter >= 9999) { return; } } //consume counter … }//end-while}//end-run

Ist dieser Code korrekt?

ProducerFlagBuffer buf;void run() { for(int counter = 0;

counter < 10000; i++) { synchronized(buf) { while(buf.isFull) buf.wait(); buf.write(counter); buf.isFull = true; notifyAll(); } //end-synchronized } //end-for} //end-run

ConsumerFlagBuffer buf;void run() { while(true) { synchronized(buf) { while(!buf.isFull) buf.wait(); int counter = buf.read)); buf.isFull = false; if(counter >= 9999) { return; } notifyAll(); } //end-synchronized //consume counter … } //end-while} //end-run

Ist dieser Code korrekt?

ProducerFlagBuffer buf;void run() { synchronized(buf) { for(int counter = 0;

counter < 10000; i++) { while(buf.isFull) buf.wait(); buf.write(counter); buf.isFull = true; buf.notifyAll(); } //end-for }//end-synchronized}//end-run

ConsumerFlagBuffer buf;void run() { while(true) { synchronized(buf) { while(!buf.isFull) buf.wait(); int counter = buf.read)); buf.isFull = false; if(counter >= 9999) { return; } buf.notifyAll(); }//end-synchronized //consume counter … }//end-while}//end-run

Ist dieser Code korrekt?

Richtig oder falsch?

• „notify() weckt nur einen der wartenden Threads, während notifyAll() alle weckt“

• „a.wait() darf nur aufgerufen werden, wenn der aktuelle Thread den Lock auf a hält.“

• „a.notify() weckt einen der auf a wartenden Threads und lässt ihn um den Lock auf a konkurrieren.“

3. – ASSIGNMENT 3

Mergesort

Gegeben:

Liste L mit n Zahlen (int)

Gesucht:

Sortierte Liste L’

Algorithmus:

Teile L auf in zwei Listen der Länge n/2

Sortiere jede der Unterlisten rekursiv mit Mergesort

Führe die zwei sortierten Unterlisten zusammen

Ende der Rekursion: Listen mit nur 1 Element

Beispiel (Zerlegung)

5, 4, 3, 2, 1, 0

5, 4, 3 2, 1, 0

5, 4 3

5 4

2, 1 0

2 1

Zusammenführen (merging)

Beispiel

L1: 0 5

L2: 3 4 45

Ausgabe:

Per Definition

0 3 4 5 45

Vergleichen

Kopieren

Zeiger bewegen

Fertig!

Zusammenführen, Beispiel

0, 1, 2, 3, 4, 5

3, 4, 5 0, 1, 2

4, 5 3

5 4

1, 2 0

2 1

Mergesort, parallelisiert

• Welche Teile des Algorithmus können parallelisiert werden?

Unterlisten sortieren

Zwei Unterlisten zusammenführen

• Synchronisationsprobleme?

Zwei Unterlisten erst zusammenführen, wenn sie fertig sortiert sind

• Performance

Anzahl Threads

Länge der Liste

Performance-Messungn t 1 2 4 8 16 32 … 51

2100´000 ×

500´000 ×

1´000´000

100´000´000n ≔ Länge der Listet ≔ Anzahl Threads

Frage: Wieviele Threads sind optimal?

Wie misst man Zeit?

• System.currentTimeMillis()

Nicht jede Millisekunde aktualisiert

Nicht auf Millisekunden genau

• System.nanoTime()

Präzision in Nanosekunden

Nicht auf Nanosekunden genau

• Für uns ist currentTimeMillis() gut genug

Wie misst man Zeit

long start;long end;start = System.currentTimeMillis();//zu messender Vorgangend = System.currentTimeMillis();System.out.println(“Time elapsed: “ + (end-start));

Auf einen Thread warten

Welche Möglichkeiten kennt ihr?

• Busy wait (in Schleife überprüfen, ob Thread fertig; Assignment 3)

• Condition Queues (wait(), notifyAll())

• Warten bis der Thread sich selbst beendet: t.join()

t.join()

Thread t = new Thread(myRunnable);t.start();//do our own stuff in the meantimetry { t.join(); //wait for the other thread to „catch up“} catch(InterruptedException e) { /* ignore */ }

t.join();

t.start();

Zu beantwortende Fragen

• Ist die parallele Version schneller?

Nicht selbstverständlich: Verwaltungsaufwand

• Wieviele Threads bieten optimale Performance?

• Welchen Einfluss haben

CPU Modell

CPU Taktfrequenz

Anzahl “Cores”

verfügbarer Arbeitsspeicher

verfügbarer Adressraum (32Bit vs. 64Bit)

Unangenehme Realität

• Ideale Welt

N Prozessoren führen zu einem N-fachen Anstieg der Rechenleistung

• Realität

Jeder zusätzliche Prozessor muss auch verwaltet werden.

Viele Abläufe können erst gar nicht parallelisiert werden (rein Sequentiell)

Kommunikation/Synchronisation können Wartezeiten zur Folge haben

FRAGEN?