Threading Building Blocks - IPD Tichy
Transcription
Threading Building Blocks - IPD Tichy
Software Engineering für moderne parallele Plattformen 6. Parallelität in C/C++ Dipl.-Inform. Korbinian Molitorisz M. Sc. Luis Manuel Carril Rodriguez IPD Prof. Dr. Walter F. Tichy – Lehrstuhl für Programmiersysteme KIT – Universität des Landes Baden-Württemberg und nationales Forschungszentrum in der Helmholtz-Gemeinschaft www.kit.edu Agenda Parallele Programmierung in C/C++ Allgemeines Ausgewählte Bibliotheksansätze Pthreads Threading Building Blocks OpenMP 2 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Allgemeines Seit 2011 in C/C++ gibt es Unterstützung für Parallelität -> C++11 Ansätze zur Erweiterung: Bibliotheken Spracherweiterungen 3 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Pthreads – Überblick (1) Bibliothek zum Arbeiten mit Fäden in C POSIX Threads IEEE-Standard (IEEE Std. 1003.1, 2004) „Portable Operating System Interface“ Beschreibt Schnittstelle zwischen Applikationen und Betriebssystem Unix-Derivate halten sich mehr oder weniger daran Inzwischen auch für Windows erhältlich (Pthreads-win32) Verteilung von Fäden auf Prozessoren / Kerne sowie Ablaufplanung übernimmt das Betriebssystem 4 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Pthreads – Überblick (2) Pthreads Schnittstelle enthält mehr als 60 Funktionen Bezeichner beginnen mit pthread_ Typ Beschreibung pthread_ Basisfunktionen (z.B. create, …) pthread_attr_ Fadenattribute (z.B. Scheduling, Priorität) pthread_mutex_ Mutex-Variablen (z.B. init, lock, unlock) pthread_mutexattr_ pthread_cond_ Mutex-Attribute (z.B. Mutex-Typ normal, recursive, …) Bedingungsvariablen („wait“, „signal“) fadenspezifische Daten (erzeugt z.B. pthread_key_ 5 06.06.2013 SEMPP 2013, Kapitel 6 Datenschlüssel, der für alle Fäden sichtbar ist) IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme PThreads Erzeugen von Fäden (1) Jede Quelltextdatei, die die Pthreads-Bibliothek verwendet, muss pthread.h einbinden Initial hat das main()-Programm einen Hauptfaden Alle anderen Fäden werden explizit vom Programmierer erzeugt pthread_create erzeugt und startet einen neuen Faden 6 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme PThreads Beispiel #include <pthread.h> #include <stdio.h> //Funktion, die ein Faden ausführen soll void* hello(void* id) { printf("%d: Hello world!\n", *((int*) id)); return 0; } int main(int argc, char* argv[]) { const int COUNT = 5; int i; pthread t thread[COUNT]; int ids[COUNT]; for (i = 0; i < COUNT; i++) { ids[i] = i; int retval = pthread create(&thread[i], NULL, hello, &ids[i]); if (retval) { perror("pthread_create failed"); return 1;} } for (i = 0; i < COUNT; i++) pthread_join(thread[i], NULL); return 0; } 7 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme PThreads Erzeugen von Fäden (2) int pthread_create ( pthread_t const pthread_attr_t void pthread_attr_t void *tid, *attr, *(*start) (void *), *arg); Erzeugt einen Faden, der die Funktion start mit Argument arg ausführt Aufruf ist asynchron implementierungsabhängig: Max. Anzahl erzeugbarer Fäden Argument attr spezifiziert optionale Attribute Z.B. für Scheduling, Priorität (Standard: NULL) Nach der Fadenerzeugung wird tid für Identifikation benutzt Fäden können selbst weitere Fäden erzeugen 8 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme PThreads Beenden von Fäden Verschiedene Möglichkeiten: Wenn main() endet, werden alle Fäden zerstört Faden kehrt von seiner Start-Routine zurück Faden ruft pthread_exit auf Achtung: „Aufräumarbeiten“ werden nicht durchgeführt (z.B. können Dateien offen bleiben) Faden wird durch Aufruf von pthread_cancel durch einen anderen Faden abgebrochen Der gesamte Prozess wird durch den Aufruf von exec oder exit beendet 9 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme PThreads Beitreten eines Fadens („Join“) int pthread_join( pthread_t void Hauptfaden thread, **value_ptr); pthread_create() Arbeiterfaden pthread_join() pthread_exit() der aufrufende Faden blockiert, bis der Faden mit der spezifizierten ID thread terminiert entsprechender Faden muss “joinable” sein val_ptr: Rückgabewert des beigetretenen Threads abfangen zurückgegebener Typ: (void *) NULL, wenn kein Rückgabewert erwartet wird 10 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme PThreads Lebenszyklus eines Fadens erzeugt Warten beendet ready blocked unterbrochen Fertig zum Ausführen, wartet jedoch auf Prozessor Warten auf Ressource Für Ausführung eingeteilt (scheduled) running Wird gerade ausgeführt Fertig, cancel oder exit terminated 11 06.06.2013 SEMPP 2013, Kapitel 6 Kann nicht ausgeführt werden (wartet z.B. auf Mutex, E/A, ..) Von start-Funktion zurückgekehrt oder pthread_exit oder cancel IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme PThreads Mutex-Variablen (1) Mutex-Variable pthread_mutex_t mymutex; Muss vor Gebrauch initialisiert werden (zwei Möglichkeiten): int pthread_mutex_init( mutex, attr ); pthread_mutex_t mtx1 = PTHREAD_MUTEX_INITIALIZER //Makro Mutex-Variable wird vom aufrufenden Thread gehalten, bis dieser sie wieder freigibt (“unlock”) 12 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme PThreads Mutex-Variablen (2) Sperren / Entsperren int pthread_mutex_lock( mutex ); int pthread_mutex_unlock( mutex ); 13 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme PThreads Mutex-Variablen - Beispiel #define NUMTHREADS 4 pthread_mutex_t gMutex; int g_sum = 0; void *threadFunc(void *arg) { int mySum = bigComputation(); pthread_mutex_lock( &gMutex ); g_sum += mySum; // kritischer Abschnitt pthread_mutex_unlock( &gMutex ); } main() { pthread_t hThread[NUMTHREADS]; pthread_mutex_init( &gMutex, NULL ); for (int i = 0; i < NUMTHREADS; i++) pthread_create(&hThread[i],NULL,threadFunc,NULL); } 14 for (int i = 0; i < NUMTHREADS; i++) pthread_join(hThread[i]); printf (“Global sum = %f\n”, g_sum); 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme PThreads Mutex-Typen int pthread_mutexattr_settype( pthread_mutexattr_t *attr, int type); PTHREAD_MUTEX_NORMAL Faden, der bereits gesetzte Sperre noch einmal sperren will, erzeugt Verklemmung (Deadlock) trylock() verwenden. Verhalten undefiniert, wenn unlock bei nicht gesetzter Sperre durchgeführt wird bzw. wenn zweiter Faden Sperre eines anderen Fadens entsperren will. PTHREAD_MUTEX_ERRORCHECK Obige Operationen erzeugen Fehlermeldungen. PTHREAD_MUTEX_RECURSIVE Faden, der bereits Sperre hält, kann sie erneut akquirieren. Gleiche Anzahl von unlock-Aufrufen nötig, um Sperre freizugeben. Rest der obigen Operationen erzeugt Fehlermeldung. PTHREAD_MUTEX_DEFAULT Im Standard: „An implementation may map this mutex to one of the other mutex types“ 15 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme PThreads Bedingungsvariablen Bedingungsvariablen (Condition Variables) Signal / Wait – Mechanismus in PThreads Warten, bis bestimmte Bedingung erfüllt ist Hintergrund: Effizient warten (ohne Polling) und Sperren in der Zwischenzeit aufgeben, um Fortschritt zu ermöglichen Wert der Bedingungsvariablen bestimmt, ob Faden warten muss oder mit Ausführung fortfahren darf 16 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme PThreads Bedingungsvariablen Prinzip Faden 1 Faden 2 pthread_mutex_lock(&mtx); while (state != 5) { pthread_cond_wait(&cv,&mtx); } pthread_mutex_unlock(&mtx); pthread_mutex_lock(&mtx); state++; pthread_cond_broadcast(&cv); pthread_mutex_unlock(&mtx); acquiriere mutex while (negierte Bedingung ist wahr) {wait auf Bedingungsvariable} //else Kritischer Abschnitt gib mutex frei 17 06.06.2013 SEMPP 2013, Kapitel 6 Mutex wird automatisch freigegeben, solange der Faden wartet IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme PThreads Bedingungsvariablen pthread_cond_t mycond; Initialisieren vor Gebrauch bzw. Zerstören einer Bedingungsvariable pthread_cond_init( cond, attr ) pthread_cond_destroy( cond ) pthread_cond_wait( cond, mutex ) Blockiert Faden, bis er ein entsprechendes Signal über eine Bedingungsvariable erhält (mutex wird währenddessen freigegeben) Muss mit gesperrter mutex-Variablen aufgerufen werden (sonst undef.) pthread_cond_signal( cond ) Weckt mindestens einen der blockierten Fäden auf pthread_cond_broadcast( cond ) Alle blockierten Fäden werden aufgeweckt 18 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. 18Tichy Lehrstuhl für Programmiersysteme PThreads Bedingungsvariablen Signale werden nicht gespeichert Wenn kein Faden wartet, ist ein zu dem Zeitpunkt gesendetes Signal verloren (“lost signal”) Ein Faden verhungert, wenn er auf ein Signal wartet, das nicht mehr gesendet wird 19 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Überblick (1) Intels Bibliotheksansatz zum parallelen Programmieren in C/C++ Open Source (aktuelle Version: 4.1) Ein TBB-Faden intern realisiert als Hüllklasse um plattformspezifischen Faden (Win32 API, PThreads) Konstrukte auf höherer Abstraktionsebene als PThreads, z.B. Parallele Schleifen Fließband (Pipeline) Aufgabenorientierter Parallelismus (Task Parallelism) 20 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Überblick (2) www.threadingbuildingblocks.org Bibliothek kann gleichzeitig auch mit anderen Bibliotheken oder OpenMP verwendet werden Im Folgenden einige ausgewählte Inhalte 21 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Überblick (3) – Historisches 1988 Languages 1995 Threaded-C continuation tasks task stealing Cilk space efficient scheduler cache-oblivious algorithms Pragmas Chare Kernel small tasks STL generic programming Libraries OpenMP fork/join tasks OpenMP taskqueue while and recursion JSR166 containers STAPL recursive ranges 2001 ECMA CLI parallel iteration classes McRT Intel Threading Building Blocks 2006 “Key influences on design of Intel Threading Building Blocks” Reinders, Intel Threading Building Blocks, O’Reilly, 2007 22 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Überblick (4) Konstrukte parallel_for parallel_do parallel_reduce pipeline parallel_sort parallel_scan Container concurrent_hash_map concurrent_queue concurrent_vector Aufgabenabwickler (Task Scheduler) Konstrukte für Synchronisation atomic, spin_mutex, spin_rw_mutex, queuing_mutex, queuing_rw_mutex, mutex Konstrukte zur Speicherallokation cache_aligned_allocator scalable_allocator 23 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Minimales Programmgerüst Initialisiert Task Scheduler #include “tbb/task_scheduler_init.h” using namespace tbb; int main() { task_scheduler_init init; return 0; } 24 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Container Die Container-Datenstrukturen in der „Standard Template Library“ von C++ sind im parallelen Fall nicht anwendbar Threading Building Blocks stellt folgende Container zur Verfügung concurrent_hash_map Entwickler kann Hash- und Vergleichsoperationen selbst definieren concurrent_vector Größe dynamisch änderbar concurrent_queue Schlange mit blockierenden und nicht blockierende Operationen (pop bzw. pop_if_present) Aktives Warten beim Blockieren – Wartezeiten sollten kurz sein! 25 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Vorbereitung zu Parallel For Exkurs: Funktionsobjekte in C++ (1) Konstruktion einer Klasse die wie eine Funktion benutzt werden kann Überlädt „operator()“ Operator für Methodenaufruf Kann Zustand kapseln (im Gegensatz zu reinen Funktionszeigern) Es kann mehrere Objektinstanzen geben class MyFunc { ... public: void operator() (...) {...} }; MyFunc f; f(...); //ruft MyFunc::operator() auf 26 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Vorbereitung zu Parallel For Exkurs: Funktionsobjekte in C++ (2) Beispiel: Akkumulation template<class T> class AccumulateSum { T accValue; public:AccumulateSum(T initValue){accValue=initValue;} void operator()(T x) {accValue +=x;} T result() const {return accValue;} }; void test (list<double>& myList) { AccumulateSum<double> accSumObj(0); //rufe accSumObj() für jedes Element der Liste auf … for_each(myList.begin(), myList.end(), accSumObj); cout << "Die Summe ist: "<<accSumObj.result() <<‘n’; } 27 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Schleifen Beispiel: Sequenzielles Manipulieren eines Arrays void ChangeArraySerial (int* a, int n) { for (int i=0; i<n; i++) { Foo (a[i]); } } 28 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Parallel For #include "tbb/blocked_range.h" #include "tbb/parallel_for.h„ using namespace tbb; Aufgabe, die parallel ausgeführt werden soll class ChangeArray { int* a; public: ChangeArray(int* _a) {a=_a;} void operator()(const blocked_range<int>& r) const { for (int i=r.begin(); i!=r.end(); i++){ Foo(a[i]); }}}; void ChangeArrayParallel (int* a, int n) { parallel_for(blocked_range<int>(0,n,AGrainSize), ChangeArray(a)); } Rufe generische Funktion auf: parallel_for<range, body> hier mit: range blocked_range; body ChangeArray 29 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Parallel For #include "tbb/blocked_range.h" • Repräsentiert Intervall #include "tbb/parallel_for.h„ using namespace tbb; • Allgemein kann range ein Intervall rekursiv in zwei Teile aufteilen, bis eine class ChangeArray { vordefinierten Größe erreicht ist int* a; (unterschiedliche Arten der Aufteilung public: möglich) ChangeArray(int* _a) {a=_a;} • blocked_range: eindimensionaler, void operator()(const blocked_range<int>& r) const { gleichmäßig aufgeteilter Iterationsraum for (int i=r.begin(); i!=r.end(); i++){ über int Foo(a[i]); }}}; iteriere von 0 Kleinste bis n-1 Intervallgröße void ChangeArrayParallel (int* a, int n) { parallel_for(blocked_range<int>(0,n,AGrainSize), ChangeArray(a)); } 30 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Parallel For #include "tbb/blocked_range.h" Aus ChangeArray wird Funktionsobjekt erzeugt #include "tbb/parallel_for.h„ using namespace tbb; • Für jeden Arbeiterfaden werden durch parallel_for separate Kopien des class ChangeArray { Funktionsobjekts mit unterschiedlichen int* a; Intervallgrenzen erzeugt public: ChangeArray(int* _a) {a=_a;} void operator()(const blocked_range<int>& r) const { for (int i=r.begin(); i!=r.end(); i++){ modifizierter Foo(a[i]); }}}; Schleifenrumpf aus dem sequenziellen Fall void ChangeArrayParallel (int* a, int n) { parallel_for(blocked_range<int>(0,n,AGrainSize), ChangeArray(a)); } 31 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Parallel For und Lambda-Ausdrücke Anmerkung: Hätte C++ folgende Spracherweiterung, könnte man auf explizite Erzeugung von Funktionsobjekten verzichten //mit Lambda-Ausdruck void ParallelApplyFoo(size_t n, int x) { parallel_for ( blocked_range<size_t>(0,n,10), <>(const blocked_range<size_t>& r) { for(size_t i=r.begin(); i<r.end();++i) Schleifenrumpf Foo(i,x); } Im C++ Standardkomittee vorgeschlagene Erweiterung für ); Lambda-Ausdrücke (vgl. Willcock, „Lambda expressions and closures for C++“, } 2006 und Järvi, „Lambda Functions for C++0x”, ACM SAC, 2008). 32 06.06.2013 Bedeutung von „<>“: Compiler soll automatisch den nachfolgenden Ausdruck in ein Funktionsobjekt konvertieren. Lambda-Ausdrücke würden generell die Übergabe von Code-Blöcken als Parameter erlauben und „Schreibarbeit“ IPD Prof. Dr. Walter F. Tichy SEMPP 2013, Kapitel 6 vereinfachen. (vgl. auch C# Delegates) Lehrstuhl für Programmiersysteme Threading Building Blocks Parallel For – Prinzip der Gebietszerlegung - Aufgabenbaum Teile Intervall („range“) auf [data, data+N] [data, data+N/2] [data+N/2, data+N] …rekursiv… [data, data+N/k] … bis grainsize [data, data+grainsize] 33 06.06.2013 SEMPP 2013, Kapitel 6 Tasks für „Work Stealing“ IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Partitionierer Spezifiziert Strategie für Ausführung von Schleifen parallel_for u.a. Konstrukte rufen Partitionierer auf, wenn range aufgeteilt werden soll jedes range-Objekt ist mit einem partitioner-Objekt assoziiert Partitionierer bestimmt wie Intervall aufgeteilt werden soll Gebietszerlegung Bis zu welcher Intervallgröße „grainsize“ 34 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Partitionierer Partitionierer kann z.B. als optionaler dritter Parameter für parallel_for verwendet werden simple_partitioner (TBB 2.1 Standard) Aufteilung bis zu kleinsten Teilen der Größe „grainsize“ auto_partitioner (TBB 2.2+ Standard) Gerade genug Aufteilungen, um Lastverteilung zu ermöglichen. Bei Bedarf werden neue Partitionen erzeugt. affinity_partitioner Wie auto_partitioner, jedoch mit besserer Cache-Affinität. Benachbarte Iterationen werden ggf. auf gleiche Arbeiterfäden verteilt. 35 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Fließbänder (1) Fließband (Pipeline): Besteht aus Sequenz von Filtern Auszug: class pipeline { public: ... void add_filter( filter& f ); void run(size_t max_num_of_live_tokens); }; Kontrolliert die Anzahl der parallelen Tasks, die maximal existieren können Ein Filter f arbeitet sequenziell oder parallel mit mehreren Fäden Übergabe von Elementen zwischen Stufen erfolgt über Zeiger 36 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Fließbänder (2) Filter werden von Klasse filter geerbt Filter überschreibt virtual void* operator()( void * item ); Diese Methode wird von der Pipeline aufgerufen, um innerhalb eines Filters ein Element zu verarbeiten Parameter item zeigt auf das Element, das verarbeitet werden soll Der Rückgabewert zeigt auf das Element, das der nächsten Stufe übergeben werden soll Beispiel mit dreistufiger Pipeline: Lese Datei ein, transformiere Kleinbuchstaben in Großbuchstaben, schreibe Ergebnis in Datei 37 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Fließbänder (3) – Beispiel ...//FILE* input_file = fopen(InputFileName,"r"); ...//FILE* output_file = fopen(OutputFileName,"r"); // Create the pipeline tbb::pipeline pipeline; Datei // Erzeuge Eingabefilter (lese Datei ein, generiere Strom) MyInputFilter input_filter( input_file ); pipeline.add_filter( input_filter ); // Verarbeitungsfilter (transf. zu Großbuchstaben) MyTransformFilter transform_filter; pipeline.add_filter( transform_filter ); Filter 1 Zeichenblöcke einlesen Filter 2 (kleingroß) // Ausgabefilter für Dateiausgabe MyOutputFilter output_filter( output_file ); pipeline.add_filter( output_filter ); // Führe Pipeline aus pipeline.run( MyInputFilter::n_buffer ); pipeline.clear(); Filter 3 Dateiausgabe fclose( output_file ); fclose( input_file ); ... 38 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Fließbänder (4) – Beispiel //“Mittlerer“ Filter //transformiert Keinbuchstaben zu Großbuchstaben class MyTransformFilter: public tbb::filter { public: MyTransformFilter(); void* operator()( void* item ); //überschreibe Methode }; ... Konstruktor etc. ... Operator() bekommt Zeiger auf Block von Zeichen und liefert einen Zeiger auf konvertiertem Block, der der nächsten Stufe übergeben werden soll. void* MyTransformFilter::operator()( void* item ) { MyBuffer& b = *static_cast<MyBuffer*>(item); // konvertiere Puffer zu Großbuchstaben return &b; • Explizite Typkonvertierung, die zur } Übersetzungszeit geprüft wird • Semantik: Speicher, worauf item zeigt, wird als MyBuffer benutzt 39 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Fließbänder (5) Die Bibliothek unterstützt zur Zeit nur lineare Fließbänder Nicht-lineare Fließbänder könnten behelfsmäßig mit Hilfe einer topologischen Sortierung der Stufen durch linearer Fließbänder realisiert werden A 1 C B 40 06.06.2013 SEMPP 2013, Kapitel 6 2 3 D E 4 5 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Konstrukte zur Synchronisation „schlafen“ beim Warten Fair Reentrant (bevorzugt länger wartende Fäden) (Bei Rekursion anwendbar) mutex BS abhängig Nein Ja spin_mutex Nein Nein Nein queuing_mutex Ja Nein Nein spin_rw_mutex Nein Nein Nein queuing_rw_mutex Ja Nein Nein (sonst Schleife) • “Scoped Locking”-Muster wird benutzt • Sperre in Wächterklasse • Kein explizites Freigeben, Destruktor der Wächterklasse gibt Sperre automatisch frei • Sicher beim Auslösen von Ausnahmen 41 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Atomare Operationen atomic<T> bietet atomare Operationen auf Typ T =x Lese Wert von x x= Schreibe Wert von x und gib ihn zurück x.fetch_and_store(y) Setze y=x und liefere alten x-Wert x.fetch_and_add(y) Setze x+=y und liefere alten x-Wert x.compare_and_swap(n,z) Wenn x==z, setze x=n; liefere in beiden Fällen alten x-Wert zurück • Interne Implementierung bildet atomare Operationen auf native, atomare Prozessorbefehle ab • Vordefinierte Abbildungen für verschiedene Prozessor-Plattformen • Z.B. compare_and_swap ... mov rax,r8 lock cmpxchg [rcx],dl ret ... 42 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Aufspalten von Aufgaben Illustration am Beispiel // Fibonacci sequenziell long SerialFib( long n ) { if( n<2 ) return n; else return SerialFib(n-1)+SerialFib(n-2); } 43 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Aufspalten von Aufgaben • Aufgaben müssen mit allocateMethoden Speicher reservieren Parallele Version • Erzeugt Wurzel des Aufgabenbaums • Konstruktor von FibTask long ParallelFib( long n ) { long sum; FibTask& a = *new(task::allocate_root()) FibTask(n,&sum); task::spawn_root_and_wait(a); return sum; } • Ausführen und warten bis fertig • Anmerkung: Hier steht Demonstration des Task-Konzepts im Vordergrund. Es gibt effizientere Möglichkeiten, Fibonacci-Zahlen zu berechnen. 44 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Threading Building Blocks Aufspalten von Aufgaben class FibTask: public task { public: const long n; long* const sum; FibTask( long n_, long* sum_ ):n(n_), sum(sum_) {} task* execute() { if( n<CutOff ) { *sum = SerialFib(n); } else { long x, y; FibTask& a = *new( allocate_child() ) FibTask(n-1,&x); FibTask& b = *new( allocate_child() ) FibTask(n-2,&y); set_ref_count(3); spawn( b ); spawn_and_wait_for_all( a ); //starte a, warte auf *sum = x+y; //alle Kinder, d.h. a,b } return NULL; }}; 45 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Ausblick Weitere Bibliotheken zur parallelen Programmierung in C/C++ C++11 In GCC ab 4.6 Einige Auszüge: Explizite Faden-Erzeugung aus einer Funktion oder Lambda Mutex (auch: rekursiv, zeitlich) Conditon Variablen (wait) Futures Operationen (parallel asynchrones Verhalten) Atomic Datentypen und Operationen 46 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Ausblick Weitere Bibliotheken zur parallelen Programmierung in C/C++ Boost (www.boost.org) Einige Auszüge: Klasse boost::thread erzeugt und verwaltet Faden Fäden können zu Gruppen hinzugefügt werden Sperren Konzepte Lockable: Sperre exklusiv für einen Faden Timed lockable: Blockierendes Warten auf Sperre kann zeitlich begrenzt werden SharedLockable: Wie z.B. bei Lese-Schreib-Sperre (mehrere Fäden können gleichzeitig lesen, aber nur einer schreiben) UpgradeLockable: Von gemeinsamer Nutzung einer Sperre auf exklusive Nutzung Barrieren Bedingungsvariablen … 47 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Ausblick Weitere Bibliotheken zur parallelen Programmierung in C/C++ QtConcurrent (trolltech.com) Konzepte auf niedriger Abstraktionsebene (Sperren, Semaphore, …) Konzepte auf höherer Abstraktionsebene QtConcurrent::map() wendet Funktion parallel auf mehrere Elemene an QtConcurrent::mappedReduced() nach map wird Ergebnis mit Reduktionsoperation zu einem einzigen Ergebnis verdichtet QtConcurrent::filter() benutzt Filterfunktion um Elemente zu entfernen QtConcurrent::filteredReduced() gefiltertes Ergebnis Ergebnis wird mit Reduktionsoperation zu einem einzigen Ergebnis verdichtet QtConcurrent::run() führt eine Funktion in einem neuen Faden aus QFuture wie Futures QFutureWatcher Überwachen einer QFuture 48 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme Ausblick Weitere Bibliotheken zur parallelen Programmierung in C/C++ GNU libstdc++ parallel mode Parallelisierte Algorithmen der Standard Template Library Z.B. Suche, Sortieren, numerische Algorithmen, … Im gcc V 4.3 „parallel mode“ bereits enthalten In Karlsruhe entstanden 49 06.06.2013 SEMPP 2013, Kapitel 6 IPD Prof. Dr. Walter F. Tichy Lehrstuhl für Programmiersysteme