1 / 61

Threading Building Blocks

Threading Building Blocks. Σύνοψη. Γενικά για TBBs Tasks Parallel for Εσωτερική λειτουργία βιβλιοθήκης Task graphs . Σύνοψη. Γενικά για TBBs Tasks Parallel for Εσωτερική λειτουργία βιβλιοθήκης Task graphs . Τι είναι τα TBBs;.

morse
Download Presentation

Threading Building Blocks

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Threading Building Blocks

  2. Σύνοψη • Γενικά για TBBs • Tasks • Parallel for • Εσωτερική λειτουργίαβιβλιοθήκης • Task graphs

  3. Σύνοψη • Γενικά για TBBs • Tasks • Parallel for • Εσωτερική λειτουργίαβιβλιοθήκης • Task graphs

  4. Τι είναι τα TBBs; • C++ template library για αποδοτικό και εύκολο παράλληλο προγραμματισμό σε πλατφόρμες μοιραζόμενης μνήμης • Αναπτύσσεται από την Intel από το 2004 (open-source από το 2007) • Δεν είναι καινούρια γλώσσα ή επέκταση • Μεταφέρσιμη στους περισσότερους C++ compilers, λειτουργικά συστήματα και αρχιτεκτονικές

  5. Βασικά χαρακτηριστικά • Ο προγραμματιστής ορίζει tasks αντί για threads • επικεντρώνεται στην έκφρασητου παραλληλισμούστην εφαρμογή (σε υψηλότερο ή χαμηλότερο επίπεδο) • η βιβλιοθήκη είναι υπεύθυνη για την υλοποίησή του • διάσπαση συνολικής δουλειάς σε επιμέρους εργασίες • δρομολόγηση εργασιών στους επεξεργαστές • συγχρονισμός • ισοκατανομή φορτίου • διαχείριση πόρων συστήματος και εσωτερικών μηχανισμών

  6. Βασικά χαρακτηριστικά • Σχεδιασμένη για κλιμακωσιμότητα • η συνολική δουλειά σπάει σε πολλά μικρά κομμάτια (tasks), συνήθως πολύ περισσότερα από τον αριθμό των επεξεργαστών («parallel slack») • εξασφαλίζεται ότι θα υπάρχειπάντα διαθέσιμη δουλειά για κάθε επιπλέον επεξεργαστή που προστίθεται • ο μηχανισμός για load balancing εξασφαλίζει την κλιμακώσιμη απόδοση

  7. Βασικά χαρακτηριστικά • Εκμεταλλεύεται τη δύναμη και την ευελιξία του γενικευμένου προγραμματισμού (generic programming) • παρέχει ένα πλούσιο σύνολο από παραμετροποιήσιμα (templated), «ready to use»παράλληλα αλγοριθμικά μοτίβα και δομές • αντίστοιχα με την C++ STL για τα σειριακά προγράμματα • δεν απαιτεί ειδική υποστήριξη από μεταγλωττιστή

  8. TBB 4.0Components Generic Parallel Algorithms parallel_for parallel_reduce parallel_scan parallel_do pipeline, parallel_pipeline, parallel_sort parallel_invoke Concurrent containers concurrent_unordered_map, concurrent_unordered_set, concurrent_hash_map, concurrent_queue, concurrent_bounded_queue, concurrent_priority_queue concurrent_vector Synchronization primitives atomic mutex recursive_mutex spin_mutex, spin_rw_mutex queuing_mutex, queuing_rw_mutex Memory allocation tbb_allocator cache_aligned_allocator scalable_allocator Raw tasking task task_group task_list task_scheduler_observer Flow Graph graph function_node broadcast_node …

  9. TBB 4.0Components Generic Parallel Algorithms parallel_for parallel_reduce parallel_scan parallel_do pipeline, parallel_pipeline, parallel_sort parallel_invoke Concurrent containers concurrent_unordered_map, concurrent_unordered_set, concurrent_hash_map, concurrent_queue, concurrent_bounded_queue, concurrent_priority_queue concurrent_vector Synchronization primitives atomic mutex recursive_mutex spin_mutex, spin_rw_mutex queuing_mutex, queuing_rw_mutex Memory allocation tbb_allocator cache_aligned_allocator scalable_allocator Raw tasking task task_group task_list task_scheduler_observer Flow Graph graph function_node broadcast_node …

  10. Σύνοψη • Γενικά για TBBs • Tasks • Parallel for • Εσωτερική λειτουργίαβιβλιοθήκης • Task graphs

  11. Tasks • Εκφράζουν μια στοιχειώδη ανεξάρτητη εργασία στο πρόγραμμα του χρήστη • πολύ πιο lightweight από τα native threads του λειτουργικού • Δυνατότητα άμεσης χρήσης των tasks από τον προγραμματιστή • δημιουργία αυθαίρετα πολύπλοκων γράφων εργασιών parallel algorithms tasks scheduler

  12. Προγραμματιστικό μοντέλο • Όπως και στη Cilk, δύο βασικές λειτουργίες για την περιγραφή ενός task graph • spawn: δημιουργία εργασίας • wait: συγχρονισμός εργασιών

  13. Μέθοδος 1task groups + Lambdas long ParallelFib(long n) { if ( n < 16 ) return SerialFib(n); else { int x, y; tbb::task_group g; g.run([&]{ x = ParallelFib(n-1); }); g.run([&]{ y = ParallelFib(n-2); }); g.wait(); return x+y; } }

  14. class FibTask: public task { 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(a); wait_for_all(); *sum = x+y; } return NULL; } }; Μέθοδος 2task objects long SerialFib(long n) { if (n < 2) return n; else return SerialFib(n-1) + SerialFib(n-2); } long n, sum; FibTask& r = *new ( allocate_root())FibTask(n,&sum); spawn_root_and_wait(r); cout << sum;

  15. class FibTask: public task { 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(a); wait_for_all(); *sum = x+y; } return NULL; } }; Μέθοδος 2task objects each user-defined task must extendtbb::task and implementexecute() long SerialFib(long n) { if (n < 2) return n; else return SerialFib(n-1) + SerialFib(n-2); } long n, sum; FibTask& r = *new ( allocate_root())FibTask(n,&sum); spawn_root_and_wait(r); cout << sum;

  16. class FibTask: public task { 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(a); wait_for_all(); *sum = x+y; } return NULL; } }; Μέθοδος 2task objects long SerialFib(long n) { if (n < 2) return n; else return SerialFib(n-1) + SerialFib(n-2); } allocate root task (has no parent) long n, sum; FibTask& r = *new ( allocate_root())FibTask(n,&sum); spawn_root_and_wait(r); cout << sum; spawn it, and wait here

  17. class FibTask: public task { 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(a); wait_for_all(); *sum = x+y; } return NULL; } }; Μέθοδος 2task objects long SerialFib(long n) { if (n < 2) return n; else return SerialFib(n-1) + SerialFib(n-2); } if n small enough, execute task serially otherwise create and run two tasks long n, sum; FibTask& r = *new ( allocate_root())FibTask(n,&sum); spawn_root_and_wait(r); cout << sum;

  18. class FibTask: public task { 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(a); wait_for_all(); *sum = x+y; } return NULL; } }; Μέθοδος 2task objects long SerialFib(long n) { if (n < 2) return n; else return SerialFib(n-1) + SerialFib(n-2); } allocate child tasks long n, sum; FibTask& r = *new ( allocate_root())FibTask(n,&sum); spawn_root_and_wait(r); cout << sum; spawn tasks (indicate them as “ready to execute”) how many children should I wait for? 2 (+1 implicit...) ok, now really wait for children to complete merge their results and store into *sum

  19. Σύνοψη • Γενικά για TBBs • Tasks • Parallel for • Εσωτερική λειτουργίαβιβλιοθήκης • Task graphs

  20. TBB: Αρχικοποίηση • Για την χρήση οποιουδήποτε παράλληλου αλγόριθμου της βιβλιοθήκης, απαιτείται η δημιουργία ενός αντικειμένου task_scheduler_init #include <tbb/task_scheduler_init.h> #define NPROCS 4 int main() { tbb::task_scheduler_init init(NPROCS); … }

  21. Παραλληλοποίηση for-loop • Υπόθεση: εφαρμογή συνάρτησης Foo() σε κάθε στοιχείο ενός πίνακα • Σειριακός κώδικας • Παράλληλος κώδικας float a[100]; for ( int i=0; i!=100; ++i ) Foo(a[i]); δημιουργία (ανώνυμου) αντικειμένου για την περιγραφή του αρχικού χώρου επαναλήψεων tbb::parallel_for( tbb::blocked_range<size_t>(0,100), [=](const tbb::blocked_range<size_t>& r) { for ( size_t i = r.begin(); i != r.end(); ++i ) Foo(a[i]); } ); ανώνυμη συνάρτηση (Lambda expression) που περιγράφει τι δουλειά θα γίνεται σε έναν οποιονδήποτε υποχώροεπαναλήψεων του loop

  22. Παραλληλοποίηση for-loop • δήλωση: • η parallel_for αναλαμβάνει: • να διασπάσει το αρχικό range σε πολλά μικρότερα • να εφαρμόσει παράλληλα την ανώνυμη συνάρτηση σε καθένα από αυτά template <typename Range, typename Body> void parallel_for(const Range& R, const Body& B ); η βιβλιοθήκη παρέχει τις κλάσεις blocked_range, blocked_range2d, blocked_range3d

  23. Chunking και loop partitioners parallel_for(blocked_range<size_t>(0,n,G), [](){…}, ,some_partitioner()) • Chunking:το μέγεθος των ranges στο οποίο σταματά η αναδρομική διάσπαση • optional argument στον constructor του blocked_range • Partitioners • optional argument στην parallel_for • simple_partitioner • recursive binary splitting, εγγυάται ότι ⎡G/2⎤ ≤ chunksize ≤ G • affinity_partitioner • αναθέτει τα ranges με τρόπο ώστε να μεγιστοποιείται το cache locality • auto_partitioner (default) • επιλέγει αυτόματα το grainsize με βάση ευριστική μέθοδο • προσπαθεί να ελαχιστοποιήσει το range splitting σε σημείο που να εξασφαλίζεται καλό load balancing

  24. Σύνοψη • Γενικά για TBBs • Tasks • Parallel for • Εσωτερική λειτουργίαβιβλιοθήκης • Task graphs

  25. Λειτουργία parallel_for top (oldest task) • σε κάθε εκτέλεση της αναδρομής ένα range διασπάται σε 2 subranges • δημιουργούνται 2 νέα tasks που τοποθετούνται στη βάση της ουράς • κάθε worker παίρνει το task από τη βάση της τοπικής του ουράς και το εκτελεί • αν δεν βρει, τότε κλέβει κάποιο από την κορυφή της ουράς ενός τυχαίου worker 0 N A αναδρομική διάσπαση του range, μέχρι να γίνει ≤ GrainSize A bottom (youngest task) P0 P1 P2 P3 worker threads με double-ended queues

  26. Λειτουργία parallel_for 0 N A A P0 P1 P2 P3

  27. Λειτουργία parallel_for 0 N A P0 P1 P2 P3 A

  28. Λειτουργία parallel_for 0 N A N/2 N B B P0 P1 P2 P3 A

  29. Λειτουργία parallel_for 0 N A 0 N/2 C N/2 N B B C P0 P1 P2 P3 A

  30. Λειτουργία parallel_for 0 N A 0 N/2 C N/2 N B B P0 P1 P2 P3 C

  31. Λειτουργία parallel_for 0 N A 0 N/2 C B B D D P0 P1 P2 P3 C

  32. Λειτουργία parallel_for 0 N A 0 N/2 C B B D E 0 N/4 E D P0 P1 P2 P3 C

  33. Λειτουργία parallel_for 0 N A 0 N/2 C B B D E 0 N/4 E D P0 P1 P2 P3 C

  34. Λειτουργία parallel_for 0 N A 0 N/2 C B D E B 0 N/4 E D P0 P1 P2 P3 C

  35. Λειτουργία parallel_for 0 N A 0 N/2 C B D B 0 N/4 E D P0 P1 P2 P3 E

  36. Λειτουργία parallel_for 0 - N A 0 - N/2 C B D F B 0 - N/4 E D P0 P1 P2 P3 E F

  37. Λειτουργία parallel_for 0 - N A 0 - N/2 C B D F G B 0 - N/4 E D P0 P1 P2 P3 E 0 - N/8 G F

  38. Λειτουργία parallel_for 0 - N A 0 - N/2 C B D F G B 0 - N/4 E D P0 P1 P2 P3 E 0 - N/8 G F

  39. Λειτουργία parallel_for 0 - N A 0 - N/2 C B F G B D 0 - N/4 E D P0 P1 P2 P3 E 0 - N/8 G F

  40. Βασικοί μηχανισμοί • Work stealing • εξασφαλίζει ισοκατανομή φορτίου • Recursive splitting • επιτρέπει την επεξεργασία πάνω σε αυθαίρετα μικρά κομμάτια και τη βέλτιστη εκμετάλλευση της cache

  41. Σύνοψη • Γενικά για TBBs • Tasks • Parallel for • Εσωτερική λειτουργίαβιβλιοθήκης • Task graphs

  42. Generic task graphtask groups S(); task_group g; g.run( [&]{ C(); E(); } ); g.run( [&]{ task_group g1; g1.run( [&]{A();} ); g1.run( [&]{B();} ); g1.wait(); D(); }); g.wait(); F();

  43. Generic task graphflow graph graph g; broadcast_node <continue_msg > s; continue_node <continue_msg > a(g,A()); continue_node <continue_msg > b(g,B()); continue_node <continue_msg > c(g,C()); continue_node <continue_msg > d(g,D()); continue_node <continue_msg > e(g,E()); continue_node <continue_msg > f(g,F()); make_edge(s,a); make_edge(s,b); make_edge(s,c); make_edge(a,d); make_edge(b,d); make_edge(c,e); make_edge(d,f); make_edge(e,f); S(); s.try_put(continue_msg()); //fire! g.wait_for_all();

  44. Generic task graphtask objects + reference counts 0 1 1 1 1 class MeshTask: public task { public: const int i,j; //coordinates MeshTask *south,*east; task* execute() { double north = (i==0) ? 0 : A[i-1][j]; double west = (j==0) ? 0 : A[i][j-1]; A[i][j] = do_work(north, west); //if there is south neighbor if(south!=NULL) if (!south->decrement_ref_count()) spawn(*south); //if there is east neighbor if(east!=NULL) if (!east->decrement_ref_count()) spawn(*south); return NULL; } } 1 2 2 2 2 1 2 2 2 2 1 2 2 2 2 MeshTask* Mesh[4][5]; //for all tasks in Mesh: // allocate // initialize south,east pointers // set reference counters //wait for all but last task to complete Mesh[3][4]->spawn_and_wait_for_all(*Mesh[0][0]); //execute last task Mesh[3][4]->execute();

  45. Resources • Home • http://threadingbuildingblocks.org/ • Latest stable release (4.0): • https://threadingbuildingblocks.org/file.php?fid=77 • use sources • Documentation: • https://threadingbuildingblocks.org/documentation.php • Getting Started • Tutorial • Reference • Intel Software Network blogs: • http://software.intel.com/en-us/blogs/tag/tbb/ • Forum: • http://software.intel.com/en-us/forums/intel-threading-building-blocks/

  46. ΤΕΛΟΣ

  47. Extra slides

  48. Lambda Expressions • “C++11 feels like a new language”[B. Stroustrup] • Δυνατότητα “in-place” ορισμού συναρτήσεων στο σημείο που χρησιμοποιούνται • αντί των function objects • o compiler δημιουργεί μοναδικό, ανώνυμο function object για κάθε lambda expression • gcc 4.5 or newer char s[]="Hello World!"; int nup = 0; //modified by the lambda for_each( s, s+sizeof(s), [&nup] (char c) { if (isupper(c)) nup++; } ); cout << nup << " uppercase letters in: "<< s << endl;

  49. [&]  by-reference [=] by-value [] no capture Can omit if there are no parameters and return type is implicit. Can omit if return type is void or code is “return expr;” Lambda Syntax • [capture_mode] (formal_parameters) -> return_type {body} Examples []{return rand();} [&](float x) {sum+=x;} [](float x, float y)->float { if(x<y) return x; else return y; } [&]{return *p++;} [=](float x) {return a*x+b;}

  50. Γενικευμένος Προγραμματισμός • «... deals with finding abstract representations of efficient algorithms, data structures, and other software concepts, and with their systematic organization»[Czarnecki, Eisenecker – Generative Programming] • «... a style of computer programming in which algorithms are written in terms of to-be-specified-later types that are then instantiated when needed for specific types provided as parameters»[wikipedia] • Σκοπός η ανάπυξη λογισμικού ώστε να είναι επαναχρησιμοποιήσιμο με απλό και αποδοτικό τρόπο

More Related