This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
...
.
...........................
.
...
.
...
.
Threading Building Blocks
Computer Science Department, University of Crete
Parallel Programming
Βασίζεται σε slides του Paul Guermonprez
Pratikakis (CSD) TBB CS342, 2021 1 /56
...
.
...........................
.
...
.
...
.
Threading Building Blocks
C++ template library για ανάπτυξη παράλληλουλογισμικούΗ βιβλιοθήκη χρησιμοποιεί την έννοια των tasks για τηνέκφραση του παραλληλισμούΤα tasks ανατίθενται σε cores από τη βιβλιοθήκηδυναμικά
Διευκολύνει τον προγραμματισμόΚαλύτερη χρήση caches
Pratikakis (CSD) TBB CS342, 2021 2 /56
...
.
...........................
.
...
.
...
.
Πλεονεκτήματα της TBB
Λογικός παραλληλισμός αντί για threadsΠαραλληλισμός για ταχύτηταΣυμβατότητα με άλλες μορφές threadingΈμφαση σε data parallelismGenerics (templates)Έτοιμα patterns παραλληλισμού
Pratikakis (CSD) TBB CS342, 2021 3 /56
...
.
...........................
.
...
.
...
.
Μειονεκτήματα της TBB
Όχι για παραλληλισμό I/OΌχι για real-time εφαρμογές
“unfair” εκτέλεση των διαθέσιμων tasks, για λόγουςβελτιστοποίησης στις caches
Βιβλιοθήκη – Θέματα compilation
Pratikakis (CSD) TBB CS342, 2021 4 /56
...
.
...........................
.
...
.
...
.
Βασικές Ιδέες
Ορισμός παραλληλισμού με tasks αντί threadsΤο σύστημα αντιστοιχεί σε διαθέσιμα threads, υποστηρίζειnested tasks
Στόχος η κλιμακωσιμότητα (scalability)Χρησιμοποιεί έτοιμα αποδοτικά parallel patternsΑυτόματο load-balancing με work-stealing
Open Source: Linux, Windows, MacOS, FreeBSD, Solaris,XBox 360, …
void parallel_change_array(float ∗array, size_t M) {parallel_for(blocked_range<size_t>(0, M, IdealGrainSize),[=](const blocked_range<size_t>& r) -> void {for(size_t i = r.begin(); i != r.end(); i++ )array[i] ∗= 2;
});
}
Pratikakis (CSD) TBB CS342, 2021 15 /56
...
.
...........................
.
...
.
...
.
Task SchedulerΗ βιβλιοθήκη δημιουργεί ένα task scheduler αυτόματαόταν χρειάζεται threads και τον καταστρέφει αυτόματαόταν δεν χρειάζονται πιαΟ προγραμματιστής μπορεί να ελέγξει τηδημιουργία/καταστροφή του task scheduler (για αποφυγήoverhead)
#include <tbb/task_scheduler_init.h>
using namespace tbb;
int main (){task_scheduler_init init; // threads creationfloat A[N];initialize_array(A);parallel_change_array(A, N);return 0;
} // out of scope -> threads destruction
Παράμετρος στον constructor: maximum threadsPratikakis (CSD) TBB CS342, 2021 16 /56
...
.
...........................
.
...
.
...
.
Προγραμματισμός με generics ή lambda
Genericsclass ChangeArrayBody {float ∗array;
public:ChangeArrayBody(float ∗a): array(a) {}void operator()( const blocked_range<size_t>& r ) const{for (size_t i = r.begin(); i != r.end(); i++ ){array[i] ∗= 2;
}}
};
void parallel_change_array(float ∗array, size_t M) {parallel_for(blocked_range<int>(0, M, IdealGrainSize),ChangeArrayBody(array));
}
Pratikakis (CSD) TBB CS342, 2021 17 /56
...
.
...........................
.
...
.
...
.
Προγραμματισμός με generics ή lambda
Lambdavoid parallel_change_array(float ∗array, size_t M) {parallel_for(blocked_range<size_t>(0, M, IdealGrainSize),[=](const blocked_range<size_t>& r) -> void {for(size_t i = r.begin(); i != r.end(); i++ )array[i] ∗= 2;
});
}
Pratikakis (CSD) TBB CS342, 2021 18 /56
...
.
...........................
.
...
.
...
.
Generics και Lambda functions
Και τα δύο δίνουν την ίδια ταχύτητα και overheads“Syntactic sugar” τρόπος για να εκφραστεί το ίδιο νόημαΚάποιες φορές χρειάζεται generic έκφραση, δε γίνεται μεlambda
Παραδείγματα με lambda όταν γίνεται (μικρότερα)Lambda functions: υποστήριξη στη C++11, παράμετρος-std=c++0x στον compiler
Η δομή parallel_reduce χωρίζει το αρχικό range σεsubranges όπως και η parallel_forΗ συνάρτηση RealBody καλείται για τα subranges, και τααποτελέσματά της “ενώνονται” από τη συνάρτησηreductionFuncPratikakis (CSD) TBB CS342, 2021 20 /56
...
.
...........................
.
...
.
...
.
Παράδειγμα parallel_reduce
#include <limits>
// Find index of smallest element in a[0...n-1]size_t serialMinIndex(const float a[], size_t n) {float value_of_min = numeric_limits<float>::max();size_t index_of_min = 0;for(size_t i = 0; i < n; ++i) {float value = a[i];if(value < value_of_min) {value_of_min = value;index_of_min = i;
}}return index_of_min;
}
Pratikakis (CSD) TBB CS342, 2021 21 /56
...
.
...........................
.
...
.
...
.
Παράδειγμα parallel_reduce#include <limits>#include <tbb/blocked_range.h>#include <tbb/parallel_reduce.h>
Η δομή parallel_sort χωρίζει το αρχικό range σεsubranges όπως και η parallel_forΠαράλληλη quicksort — Balance με work-stealingPratikakis (CSD) TBB CS342, 2021 23 /56
...
.
...........................
.
...
.
...
.
Tasks
Tasks: Units of workΔιαχωρίζουν την έκφραση του παραλληλισμού από τηνεκτέλεσή τουTBB task scheduler
Per threadUnfair, non-preemptiveΧαμηλό overhead, αντιστοίχηση tasks/threadsLoad-balance: work-stealing
Pratikakis (CSD) TBB CS342, 2021 24 /56
...
.
...........................
.
...
.
...
.
Παράδειγμα: Fibonacci (πάλι)
Toy benchmarkUnbalanced υπολογισμός
long serial_fib(long n) {if (n < 2)return n;
elsereturn serial_fib(n-1) + serial_fib(n-2);
}
Pratikakis (CSD) TBB CS342, 2021 25 /56
...
.
...........................
.
...
.
...
.
parallel_invoke
void parallel_fib(int n, long &sum) {if (n < 2)sum = n;
Οι συναρτήσεις που δέχεται η parallel_invoke δενμπορούν να δέχονται ή να επιστρέφουν τιμές.
Εύκολη λύση με lambda εκφράσεις, capture μεταβλητώνby reference
Εκτελούνται ως χωριστά tasks από το διαθέσιμοπαραλληλισμόΣτο τέλος της parallel_invoke έχουν εκτελεστεί όλα
Pratikakis (CSD) TBB CS342, 2021 27 /56
...
.
...........................
.
...
.
...
.
Task Groups
Ένας τρόπος να ομαδοποιηθούν tasks παράλληλα μεταξύτους
Διαφορετικά task groups μπορεί να μην είναι παράλληλα (ήνα είναι)
Μπορεί να μεγαλώσει δυναμικάΓια πάρα πολλά tasks στο ίδιο task_group μπορεί να γίνειαργό
Task spawn: σειριακό, synchronization operation
Pratikakis (CSD) TBB CS342, 2021 28 /56
...
.
...........................
.
...
.
...
.
Task Groups Fibonacci
#include <tbb/task_group.h>
using namespace tbb;
int Fib(int n) {if( n<2 ) {return n;
} else {int x, y;task_group g;g.run([&]{x=Fib(n-1);}); // spawn a taskg.run([&]{y=Fib(n-2);}); // spawn another taskg.wait(); // wait for both tasks to completereturn x+y;
}}
Pratikakis (CSD) TBB CS342, 2021 29 /56
...
.
...........................
.
...
.
...
.
Tasks χωρίς lambda
“Παραδοσιακό” APITask classAllocate, Construct
Recursive
long parallel_fib(long n) {long sum;FibTask& a = ∗new(Task::allocate_root()) FibTask(n, &sum);Task::spawn_root_and_wait(a);return sum;
}
Pratikakis (CSD) TBB CS342, 2021 30 /56
...
.
...........................
.
...
.
...
.
Tasks χωρίς lambda
public:const long n;long ∗const sum;FibTask(long n_, long ∗sum_) : n(n_), sum(sum_) {} // constructor
task∗ execute() { // override virtual execute for task bodyif( n < 1000 ) {∗sum = serial_fib(n);
} else {long x, y;FibTask& a = ∗new(allocate_child()) FibTask(n-1, &x);FibTask& b = ∗new(allocate_child()) FibTask(n-2, &x);// keep count of children tasks (count +1 for waiting on 2 children)set_ref_count(3);spawn(b);spawn_and_wait_for_all(a);sum = x + y;
}return NULL;
}};
Pratikakis (CSD) TBB CS342, 2021 31 /56
...
.
...........................
.
...
.
...
.
Παράλληλα Containers
Η βιβλιοθήκη TBB περιέχει παράλληλες υλοποιήσειςχρήσιμων containers
Τα C++ STL containers δεν είναι γραμμένα για χρήση μεπαραλληλισμόΗ χρήση STL containers παράλληλα μπορεί να τα κάνειcorruptΣυνήθης χρήση: wrap με lock
TBB ContainersFine-grain συγχρονισμόςΧαμηλότερη απόδοση σε σειριακή χρήσηΚαλύτερη κλιμακωσιμότητα σε παράλληλο κώδικαΔεν χρειάζονται τον TBB schedulerΣυμβατά με pthreads, OpenMP
Pratikakis (CSD) TBB CS342, 2021 32 /56
...
.
...........................
.
...
.
...
.
Container API για παραλληλισμό
Η C++ STL περιέχει containers με APIs που απαιτούνσειριακή εκτέλεσηΠαράδειγμα
extern std:queue q;if (!q.empty()) {// race: first thread that pop()s will make queue emptyitem = q.front();q.pop();
}
Λύση: concurrent_queueΝέο API: pop_if_present()
Pratikakis (CSD) TBB CS342, 2021 33 /56
...
.
...........................
.
...
.
...
.
Concurrent Queue
concurrent_queue<T>Διατηρεί την τοπική FIFO σειρά
Αν ένα thread εισάγει δύο τιμές και ένα άλλο threadβγάλει, η μεταξύ τους σειρά θα είναι ίδια
Η μέθοδος push(const T&) εισάγει αντίγραφο τουαντικειμένου στο τέλος της ουράςΔύο είδη pop:
pop(T&): Blocking, περιμένειpop_if_present(T&): Non-Blocking, δεν περιμένει
Η μέθοδος size() επιστρέφει ακέραιο αριθμό μεπρόσημο, signed
Αρνητικό αποτέλεσμα μετρά πόσα pop περιμένουν
Pratikakis (CSD) TBB CS342, 2021 34 /56
...
.
...........................
.
...
.
...
.
Καλή χρήση Concurrent Queue
Κάθε queue είναι ουσιαστικά bottleneckΠρέπει να διατηρεί την first-in-first-out σειρά
Κάθε thread που προσπαθεί να πάρει μια τιμή μπορεί ναχρειαστεί να περιμένει μέχρι να υπάρξει μια τιμήΑν ένα thread εισάγει μια τιμή και ένα άλλο thread τηνβγάλει, τα δεδομένα πρέπει να μεταφερθούν στον πυρήναόπου εκτελείται το 2οΣυνήθως τα queues αφήνουν τα δεδομένα να φύγουν απότην cache πριν χρησιμοποιηθούν (από ένα μέγεθος καιπάνω)Use wisely
Ίσως χρειάζεται rewrite με χρήση parallel_pipeline
concurrent_vector<T>Λειτουργεί όπως ένας πίνακας από T που μπορεί ναμεγαλώσει δυναμικά
Μέθοδος grow_by(size_type) προσθέτει στοιχεία στοτέλοςΜέθοδος gro_to_at_least(size_type) προσθέτειστοιχεία μέχρι το ζητούμενο μέγεθοςΜέθοδος size() επιστρέφει τον αριθμό στοιχείων πουπεριέχει το vectorΜέθοδος empty() επιστρέφει size() == 0
Τα στοιχεία δεν μετακινούνται ποτέ μέχρι να αφαιρεθούνΜπορεί να γίνεται πρόσβαση παράλληλα με growΗ μέθοδος clear() δεν είναι thread-safe για παράλληληεκτέλεση με resize
}else cout << ”One or both strings not seen before” << endl;
}
Αν η find επιστρέψει true, το αλφαριθμητικό υπάρχει στοhash tableΤο πεδίο second του pair που περιέχει ο accessor είναι οαριθμός σειράς του αλφαριθμητικούPratikakis (CSD) TBB CS342, 2021 41 /56
...
.
...........................
.
...
.
...
.
Scalable Memory Allocator
Η δυναμική διαχείριση μνήμης μπορεί να γίνει bottleneckΤα threads δεσμεύουν μνήμη στο heap με mutual exclusionΜόνο ένα malloc() ανά πάσα στιγμή
False sharing: Πάνω από ένα threads γράφουνδιαφορετικά σημεία του ίδιου cache line
Ping-pong του cache line στις διάφορες caches,καθυστερεί όλους
Η βιβλιοθήκη TBB περιέχει δύο allocatorsΠαρόμοια με τον STL std::allocatorscalable_allocator
Κλιμακώσιμο, αλλά χωρίς προστασία από false sharingΗ μνήμη δεσμεύεται για κάθε thread από διαφορετικόmemory pool
cache_aligned_allocatorΚλιμακωσιμότητα και προστασία από false sharing
Pratikakis (CSD) TBB CS342, 2021 42 /56
...
.
...........................
.
...
.
...
.
API - scalable allocator
#include <tbb/scalable_allocator.h>template<typename T> class scalable_allocator;
T∗ A::allocate( size_type n, void∗ hint=0 ) // Allocate space for n valuesvoid A::deallocate( T∗ p, size_t n ) // Deallocate n values from pvoid A::construct( T∗ p, const T& value )void A::destroy( T∗ p )
int ∗p;MyString str1 = ”qwertyuiopasdfghjkl”;MyString str2 = ”asdfghjklasdfghjkl”;p = tbb::scalable_allocator<int>().allocate(24);
Pratikakis (CSD) TBB CS342, 2021 44 /56
...
.
...........................
.
...
.
...
.
Flow Graph
Data Flow Graph patternΜερικές εφαρμογές εκφράζονται ως κόμβοι γράφου πουανταλλάζουν μηνύματαReactive προγραμματισμός: event → responseTask graph, πολύπλοκες σχέσεις μεταξύ tasksΕφαρμογές actor-based, κόμβοι actors που αντιδρούν στοπεριβάλλονκλπ
Pratikakis (CSD) TBB CS342, 2021 45 /56
...
.
...........................
.
...
.
...
.
Είδη κόμβων του Flow Graph
Functional: εκτελούν μια λειτουργίαsource_node: Εκτελεί μια συνάρτηση και παράγει τοαποτέλεσμαcontinue_node: Παράγει έξοδο όταν λάβει είσοδο απόόλες τις εισόδουςfunction_node: Εκτελεί μια συνάρτηση πάνω στην είσοδοκαι επιστρέφει το αποτέλεσμά της σε όλες τις εξόδουςmultifunction_node: Εκτελεί μια συνάρτηση πάνω στηνείσοδο και επιστρέφει πολλά αποτελέσματα σε πολλέςεξόδους
Buffering: λειτουργία αποθήκευσης δεδομένωνbuffer_node: Buffer μηνυμάτων, έξοδος σε έναν-έναν, όχιαπαραίτητα με τη σειρά (παράλληλα)queue_node: FIFO buffer, έξοδος με τη σειράpriority_queue_node: Queue με προτεραιότηταsequencer_node: Priority με βάση το sequence πουπαράγεται με custom συνάρτηση
Pratikakis (CSD) TBB CS342, 2021 46 /56
...
.
...........................
.
...
.
...
.
Είδη κόμβων του Flow Graph
Split/Join: συνδυασμός αποτελεσμάτων διαφορετικώνμονοπατιών στο γράφο, ή δημιουργία μονοπατιών
queuing_join: Είσοδος από FIFO queues, όταν όλες έχουνστοιχείο, έξοδος tuplereserving_join: Κράτηση των εισόδων και verificationόταν είναι όλες διαθέσιμες, αλλιώς releasetag_matching_join: Υπολογισμός tag/key με customhash() και επιλογή με βάση tagsplit_node: Είσοδος από tuple και έξοδος σε χωριστάκομμάτιαindexer_node: Αναμετάδοση κάθε εισόδου από κάθε θύραεισόδου σε όλες τις εξόδους ως union
Pratikakis (CSD) TBB CS342, 2021 47 /56
...
.
...........................
.
...
.
...
.
Είδη κόμβων του Flow Graph
Otherbroadcast_node: Αναμετάδοση κάθε εισόδου σε όλες τιςεξόδουςwrite_once_node: Buffer μεγέθους 1, δεν μπαίνει 2οπεριεχόμενο αν δεν αδειάσει το πρώτοoverwrite_node: Buffer μεγέθους 1 που κρατά μόνο τοτελευταίο pushlimiter_node: Όπως το broadcast_node αλλά επιλέγει Νμηνύματα, μετά από Ν μηνύματα σταματά να δέχεταιείσοδο
Μερικές φορές υπάρχει ανάγκη παράλληλα tasks να έχουνπρόσβαση σε shared data
Mutual exclusion, αποφυγή data racesΗ TBB παρέχει high-level abstraction για hardwaresynchronization
Atomic update μιας μόνο μεταβλητής
Pratikakis (CSD) TBB CS342, 2021 50 /56
...
.
...........................
.
...
.
...
.
Primitives Συγχρονισμού
Scoped locksLock range που εξαρτάται από το lifetime του αντικειμένου(scope)Έξοδος από το scope καλεί τον destructor, exception-safeΕλαχιστοποίηση του lock lifetime: λιγότερο congestionΠολλά πιθανά patterns
Spin-locks, queue-locksWriter, reader/writer locksScoped wrapper της native συνάρτησης
Pratikakis (CSD) TBB CS342, 2021 51 /56
...
.
...........................
.
...
.
...
.
Atomic<T>
atomic<T>Είτε τύπος δείκτη, είτε βασικός τύπος8, 16, 32, 64-bit integerstype-safe= x και x =: read/writex.fetch_and_store(y): αντικατάσταση τιμής,επιστροφή της προηγούμενηςx.fetch_and_add(y): αύξηση τιμής, επιστροφή τηςπροηγούμενηςx.compare_and_swap(y, p): conditional αντικατάστασημε νέα τιμή, επιστροφή της προηγούμενης
atomic<int> i;int z = i.fetch_and_add(2);
Pratikakis (CSD) TBB CS342, 2021 52 /56
...
.
...........................
.
...
.
...
.
TBB Mutex
Αντικείμενα C++ που βασίζονται σε scoped lockingΧρησιμοποιούνται για τη διαχείριση locks, παρέχουνmutual exclusion
M() // Construct unlocked mutex~M() // Destroy unlocked mutextypename M::scoped_lock // Corresponding scoped_lock typeM::scoped_lock () // Construct lock w/out acquiring a mutexM::scoped_lock (M&) // Construct lock and acquire lock on mutexM::~scoped_lock () // Release lock if acquiredM::scoped_lock::acquire (M&) // Acquire lock on mutexM::scoped_lock::release () // Release lock
Pratikakis (CSD) TBB CS342, 2021 53 /56
...
.
...........................
.
...
.
...
.
Είδη mutex
FairΤα threads εκτελούν το critical region με τη σειρά που τοφτάνουνUnfair mutexes επιτρέπουν λιγότερα context switchαφήνοντας threads που τρέχουν ήδη να εκτελέσουν πρώτατο region
ReentrantΤο thread που έχει κλειδώσει το mutex μπορεί να τοξανακλειδώσειΧρήσιμο σε αναδρομικό κώδικα
SpinBusy-wait χωρίς context switchΚαλύτερο για μικρή αναμονήΚατανάλωση cpu resourcesΧειρότερο σε congestion
Pratikakis (CSD) TBB CS342, 2021 54 /56
...
.
...........................
.
...
.
...
.
Είδη mutexspin_mutex
Non-reentrant, unfair, spinΠολύ γρήγορο για συγχρονισμό που χρειάζεται σπάνια,low-congestion, πολύ μικρά critical sections
queuing_mutexNon-reentrant, fair, spinΧρήσιμο όταν χρειάζεται κλιμακωσιμότητα και fairness
queuing_rw_mutexNon-reentrant, fair, spin
spin_rw_mutexNon-reentrant, unfair, spinΧρήση για το ReaderWriterMutex pattern
mutexWrapper για το OS syncΣτο linux είναι pthread_mutex