350 likes | 474 Views
Parallel Programming. Condition Queues. http://n.ethz.ch/~klauserc/FS10/PP/. Heute. Lösung zu Assignment 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.
E N D
Parallel Programming Condition Queues http://n.ethz.ch/~klauserc/FS10/PP/
Heute • Lösung zu Assignment 2 • Condition Queues • Was sind Condition Queues? • Wannsind Condition Queues sinnvoll? • Wieverwendet man Condition Queues? 3. Ausblick auf Assignment 3
Teil 2 – Fragen Frage: Wiesoreichtesnicht, die Methodenread und write `synchronized` zumachen? Antwort: Dadurchwirdnurverhindert, dassProducer und Consumergleichzeitigschreiben/lesen. Hat keinenEinfluss auf die ReihenfolgederOperationen.
Teil 2 – Fragen Frage: Würdeesausreichen in den Methoden read und write anstelle von synchronizedeineBool’sche Variable als “Guard” zuverwenden? Antwort: Nein! read und write sindkeineatomarenOperationen! Bonus: Warumisti++ nichtatomar?
Teil 3 – Fragen Frage: Reichtsynchronized(this) in den jeweiligenrun-Methoden von Producer und Consumer? Antwort: Nein, denn this istjeweils an einanderesObjektgebunden. Zweiunabhängige locks Schliessensichgegenseitignichtaus
Teil 3 – Fragen Frage: WelchesObjektsolltestattdessenalsgemeinsamer “Monitor” verwendetwerden? Antwort: Egal, solangebeide das selbeObjektverwenden. Am einfachstennimmt man die gemeinsameInstanz von UnsafeBuffer.
Teil 3 – Fragen Vorteile/Nachteile von SynchronisationderProducer/ConsumergegenüberSynchronisation des Buffers? Vorteile: • Man kannbeliebigeBufferverwenden (auchUnsafeBuffer) • ErmöglichtzusätzlicheAktionen, die ausgeführtwerdenmüssen, bevorandere Threads den Bufferverwenden Nachteile • MehrAufwand • Fehleranfällig (besonderswennneueProzessehinzukommen)
Condition queue ≡ Intrinsic queue • EineErweiterung des Monitor-Locks in Java • Situation: Thread fährtnurunterfolgendenBedingungen fort • ExklusivenZugriff auf eine Resource (Lock) • Preconditions erfüllt Wiegeht man mitSituationen um, wo (2.) nichterfülltist?
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 intread() { while(!isFull) { wait(); } int d = data; isFull = false; notifyAll(); return d; }
notify versus notifyAll a.notifyAllwecktalle Threads, die auf a warten (via a.wait()) • Die geweckten Threads verlangenalleexlusiven Lock auf a. • Konkurrierenauchmitallenanderen Threads, die diesen Lock wollen • Jeder Thread wirdeinmal die Chance haben, fortzufahren (Deadlock/Livelockausgenommen)
notify versus notifyAll a.notifyweckteinen (!) der Threads, die auf a warten (via a.wait()) • Welcherderwartenden Threads aufgewecktwird, hängt von derImplementierungab (könnteauchzufälligsein) • Verlangen Lock auf a; konkurrierenmitallenanderen Threads, die ebenfalls Lock auf awollen. • Anderewartende Threads bleibenwartendbisjemandsie “notified”
notify vs. notifyAll - Empfehlung • EinfacherGrundsatz: ImmernotifyAll • ErweiterterGrundsatz: Wennihrdarübernachdenkenmüsst, ob notifyanstattnotifyAllfunktioniert, nehmtnotifyAll • Szenariofürnotify: Es istegalwelcher Thread aufgewecktwird. • Beispiel: Pool von Workerthreads. Es spieltkeineRollewelcherder Worker aufgewecktwird. Jederkann die Arbeitverrichten.
Producer Consumer FlagBufferbuf; voidrun() { while(true) { if(!buf.isFull) buf.wait(); synchronized(buf) { intcounter = buf.read)); buf.isFull = false; if(counter >= 9999) { return; } } //consumecounter … }//end-while }//end-run FlagBufferbuf; voidrun() { for(intcounter = 0; counter < 10000; i++) { if(buf.isFull) { buf.wait(); } synchronized(buf) { buf.write(counter); buf.isFull = true; } }//end-for }//end-run Ist dieser Code korrekt?
Producer Consumer FlagBufferbuf; voidrun() { while(true) { synchronized(buf) { while(!buf.isFull) buf.wait(); intcounter = buf.read)); buf.isFull = false; if(counter >= 9999) { return; } notifyAll(); } //end-synchronized //consumecounter … } //end-while } //end-run FlagBufferbuf; voidrun() { for(intcounter = 0; counter < 10000; i++) { synchronized(buf) { while(buf.isFull) buf.wait(); buf.write(counter); buf.isFull = true; notifyAll(); } //end-synchronized } //end-for } //end-run Ist dieser Code korrekt?
Producer Consumer FlagBufferbuf; voidrun() { while(true) { synchronized(buf) { while(!buf.isFull) buf.wait(); intcounter = buf.read)); buf.isFull = false; if(counter >= 9999) { return; } buf.notifyAll(); }//end-synchronized //consumecounter … }//end-while }//end-run FlagBufferbuf; voidrun() { synchronized(buf) { for(intcounter = 0; counter < 10000; i++) { while(buf.isFull) buf.wait(); buf.write(counter); buf.isFull = true; buf.notifyAll(); } //end-for }//end-synchronized }//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.“
Mergesort Gegeben: Liste L mit n Zahlen (int) Gesucht: SortierteListe L’ Algorithmus: • Teile L auf in zwei Listen derLänge n/2 • SortierejedederUnterlistenrekursivmitMergesort • Führe die zweisortiertenUnterlistenzusammen EndederRekursion: Listen mitnur 1 Element
Beispiel (Zerlegung) 5, 4, 3, 2, 1, 0 5, 4, 3 2, 1, 0 5, 4 3 2, 1 0 5 4 2 1
Zusammenführen (merging) Beispiel • L1: 0 5 • L2: 3 4 45 Ausgabe: Per Definition Vergleichen Kopieren Zeiger bewegen Fertig! 0 3 4 5 45
Zusammenführen, Beispiel 0, 1, 2, 3, 4, 5 3, 4, 5 0, 1, 2 4, 5 3 1, 2 0 5 4 2 1
Mergesort, parallelisiert • WelcheTeile des Algorithmuskönnenparallelisiertwerden? • Unterlistensortieren • ZweiUnterlistenzusammenführen • Synchronisationsprobleme? • ZweiUnterlistenerstzusammenführen, wennsiefertigsortiertsind • Performance • Anzahl Threads • LängederListe
Performance-Messung n ≔ LängederListe t ≔ Anzahl Threads Frage: Wieviele Threads sind optimal?
Wiemisst man Zeit? • System.currentTimeMillis() • NichtjedeMillisekundeaktualisiert • Nicht auf Millisekundengenau • System.nanoTime() • Präzision in Nanosekunden • Nicht auf Nanosekundengenau • FürunsistcurrentTimeMillis() gut genug
Wiemisst man Zeit • long start; • long end; • start = System.currentTimeMillis(); • //zumessenderVorgang • end = System.currentTimeMillis(); • System.out.println(“Time elapsed: “ + (end-start));
Auf einen Thread warten Welche Möglichkeiten kennt ihr? • Busywait (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 ourownstuff in themeantime • try { • t.join(); //waitfortheotherthreadto „catch up“ • } catch(InterruptedException e) { /* ignore */ } t.start(); t.join();
ZubeantwortendeFragen • Ist die parallele Version schneller? • Nichtselbstverständlich: Verwaltungsaufwand • Wieviele Threads bietenoptimale Performance? • WelchenEinflusshaben • CPU Modell • CPU Taktfrequenz • Anzahl “Cores” • verfügbarerArbeitsspeicher • verfügbarerAdressraum (32Bit vs. 64Bit)
UnangenehmeRealität • Ideale Welt • N Prozessorenführenzueinem N-fachenAnstiegderRechenleistung • Realität • JederzusätzlicheProzessor muss auchverwaltetwerden. • VieleAbläufekönnenerst gar nichtparallelisiertwerden (rein Sequentiell) • Kommunikation/SynchronisationkönnenWartezeitenzurFolgehaben