BRE12
parallel_do.h
1 /*
2  Copyright 2005-2016 Intel Corporation. All Rights Reserved.
3 
4  This file is part of Threading Building Blocks. Threading Building Blocks is free software;
5  you can redistribute it and/or modify it under the terms of the GNU General Public License
6  version 2 as published by the Free Software Foundation. Threading Building Blocks is
7  distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
8  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9  See the GNU General Public License for more details. You should have received a copy of
10  the GNU General Public License along with Threading Building Blocks; if not, write to the
11  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
12 
13  As a special exception, you may use this file as part of a free software library without
14  restriction. Specifically, if other files instantiate templates or use macros or inline
15  functions from this file, or you compile this file and link it with other files to produce
16  an executable, this file does not by itself cause the resulting executable to be covered
17  by the GNU General Public License. This exception does not however invalidate any other
18  reasons why the executable file might be covered by the GNU General Public License.
19 */
20 
21 #ifndef __TBB_parallel_do_H
22 #define __TBB_parallel_do_H
23 
24 #include "internal/_range_iterator.h"
25 #include "internal/_template_helpers.h"
26 #include "task.h"
27 #include "aligned_space.h"
28 #include <iterator>
29 
30 namespace tbb {
31 
33 namespace internal {
34  template<typename Body, typename Item> class parallel_do_feeder_impl;
35  template<typename Body> class do_group_task;
36 } // namespace internal
38 
40 
41 template<typename Item>
42 class parallel_do_feeder: internal::no_copy
43 {
45  virtual ~parallel_do_feeder () {}
46  virtual void internal_add( const Item& item ) = 0;
47  template<typename Body_, typename Item_> friend class internal::parallel_do_feeder_impl;
48 public:
50  // TODO: add an overload for r-value reference
51  void add( const Item& item ) {internal_add(item);}
52 };
53 
55 namespace internal {
57 
59  template<class Body, typename Item>
60  class parallel_do_operator_selector
61  {
62  typedef parallel_do_feeder<Item> Feeder;
63  template<typename A1, typename A2, typename CvItem >
64  static void internal_call( const Body& obj, A1& arg1, A2&, void (Body::*)(CvItem) const ) {
65  obj(arg1);
66  }
67  template<typename A1, typename A2, typename CvItem >
68  static void internal_call( const Body& obj, A1& arg1, A2& arg2, void (Body::*)(CvItem, parallel_do_feeder<Item>&) const ) {
69  obj(arg1, arg2);
70  }
71 
72  public:
73  template<typename A1, typename A2 >
74  static void call( const Body& obj, A1& arg1, A2& arg2 )
75  {
76  internal_call( obj, arg1, arg2, &Body::operator() );
77  }
78  };
79 
81 
83  template<typename Body, typename Item>
84  class do_iteration_task: public task
85  {
86  typedef parallel_do_feeder_impl<Body, Item> feeder_type;
87 
88  Item my_value;
89  feeder_type& my_feeder;
90 
91  do_iteration_task( const Item& value, feeder_type& feeder ) :
92  my_value(value), my_feeder(feeder)
93  {}
94 
95  /*override*/
96  task* execute()
97  {
98  // TODO: use move semantics for my_value
99  parallel_do_operator_selector<Body, Item>::call(*my_feeder.my_body, my_value, my_feeder);
100  return NULL;
101  }
102 
103  template<typename Body_, typename Item_> friend class parallel_do_feeder_impl;
104  }; // class do_iteration_task
105 
106  template<typename Iterator, typename Body, typename Item>
107  class do_iteration_task_iter: public task
108  {
109  typedef parallel_do_feeder_impl<Body, Item> feeder_type;
110 
111  Iterator my_iter;
112  feeder_type& my_feeder;
113 
114  do_iteration_task_iter( const Iterator& iter, feeder_type& feeder ) :
115  my_iter(iter), my_feeder(feeder)
116  {}
117 
118  /*override*/
119  task* execute()
120  {
121  parallel_do_operator_selector<Body, Item>::call(*my_feeder.my_body, *my_iter, my_feeder);
122  return NULL;
123  }
124 
125  template<typename Iterator_, typename Body_, typename Item_> friend class do_group_task_forward;
126  template<typename Body_, typename Item_> friend class do_group_task_input;
127  template<typename Iterator_, typename Body_, typename Item_> friend class do_task_iter;
128  }; // class do_iteration_task_iter
129 
131 
133  template<class Body, typename Item>
134  class parallel_do_feeder_impl : public parallel_do_feeder<Item>
135  {
136  /*override*/
137  void internal_add( const Item& item )
138  {
139  typedef do_iteration_task<Body, Item> iteration_type;
140 
141  iteration_type& t = *new (task::allocate_additional_child_of(*my_barrier)) iteration_type(item, *this);
142 
143  t.spawn( t );
144  }
145  public:
146  const Body* my_body;
147  empty_task* my_barrier;
148 
149  parallel_do_feeder_impl()
150  {
151  my_barrier = new( task::allocate_root() ) empty_task();
152  __TBB_ASSERT(my_barrier, "root task allocation failed");
153  }
154 
155 #if __TBB_TASK_GROUP_CONTEXT
156  parallel_do_feeder_impl(tbb::task_group_context &context)
157  {
158  my_barrier = new( task::allocate_root(context) ) empty_task();
159  __TBB_ASSERT(my_barrier, "root task allocation failed");
160  }
161 #endif
162 
163  ~parallel_do_feeder_impl()
164  {
165  my_barrier->destroy(*my_barrier);
166  }
167  }; // class parallel_do_feeder_impl
168 
169 
171 
174  template<typename Iterator, typename Body, typename Item>
175  class do_group_task_forward: public task
176  {
177  static const size_t max_arg_size = 4;
178 
179  typedef parallel_do_feeder_impl<Body, Item> feeder_type;
180 
181  feeder_type& my_feeder;
182  Iterator my_first;
183  size_t my_size;
184 
185  do_group_task_forward( Iterator first, size_t size, feeder_type& feeder )
186  : my_feeder(feeder), my_first(first), my_size(size)
187  {}
188 
189  /*override*/ task* execute()
190  {
191  typedef do_iteration_task_iter<Iterator, Body, Item> iteration_type;
192  __TBB_ASSERT( my_size>0, NULL );
193  task_list list;
194  task* t;
195  size_t k=0;
196  for(;;) {
197  t = new( allocate_child() ) iteration_type( my_first, my_feeder );
198  ++my_first;
199  if( ++k==my_size ) break;
200  list.push_back(*t);
201  }
202  set_ref_count(int(k+1));
203  spawn(list);
204  spawn_and_wait_for_all(*t);
205  return NULL;
206  }
207 
208  template<typename Iterator_, typename Body_, typename _Item> friend class do_task_iter;
209  }; // class do_group_task_forward
210 
211  template<typename Body, typename Item>
212  class do_group_task_input: public task
213  {
214  static const size_t max_arg_size = 4;
215 
216  typedef parallel_do_feeder_impl<Body, Item> feeder_type;
217 
218  feeder_type& my_feeder;
219  size_t my_size;
221 
222  do_group_task_input( feeder_type& feeder )
223  : my_feeder(feeder), my_size(0)
224  {}
225 
226  /*override*/ task* execute()
227  {
228  typedef do_iteration_task_iter<Item*, Body, Item> iteration_type;
229  __TBB_ASSERT( my_size>0, NULL );
230  task_list list;
231  task* t;
232  size_t k=0;
233  for(;;) {
234  t = new( allocate_child() ) iteration_type( my_arg.begin() + k, my_feeder );
235  if( ++k==my_size ) break;
236  list.push_back(*t);
237  }
238  set_ref_count(int(k+1));
239  spawn(list);
240  spawn_and_wait_for_all(*t);
241  return NULL;
242  }
243 
244  ~do_group_task_input(){
245  for( size_t k=0; k<my_size; ++k)
246  (my_arg.begin() + k)->~Item();
247  }
248 
249  template<typename Iterator_, typename Body_, typename Item_> friend class do_task_iter;
250  }; // class do_group_task_input
251 
253 
255  template<typename Iterator, typename Body, typename Item>
256  class do_task_iter: public task
257  {
258  typedef parallel_do_feeder_impl<Body, Item> feeder_type;
259 
260  public:
261  do_task_iter( Iterator first, Iterator last , feeder_type& feeder ) :
262  my_first(first), my_last(last), my_feeder(feeder)
263  {}
264 
265  private:
266  Iterator my_first;
267  Iterator my_last;
268  feeder_type& my_feeder;
269 
270  /* Do not merge run(xxx) and run_xxx() methods. They are separated in order
271  to make sure that compilers will eliminate unused argument of type xxx
272  (that is will not put it on stack). The sole purpose of this argument
273  is overload resolution.
274 
275  An alternative could be using template functions, but explicit specialization
276  of member function templates is not supported for non specialized class
277  templates. Besides template functions would always fall back to the least
278  efficient variant (the one for input iterators) in case of iterators having
279  custom tags derived from basic ones. */
280  /*override*/ task* execute()
281  {
282  typedef typename std::iterator_traits<Iterator>::iterator_category iterator_tag;
283  return run( (iterator_tag*)NULL );
284  }
285 
288  inline task* run( void* ) { return run_for_input_iterator(); }
289 
290  task* run_for_input_iterator() {
291  typedef do_group_task_input<Body, Item> block_type;
292 
293  block_type& t = *new( allocate_additional_child_of(*my_feeder.my_barrier) ) block_type(my_feeder);
294  size_t k=0;
295  while( !(my_first == my_last) ) {
296  // TODO: move *my_first
297  new (t.my_arg.begin() + k) Item(*my_first);
298  ++my_first;
299  if( ++k==block_type::max_arg_size ) {
300  if ( !(my_first == my_last) )
301  recycle_to_reexecute();
302  break;
303  }
304  }
305  if( k==0 ) {
306  destroy(t);
307  return NULL;
308  } else {
309  t.my_size = k;
310  return &t;
311  }
312  }
313 
314  inline task* run( std::forward_iterator_tag* ) { return run_for_forward_iterator(); }
315 
316  task* run_for_forward_iterator() {
317  typedef do_group_task_forward<Iterator, Body, Item> block_type;
318 
319  Iterator first = my_first;
320  size_t k=0;
321  while( !(my_first==my_last) ) {
322  ++my_first;
323  if( ++k==block_type::max_arg_size ) {
324  if ( !(my_first==my_last) )
325  recycle_to_reexecute();
326  break;
327  }
328  }
329  return k==0 ? NULL : new( allocate_additional_child_of(*my_feeder.my_barrier) ) block_type(first, k, my_feeder);
330  }
331 
332  inline task* run( std::random_access_iterator_tag* ) { return run_for_random_access_iterator(); }
333 
334  task* run_for_random_access_iterator() {
335  typedef do_group_task_forward<Iterator, Body, Item> block_type;
336  typedef do_iteration_task_iter<Iterator, Body, Item> iteration_type;
337 
338  size_t k = static_cast<size_t>(my_last-my_first);
339  if( k > block_type::max_arg_size ) {
340  Iterator middle = my_first + k/2;
341 
342  empty_task& c = *new( allocate_continuation() ) empty_task;
343  do_task_iter& b = *new( c.allocate_child() ) do_task_iter(middle, my_last, my_feeder);
344  recycle_as_child_of(c);
345 
346  my_last = middle;
347  c.set_ref_count(2);
348  c.spawn(b);
349  return this;
350  }else if( k != 0 ) {
351  task_list list;
352  task* t;
353  size_t k1=0;
354  for(;;) {
355  t = new( allocate_child() ) iteration_type(my_first, my_feeder);
356  ++my_first;
357  if( ++k1==k ) break;
358  list.push_back(*t);
359  }
360  set_ref_count(int(k+1));
361  spawn(list);
362  spawn_and_wait_for_all(*t);
363  }
364  return NULL;
365  }
366  }; // class do_task_iter
367 
369 
371  template<typename Iterator, typename Body, typename Item>
372  void run_parallel_do( Iterator first, Iterator last, const Body& body
373 #if __TBB_TASK_GROUP_CONTEXT
374  , task_group_context& context
375 #endif
376  )
377  {
378  typedef do_task_iter<Iterator, Body, Item> root_iteration_task;
379 #if __TBB_TASK_GROUP_CONTEXT
380  parallel_do_feeder_impl<Body, Item> feeder(context);
381 #else
382  parallel_do_feeder_impl<Body, Item> feeder;
383 #endif
384  feeder.my_body = &body;
385 
386  root_iteration_task &t = *new( feeder.my_barrier->allocate_child() ) root_iteration_task(first, last, feeder);
387 
388  feeder.my_barrier->set_ref_count(2);
389  feeder.my_barrier->spawn_and_wait_for_all(t);
390  }
391 
393 
395  template<typename Iterator, typename Body, typename Item>
396  void select_parallel_do( Iterator first, Iterator last, const Body& body, void (Body::*)(Item) const
397 #if __TBB_TASK_GROUP_CONTEXT
398  , task_group_context& context
399 #endif // __TBB_TASK_GROUP_CONTEXT
400  )
401  {
402  run_parallel_do<Iterator, Body, typename strip<Item>::type>( first, last, body
403 #if __TBB_TASK_GROUP_CONTEXT
404  , context
405 #endif // __TBB_TASK_GROUP_CONTEXT
406  );
407  }
408 
410 
412  template<typename Iterator, typename Body, typename Item, typename _Item>
413  void select_parallel_do( Iterator first, Iterator last, const Body& body, void (Body::*)(Item, parallel_do_feeder<_Item>&) const
414 #if __TBB_TASK_GROUP_CONTEXT
415  , task_group_context& context
416 #endif // __TBB_TASK_GROUP_CONTEXT
417  )
418  {
419  run_parallel_do<Iterator, Body, typename strip<Item>::type>( first, last, body
420 #if __TBB_TASK_GROUP_CONTEXT
421  , context
422 #endif // __TBB_TASK_GROUP_CONTEXT
423  );
424  }
425 
426 } // namespace internal
428 
429 
452 
454 template<typename Iterator, typename Body>
455 void parallel_do( Iterator first, Iterator last, const Body& body )
456 {
457  if ( first == last )
458  return;
459 #if __TBB_TASK_GROUP_CONTEXT
460  task_group_context context;
461 #endif // __TBB_TASK_GROUP_CONTEXT
462  internal::select_parallel_do( first, last, body, &Body::operator()
463 #if __TBB_TASK_GROUP_CONTEXT
464  , context
465 #endif // __TBB_TASK_GROUP_CONTEXT
466  );
467 }
468 
469 template<typename Range, typename Body>
470 void parallel_do(Range& rng, const Body& body) {
471  parallel_do(tbb::internal::first(rng), tbb::internal::last(rng), body);
472 }
473 
474 template<typename Range, typename Body>
475 void parallel_do(const Range& rng, const Body& body) {
476  parallel_do(tbb::internal::first(rng), tbb::internal::last(rng), body);
477 }
478 
479 #if __TBB_TASK_GROUP_CONTEXT
480 
482 template<typename Iterator, typename Body>
483 void parallel_do( Iterator first, Iterator last, const Body& body, task_group_context& context )
484 {
485  if ( first == last )
486  return;
487  internal::select_parallel_do( first, last, body, &Body::operator(), context );
488 }
489 
490 template<typename Range, typename Body>
491 void parallel_do(Range& rng, const Body& body, task_group_context& context) {
492  parallel_do(tbb::internal::first(rng), tbb::internal::last(rng), body, context);
493 }
494 
495 template<typename Range, typename Body>
496 void parallel_do(const Range& rng, const Body& body, task_group_context& context) {
497  parallel_do(tbb::internal::first(rng), tbb::internal::last(rng), body, context);
498 }
499 
500 #endif // __TBB_TASK_GROUP_CONTEXT
501 
503 
504 } // namespace
505 
506 #endif /* __TBB_parallel_do_H */
void add(const Item &item)
Add a work item to a running parallel_do.
Definition: parallel_do.h:51
Block of space aligned sufficiently to construct an array T with N elements.
Definition: aligned_space.h:33
*/
Definition: material.h:665
T * begin()
Pointer to beginning of array.
Definition: aligned_space.h:39
Definition: _flow_graph_async_msg_impl.h:32
Class the user supplied algorithm body uses to add new tasks.
Definition: parallel_do.h:42
The namespace tbb contains all components of the library.
Definition: parallel_for.h:44
void parallel_do(Iterator first, Iterator last, const Body &body)
Parallel iteration over a range, with optional addition of more work.
Definition: parallel_do.h:455