- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

# parallel_reduce.h

    00001 /*
    00002     Copyright 2005-2012 Intel Corporation.  All Rights Reserved.
    00003
    00004     This file is part of Threading Building Blocks.
    00005
    00006     Threading Building Blocks is free software; you can redistribute it
    00007     and/or modify it under the terms of the GNU General Public License
    00008     version 2 as published by the Free Software Foundation.
    00009
    00010     Threading Building Blocks is distributed in the hope that it will be
    00011     useful, but WITHOUT ANY WARRANTY; without even the implied warranty
    00012     of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    00013     GNU General Public License for more details.
    00014
    00015     You should have received a copy of the GNU General Public License
    00016     along with Threading Building Blocks; if not, write to the Free Software
    00017     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    00018
    00019     As a special exception, you may use this file as part of a free software
    00020     library without restriction.  Specifically, if other files instantiate
    00021     templates or use macros or inline functions from this file, or you compile
    00022     this file and link it with other files to produce an executable, this
    00023     file does not by itself cause the resulting executable to be covered by
    00024     the GNU General Public License.  This exception does not however
    00025     invalidate any other reasons why the executable file might be covered by
    00026     the GNU General Public License.
    00027 */
    00028
    00029 #ifndef __TBB_parallel_reduce_H
    00030 #define __TBB_parallel_reduce_H
    00031
    00032 #include <new>
    00033 #include "task.h"
    00034 #include "aligned_space.h"
    00035 #include "partitioner.h"
    00036 #include "tbb_profiling.h"
    00037
    00038 namespace tbb {
    00039
    00040 namespace interface6 {
    00042 namespace internal {
    00043
    00044     using namespace tbb::internal;
    00045
    00047     enum {
    00048         root_task, left_child, right_child
    00049     };
    00050
    00052     typedef char reduction_context;
    00053
    00055
    00056     template<typename Body>
    00057     class finish_reduce: public flag_task {
    00059         bool has_right_zombie;
    00060         const reduction_context my_context;
    00061         Body* my_body;
    00062         aligned_space<Body,1> zombie_space;
    00063         finish_reduce( reduction_context context_ ) :
    00064             has_right_zombie(false), // TODO: substitute by flag_task::child_stolen?
    00065             my_context(context_),
    00066             my_body(NULL)
    00067         {
    00068         }
    00069         task* execute() {
    00070             if( has_right_zombie ) {
    00071                 // Right child was stolen.
    00072                 Body* s = zombie_space.begin();
    00073                 my_body->join( *s );
    00074                 s->~Body();
    00075             }
    00076             if( my_context==left_child )
    00077                 itt_store_word_with_release( static_cast<finish_reduce*>(parent())->my_body, my_body );
    00078             return NULL;
    00079         }
    00080         template<typename Range,typename Body_, typename Partitioner>
    00081         friend class start_reduce;
    00082     };
    00083
    00085
    00086     template<typename Range, typename Body, typename Partitioner>
    00087     class start_reduce: public task {
    00088         typedef finish_reduce<Body> finish_type;
    00089         Body* my_body;
    00090         Range my_range;
    00091         typename Partitioner::task_partition_type my_partition;
    00092         reduction_context my_context; // TODO: factor out into start_reduce_base
    00093         /*override*/ task* execute();
    00094         template<typename Body_>
    00095         friend class finish_reduce;
    00096
    00097 public:
    00099         start_reduce( const Range& range, Body* body, Partitioner& partitioner ) :
    00100             my_body(body),
    00101             my_range(range),
    00102             my_partition(partitioner),
    00103             my_context(root_task)
    00104         {
    00105         }
    00107
    00108         start_reduce( start_reduce& parent_, split ) :
    00109             my_body(parent_.my_body),
    00110             my_range(parent_.my_range,split()),
    00111             my_partition(parent_.my_partition,split()),
    00112             my_context(right_child)
    00113         {
    00114             my_partition.set_affinity(*this);
    00115             parent_.my_context = left_child;
    00116         }
    00118
    00119         start_reduce( start_reduce& parent_, const Range& r, depth_t d ) :
    00120             my_body(parent_.my_body),
    00121             my_range(r),
    00122             my_partition(parent_.my_partition,split()),
    00123             my_context(right_child)
    00124         {
    00125             my_partition.set_affinity(*this);
    00126             my_partition.align_depth( d );
    00127             parent_.my_context = left_child;
    00128         }
    00130         /*override*/ void note_affinity( affinity_id id ) {
    00131             my_partition.note_affinity( id );
    00132         }
    00133         static void run( const Range& range, Body& body, Partitioner& partitioner ) {
    00134             if( !range.empty() ) {
    00135 #if !__TBB_TASK_GROUP_CONTEXT || TBB_JOIN_OUTER_TASK_GROUP
    00136                 task::spawn_root_and_wait( *new(task::allocate_root()) start_reduce(range,&body,partitioner) );
    00137 #else
    00138                 // Bound context prevents exceptions from body to affect nesting or sibling algorithms,
    00139                 // and allows users to handle exceptions safely by wrapping parallel_for in the try-block.
    00140                 task_group_context context;
    00141                 task::spawn_root_and_wait( *new(task::allocate_root(context)) start_reduce(range,&body,partitioner) );
    00142 #endif /* __TBB_TASK_GROUP_CONTEXT && !TBB_JOIN_OUTER_TASK_GROUP */
    00143             }
    00144         }
    00145 #if __TBB_TASK_GROUP_CONTEXT
    00146         static void run( const Range& range, Body& body, Partitioner& partitioner, task_group_context& context ) {
    00147             if( !range.empty() )
    00148                 task::spawn_root_and_wait( *new(task::allocate_root(context)) start_reduce(range,&body,partitioner) );
    00149         }
    00150 #endif /* __TBB_TASK_GROUP_CONTEXT */
    00152         finish_type *create_continuation() {
    00153             return new( allocate_continuation() ) finish_type(my_context);
    00154         }
    00156         void run_body( Range &r ) { (*my_body)( r ); }
    00157     };
    00158     template<typename Range, typename Body, typename Partitioner>
    00159     task* start_reduce<Range,Body,Partitioner>::execute() {
    00160         my_partition.check_being_stolen( *this );
    00161         if( my_context==right_child ) {
    00162             finish_type* parent_ptr = static_cast<finish_type*>(parent());
    00163             if( !itt_load_word_with_acquire(parent_ptr->my_body) ) { // TODO: replace by is_stolen_task() or by parent_ptr->ref_count() == 2???
    00164                 my_body = new( parent_ptr->zombie_space.begin() ) Body(*my_body,split());
    00165                 parent_ptr->has_right_zombie = true;
    00166             }
    00167         } else __TBB_ASSERT(my_context==root_task,NULL);// because left leaf spawns right leafs without recycling
    00168         my_partition.execute(*this, my_range);
    00169         if( my_context==left_child ) {
    00170             finish_type* parent_ptr = static_cast<finish_type*>(parent());
    00171             __TBB_ASSERT(my_body!=parent_ptr->zombie_space.begin(),NULL);
    00172             itt_store_word_with_release(parent_ptr->my_body, my_body );
    00173         }
    00174         return NULL;
    00175     }
    00176
    00178
    00179     template<typename Body>
    00180     class finish_deterministic_reduce: public task {
    00181         Body &my_left_body;
    00182         Body my_right_body;
    00183
    00184         finish_deterministic_reduce( Body &body ) :
    00185             my_left_body( body ),
    00186             my_right_body( body, split() )
    00187         {
    00188         }
    00189         task* execute() {
    00190             my_left_body.join( my_right_body );
    00191             return NULL;
    00192         }
    00193         template<typename Range,typename Body_>
    00194         friend class start_deterministic_reduce;
    00195     };
    00196
    00198
    00199     template<typename Range, typename Body>
    00200     class start_deterministic_reduce: public task {
    00201         typedef finish_deterministic_reduce<Body> finish_type;
    00202         Body &my_body;
    00203         Range my_range;
    00204         /*override*/ task* execute();
    00205
    00207         start_deterministic_reduce( const Range& range, Body& body ) :
    00208             my_body( body ),
    00209             my_range( range )
    00210         {
    00211         }
    00213
    00214         start_deterministic_reduce( start_deterministic_reduce& parent_, finish_type& c ) :
    00215             my_body( c.my_right_body ),
    00216             my_range( parent_.my_range, split() )
    00217         {
    00218         }
    00219
    00220 public:
    00221         static void run( const Range& range, Body& body ) {
    00222             if( !range.empty() ) {
    00223 #if !__TBB_TASK_GROUP_CONTEXT || TBB_JOIN_OUTER_TASK_GROUP
    00224                 task::spawn_root_and_wait( *new(task::allocate_root()) start_deterministic_reduce(range,&body) );
    00225 #else
    00226                 // Bound context prevents exceptions from body to affect nesting or sibling algorithms,
    00227                 // and allows users to handle exceptions safely by wrapping parallel_for in the try-block.
    00228                 task_group_context context;
    00229                 task::spawn_root_and_wait( *new(task::allocate_root(context)) start_deterministic_reduce(range,body) );
    00230 #endif /* __TBB_TASK_GROUP_CONTEXT && !TBB_JOIN_OUTER_TASK_GROUP */
    00231             }
    00232         }
    00233 #if __TBB_TASK_GROUP_CONTEXT
    00234         static void run( const Range& range, Body& body, task_group_context& context ) {
    00235             if( !range.empty() )
    00236                 task::spawn_root_and_wait( *new(task::allocate_root(context)) start_deterministic_reduce(range,body) );
    00237         }
    00238 #endif /* __TBB_TASK_GROUP_CONTEXT */
    00239     };
    00240
    00241     template<typename Range, typename Body>
    00242     task* start_deterministic_reduce<Range,Body>::execute() {
    00243         if( !my_range.is_divisible() ) {
    00244             my_body( my_range );
    00245             return NULL;
    00246         } else {
    00247             finish_type& c = *new( allocate_continuation() ) finish_type( my_body );
    00248             recycle_as_child_of(c);
    00249             c.set_ref_count(2);
    00250             start_deterministic_reduce& b = *new( c.allocate_child() ) start_deterministic_reduce( *this, c );
    00251             task::spawn(b);
    00252             return this;
    00253         }
    00254     }
    00255 } // namespace internal
    00257 } //namespace interfaceX
    00258
    00260 namespace internal {
    00261     using interface6::internal::start_reduce;
    00262     using interface6::internal::start_deterministic_reduce;
    00264
    00268     template<typename Range, typename Value, typename RealBody, typename Reduction>
    00269     class lambda_reduce_body {
    00270
    00271 //FIXME: decide if my_real_body, my_reduction, and identity_element should be copied or referenced
    00272 //       (might require some performance measurements)
    00273
    00274         const Value&     identity_element;
    00275         const RealBody&  my_real_body;
    00276         const Reduction& my_reduction;
    00277         Value            my_value;
    00278         lambda_reduce_body& operator= ( const lambda_reduce_body& other );
    00279     public:
    00280         lambda_reduce_body( const Value& identity, const RealBody& body, const Reduction& reduction )
    00281             : identity_element(identity)
    00282             , my_real_body(body)
    00283             , my_reduction(reduction)
    00284             , my_value(identity)
    00285         { }
    00286         lambda_reduce_body( const lambda_reduce_body& other )
    00287             : identity_element(other.identity_element)
    00288             , my_real_body(other.my_real_body)
    00289             , my_reduction(other.my_reduction)
    00290             , my_value(other.my_value)
    00291         { }
    00292         lambda_reduce_body( lambda_reduce_body& other, tbb::split )
    00293             : identity_element(other.identity_element)
    00294             , my_real_body(other.my_real_body)
    00295             , my_reduction(other.my_reduction)
    00296             , my_value(other.identity_element)
    00297         { }
    00298         void operator()(Range& range) {
    00299             my_value = my_real_body(range, const_cast<const Value&>(my_value));
    00300         }
    00301         void join( lambda_reduce_body& rhs ) {
    00302             my_value = my_reduction(const_cast<const Value&>(my_value), const_cast<const Value&>(rhs.my_value));
    00303         }
    00304         Value result() const {
    00305             return my_value;
    00306         }
    00307     };
    00308
    00309 } // namespace internal
    00311
    00312 // Requirements on Range concept are documented in blocked_range.h
    00313
    00332
    00334
    00335 template<typename Range, typename Body>
    00336 void parallel_reduce( const Range& range, Body& body ) {
    00337     internal::start_reduce<Range,Body, const __TBB_DEFAULT_PARTITIONER>::run( range, body, __TBB_DEFAULT_PARTITIONER() );
    00338 }
    00339
    00341
    00342 template<typename Range, typename Body>
    00343 void parallel_reduce( const Range& range, Body& body, const simple_partitioner& partitioner ) {
    00344     internal::start_reduce<Range,Body,const simple_partitioner>::run( range, body, partitioner );
    00345 }
    00346
    00348
    00349 template<typename Range, typename Body>
    00350 void parallel_reduce( const Range& range, Body& body, const auto_partitioner& partitioner ) {
    00351     internal::start_reduce<Range,Body,const auto_partitioner>::run( range, body, partitioner );
    00352 }
    00353
    00355
    00356 template<typename Range, typename Body>
    00357 void parallel_reduce( const Range& range, Body& body, affinity_partitioner& partitioner ) {
    00358     internal::start_reduce<Range,Body,affinity_partitioner>::run( range, body, partitioner );
    00359 }
    00360
    00361 #if __TBB_TASK_GROUP_CONTEXT
    00363
    00364 template<typename Range, typename Body>
    00365 void parallel_reduce( const Range& range, Body& body, const simple_partitioner& partitioner, task_group_context& context ) {
    00366     internal::start_reduce<Range,Body,const simple_partitioner>::run( range, body, partitioner, context );
    00367 }
    00368
    00370
    00371 template<typename Range, typename Body>
    00372 void parallel_reduce( const Range& range, Body& body, const auto_partitioner& partitioner, task_group_context& context ) {
    00373     internal::start_reduce<Range,Body,const auto_partitioner>::run( range, body, partitioner, context );
    00374 }
    00375
    00377
    00378 template<typename Range, typename Body>
    00379 void parallel_reduce( const Range& range, Body& body, affinity_partitioner& partitioner, task_group_context& context ) {
    00380     internal::start_reduce<Range,Body,affinity_partitioner>::run( range, body, partitioner, context );
    00381 }
    00382 #endif /* __TBB_TASK_GROUP_CONTEXT */
    00383
    00387
    00388
    00389 template<typename Range, typename Value, typename RealBody, typename Reduction>
    00390 Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction ) {
    00391     internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction);
    00392     internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,const __TBB_DEFAULT_PARTITIONER>
    00393                           ::run(range, body, __TBB_DEFAULT_PARTITIONER() );
    00394     return body.result();
    00395 }
    00396
    00398
    00399 template<typename Range, typename Value, typename RealBody, typename Reduction>
    00400 Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction,
    00401                        const simple_partitioner& partitioner ) {
    00402     internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction);
    00403     internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,const simple_partitioner>
    00404                           ::run(range, body, partitioner );
    00405     return body.result();
    00406 }
    00407
    00409
    00410 template<typename Range, typename Value, typename RealBody, typename Reduction>
    00411 Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction,
    00412                        const auto_partitioner& partitioner ) {
    00413     internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction);
    00414     internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,const auto_partitioner>
    00415                           ::run( range, body, partitioner );
    00416     return body.result();
    00417 }
    00418
    00420
    00421 template<typename Range, typename Value, typename RealBody, typename Reduction>
    00422 Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction,
    00423                        affinity_partitioner& partitioner ) {
    00424     internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction);
    00425     internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,affinity_partitioner>
    00426                                         ::run( range, body, partitioner );
    00427     return body.result();
    00428 }
    00429
    00430 #if __TBB_TASK_GROUP_CONTEXT
    00432
    00433 template<typename Range, typename Value, typename RealBody, typename Reduction>
    00434 Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction,
    00435                        const simple_partitioner& partitioner, task_group_context& context ) {
    00436     internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction);
    00437     internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,const simple_partitioner>
    00438                           ::run( range, body, partitioner, context );
    00439     return body.result();
    00440 }
    00441
    00443
    00444 template<typename Range, typename Value, typename RealBody, typename Reduction>
    00445 Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction,
    00446                        const auto_partitioner& partitioner, task_group_context& context ) {
    00447     internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction);
    00448     internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,const auto_partitioner>
    00449                           ::run( range, body, partitioner, context );
    00450     return body.result();
    00451 }
    00452
    00454
    00455 template<typename Range, typename Value, typename RealBody, typename Reduction>
    00456 Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction,
    00457                        affinity_partitioner& partitioner, task_group_context& context ) {
    00458     internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction);
    00459     internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,affinity_partitioner>
    00460                                         ::run( range, body, partitioner, context );
    00461     return body.result();
    00462 }
    00463 #endif /* __TBB_TASK_GROUP_CONTEXT */
    00464
    00466
    00467 template<typename Range, typename Body>
    00468 void parallel_deterministic_reduce( const Range& range, Body& body ) {
    00469     internal::start_deterministic_reduce<Range,Body>::run( range, body );
    00470 }
    00471
    00472 #if __TBB_TASK_GROUP_CONTEXT
    00474
    00475 template<typename Range, typename Body>
    00476 void parallel_deterministic_reduce( const Range& range, Body& body, task_group_context& context ) {
    00477     internal::start_deterministic_reduce<Range,Body>::run( range, body, context );
    00478 }
    00479 #endif /* __TBB_TASK_GROUP_CONTEXT */
    00480
    00484
    00485
    00486 template<typename Range, typename Value, typename RealBody, typename Reduction>
    00487 Value parallel_deterministic_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction ) {
    00488     internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction);
    00489     internal::start_deterministic_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction> >
    00490                           ::run(range, body);
    00491     return body.result();
    00492 }
    00493
    00494 #if __TBB_TASK_GROUP_CONTEXT
    00496
    00497 template<typename Range, typename Value, typename RealBody, typename Reduction>
    00498 Value parallel_deterministic_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction,
    00499                        task_group_context& context ) {
    00500     internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction);
    00501     internal::start_deterministic_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction> >
    00502                           ::run( range, body, context );
    00503     return body.result();
    00504 }
    00505 #endif /* __TBB_TASK_GROUP_CONTEXT */
    00506
    00507
    00508 } // namespace tbb
    00509
    00510 #endif /* __TBB_parallel_reduce_H */
    00511

Copyright © 2005-2012 Intel Corporation. All Rights Reserved.

Intel, Pentium, Intel Xeon, Itanium, Intel XScale and VTune are registered trademarks or trademarks of Intel Corporation or its subsidiaries in the United States and other countries.

* Other names and brands may be claimed as the property of others.