Η παρουσίαση φορτώνεται. Παρακαλείστε να περιμένετε

Η παρουσίαση φορτώνεται. Παρακαλείστε να περιμένετε

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

Παρόμοιες παρουσιάσεις


Παρουσίαση με θέμα: "Threading Building Blocks. Σύνοψη  Γενικά για TBBs  Tasks  Parallel for  Εσωτερική λειτουργία βιβλιοθήκης  Task graphs."— Μεταγράφημα παρουσίασης:

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 Βασικά χαρακτηριστικά 1. Ο προγραμματιστής ορίζει tasks αντί για threads – επικεντρώνεται στην έκφραση του παραλληλισμού στην εφαρμογή (σε υψηλότερο ή χαμηλότερο επίπεδο) – η βιβλιοθήκη είναι υπεύθυνη για την υλοποίησή του •διάσπαση συνολικής δουλειάς σε επιμέρους εργασίες •δρομολόγηση εργασιών στους επεξεργαστές •συγχρονισμός •ισοκατανομή φορτίου •διαχείριση πόρων συστήματος και εσωτερικών μηχανισμών

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

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

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

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

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

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

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

13 Μέθοδος 1 task 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 Μέθοδος 2 task objects long SerialFib(long n) { if (n < 2) return n; else return SerialFib(n-1) + SerialFib(n-2); } 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; long n, sum; FibTask& r = *new ( allocate_root())FibTask(n,&sum); spawn_root_and_wait(r); cout << sum; 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; } }; 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; } };

15 Μέθοδος 2 task objects long SerialFib(long n) { if (n < 2) return n; else return SerialFib(n-1) + SerialFib(n-2); } long SerialFib(long n) { if (n < 2) return n; else return SerialFib(n-1) + SerialFib(n-2); } 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; } }; 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; } }; each user-defined task must extend tbb::task and implement execute() long n, sum; FibTask& r = *new ( allocate_root())FibTask(n,&sum); spawn_root_and_wait(r); cout << sum; long n, sum; FibTask& r = *new ( allocate_root())FibTask(n,&sum); spawn_root_and_wait(r); cout << sum;

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

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

18 long n, sum; FibTask& r = *new ( allocate_root())FibTask(n,&sum); spawn_root_and_wait(r); cout << sum; long n, sum; FibTask& r = *new ( allocate_root())FibTask(n,&sum); spawn_root_and_wait(r); cout << sum; Μέθοδος 2 task objects long SerialFib(long n) { if (n < 2) return n; else return SerialFib(n-1) + SerialFib(n-2); } long SerialFib(long n) { if (n < 2) return n; else return SerialFib(n-1) + SerialFib(n-2); } 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; } }; 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; } }; allocate child tasks how many children should I wait for? 2 (+1 implicit...) spawn tasks (indicate them as “ready to execute”) 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 #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 (0,100), [=](const tbb::blocked_range & r) { for ( size_t i = r.begin(); i != r.end(); ++i ) Foo(a[i]); } ); δημιουργία (ανώνυμου) αντικειμένου για την περιγραφή του αρχικού χώρου επαναλήψεων ανώνυμη συνάρτηση (Lambda expression) που περιγράφει τι δουλειά θα γίνεται σε έναν οποιονδήποτε υποχώρο επαναλήψεων του loop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

42 Generic task graph task 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 graph flow graph graph g; broadcast_node s; continue_node a(g,A()); continue_node b(g,B()); continue_node c(g,C()); continue_node d(g,D()); continue_node e(g,E()); continue_node 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 graph task objects + reference counts 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; } 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; } 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(); 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 –  Latest stable release (4.0): – https://threadingbuildingblocks.org/file.php?fid=77 https://threadingbuildingblocks.org/file.php?fid=77 – use sources  Documentation: – https://threadingbuildingblocks.org/documentation.php https://threadingbuildingblocks.org/documentation.php – Getting Started – Tutorial – Reference  Intel Software Network blogs: –  Forum: – blocks/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; 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 Lambda Syntax 49  [capture_mode] (formal_parameters) -> return_type {body} Can omit if there are no parameters and return type is implicit. Can omit if return type is void or code is “return expr;” [&]  by-reference [=]  by-value []  no capture [&](float x) {sum+=x;} [&]{return *p++;} [=](float x) {return a*x+b;} []{return rand();} [](float x, float y)->float { if(x

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]  Σκοπός η ανάπυξη λογισμικού ώστε να είναι επαναχρησιμοποιήσιμο με απλό και αποδοτικό τρόπο

51 Templates  Επιτρέπουν την παραμετροποίηση τύπων σε συναρτήσεις και κλάσεις  Παράδειγμα templated συνάρτησης template void swap(T & x, T & y) { T tmp = x; x = y; y = tmp; } … float f1,f2; String s1,s2; swap(f1,f2); //template instantiation: swap floats swap(s1,s2); //template instantiation: swap strings Ελάχιστες απαιτήσεις για τον Τ 1. copy constructor T(const T&) 2. assignment operator void T::operator=(const T&); 3. destructor ~T() Ελάχιστες απαιτήσεις για τον Τ 1. copy constructor T(const T&) 2. assignment operator void T::operator=(const T&); 3. destructor ~T()

52 Templates  Παράδειγμα templated κλάσης template class pair { public: T first; U second; pair( const T & x, const U & y ) : first(x), second(y) {} }; … //compiler instantiates pair with T=string and U=int pair x; x.first = “abc”; x.second = 42;

53 C++ Standard Template Library (STL)  Ένα από τα πλέον επιτυχημένα παραδείγματα γενικευμένου προγραμματισμού  Παρέχει ένα σύνολο από:  οι καλύτεροι δυνατοί αλγόριθμοι και δομές γραμμένοι με τον πιο γενικό τρόπο  instantiation ανάλογα με την περίπτωση κατά τη μεταγλώττιση Δομές δεδομένων vector, list, deque, set, map, stack, … Δομές δεδομένων vector, list, deque, set, map, stack, … Αλγόριθμοι for_each, sort, binary_search, find, count, … Αλγόριθμοι for_each, sort, binary_search, find, count, … Iterators input, output, random access, bidirectional, … Iterators input, output, random access, bidirectional, …

54 Παράδειγμα: πλήθος εμφανίσεων μιας τιμής σε ένα vector vector v; vector ::iterator b = v.begin(), e = v.end(); long c = count( 4); template long count(Iter first, Iter last, const T& value) { long ret=0; while ( first != last ) if (*first++ == value) ++ret; return ret; } b, e,  γενικευμένη υλοποίηση χωρίς να υστερεί σε απόδοση

55 Function objects (“Functors”)  Κλάσεις στις οποίες έχει υπερφορτωθεί ο τελεστής ()  Έχουν την λειτουργικότητα συναρτήσεων – στην ουσία είναι σαν δείκτες σε συναρτήσεις – επιπλέον πλεονεκτήματα (π.χ. αποθήκευση state)  Σε αυτά στηρίζεται η λειτουργία των TBBs – παράλληλα αλγοριθμικά μοτίβα υλοποιημένα σαν template functions – κώδικας χρήστη υλοποιημένος σαν function object

56 Παράδειγμα functor 56 template void ForEach( I lower, I upper, const Functor& f ) { for ( I i=lower; i

57 Παραλληλοποίηση for-loop  tbb::parallel_for – χωρίζει τον αρχικό χώρο επαναλήψεων σε μικρότερα κομμάτια και τα εκτελεί παράλληλα – template function  1 ο βήμα: χρειάζεται να δώσουμε μια περιγραφή για το τι δουλειά θα γίνεται σε έναν οποιονδήποτε υποχώρο επαναλήψεων του loop – γράφουμε το σώμα του loop στον operator() ενός function object – ο operator() είναι παραμετροποιημένος με βάση έναν υποχώρο class ApplyFoo { float *const my_a; public: ApplyFoo( float *a ) : my_a(a) {} void operator()( const blocked_range & r ) const { float *a = my_a; for ( int i=r.begin(); i!=r.end(); ++i ) Foo(a[i]); } blocked_range : κλάση που εκφράζει 1D γραμμικό range πάνω στον τύπο Τ

58 Παραλληλοποίηση for-loop  2 ο βήμα: κλήση parallel_for  η parallel_for αναλαμβάνει: – να διασπάσει το αρχικό range σε πολλά μικρότερα – να εφαρμόσει παράλληλα το function object σε καθένα από αυτά float a[100]; parallel_for( blocked_range (0,100), ApplyFoo(a) ); δημιουργία (ανώνυμου) αντικειμένου για την περιγραφή του αρχικού χώρου επαναλήψεων δημιουργία function object

59 Δήλωση parallel_for  Απαιτήσεις για το Body B  Απαιτήσεις για το Range R template void parallel_for(const Range& R, const Body& B ); R(const R&)Copy a range R::~R()Destroy a range bool R::empty() constIs range empty? bool R::is_divisible() constCan range be split? R::R (R& r, split)Split r into two subranges B::B( const F& )Copy constructor B::~B()Destructor void B::operator() (Range& subrange) const Apply B to subrange η βιβλιοθήκη παρέχει τις κλάσεις blocked_range, blocked_range2d, blocked_range3d

60 Πιθανές σειρές εκτέλεσης των tasks Depth-first - Απαιτεί λίγο χώρο - Καλή τοπικότητα αναφορών - Μηδενικός παραλληλισμός Breadth-first - Απαιτεί πολύ χώρο - Κακή τοπικότητα αναφορών - Μέγιστος παραλληλισμός

61 Work depth-first, steal breadth-first  Εξισορροπεί καλύτερα τις προηγούμενες απαιτήσεις L1 L2 victim thread Δεύτερη καλύτερη επιλογή Το κλέψιμο από την κορυφή εξασφαλίζει: - μεγάλο κομμάτι δουλειάς → καλή περαιτέρω κατανομή φορτίου - LRU δεδομένα του θύματος στον «κλέφτη», τα MRU παραμένουν στο «θύμα»


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

Παρόμοιες παρουσιάσεις


Διαφημίσεις Google