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

- File List
- File Members

# parallel_scan.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_scan_H
    00030 #define __TBB_parallel_scan_H
    00031
    00032 #include "task.h"
    00033 #include "aligned_space.h"
    00034 #include <new>
    00035 #include "partitioner.h"
    00036
    00037 namespace tbb {
    00038
    00040
    00041 struct pre_scan_tag {
    00042     static bool is_final_scan() {return false;}
    00043 };
    00044
    00046
    00047 struct final_scan_tag {
    00048     static bool is_final_scan() {return true;}
    00049 };
    00050
    00052 namespace internal {
    00053
    00055
    00056     template<typename Range, typename Body>
    00057     class final_sum: public task {
    00058     public:
    00059         Body body;
    00060     private:
    00061         aligned_space<Range,1> range;
    00063         Body* stuff_last;
    00064     public:
    00065         final_sum( Body& body_ ) :
    00066             body(body_,split())
    00067         {
    00068             poison_pointer(stuff_last);
    00069         }
    00070         ~final_sum() {
    00071             range.begin()->~Range();
    00072         }
    00073         void finish_construction( const Range& range_, Body* stuff_last_ ) {
    00074             new( range.begin() ) Range(range_);
    00075             stuff_last = stuff_last_;
    00076         }
    00077     private:
    00078         /*override*/ task* execute() {
    00079             body( *range.begin(), final_scan_tag() );
    00080             if( stuff_last )
    00081                 stuff_last->assign(body);
    00082             return NULL;
    00083         }
    00084     };
    00085
    00087
    00088     template<typename Range, typename Body>
    00089     class sum_node: public task {
    00090         typedef final_sum<Range,Body> final_sum_type;
    00091     public:
    00092         final_sum_type *incoming;
    00093         final_sum_type *body;
    00094         Body *stuff_last;
    00095     private:
    00096         final_sum_type *left_sum;
    00097         sum_node *left;
    00098         sum_node *right;
    00099         bool left_is_final;
    00100         Range range;
    00101         sum_node( const Range range_, bool left_is_final_ ) :
    00102             left_sum(NULL),
    00103             left(NULL),
    00104             right(NULL),
    00105             left_is_final(left_is_final_),
    00106             range(range_)
    00107         {
    00108             // Poison fields that will be set by second pass.
    00109             poison_pointer(body);
    00110             poison_pointer(incoming);
    00111         }
    00112         task* create_child( const Range& range_, final_sum_type& f, sum_node* n, final_sum_type* incoming_, Body* stuff_last_ ) {
    00113             if( !n ) {
    00114                 f.recycle_as_child_of( *this );
    00115                 f.finish_construction( range_, stuff_last_ );
    00116                 return &f;
    00117             } else {
    00118                 n->body = &f;
    00119                 n->incoming = incoming_;
    00120                 n->stuff_last = stuff_last_;
    00121                 return n;
    00122             }
    00123         }
    00124         /*override*/ task* execute() {
    00125             if( body ) {
    00126                 if( incoming )
    00127                     left_sum->body.reverse_join( incoming->body );
    00128                 recycle_as_continuation();
    00129                 sum_node& c = *this;
    00130                 task* b = c.create_child(Range(range,split()),*left_sum,right,left_sum,stuff_last);
    00131                 task* a = left_is_final ? NULL : c.create_child(range,*body,left,incoming,NULL);
    00132                 set_ref_count( (a!=NULL)+(b!=NULL) );
    00133                 body = NULL;
    00134                 if( a ) spawn(*b);
    00135                 else a = b;
    00136                 return a;
    00137             } else {
    00138                 return NULL;
    00139             }
    00140         }
    00141         template<typename Range_,typename Body_,typename Partitioner_>
    00142         friend class start_scan;
    00143
    00144         template<typename Range_,typename Body_>
    00145         friend class finish_scan;
    00146     };
    00147
    00149
    00150     template<typename Range, typename Body>
    00151     class finish_scan: public task {
    00152         typedef sum_node<Range,Body> sum_node_type;
    00153         typedef final_sum<Range,Body> final_sum_type;
    00154         final_sum_type** const sum;
    00155         sum_node_type*& return_slot;
    00156     public:
    00157         final_sum_type* right_zombie;
    00158         sum_node_type& result;
    00159
    00160         /*override*/ task* execute() {
    00161             __TBB_ASSERT( result.ref_count()==(result.left!=NULL)+(result.right!=NULL), NULL );
    00162             if( result.left )
    00163                 result.left_is_final = false;
    00164             if( right_zombie && sum )
    00165                 ((*sum)->body).reverse_join(result.left_sum->body);
    00166             __TBB_ASSERT( !return_slot, NULL );
    00167             if( right_zombie || result.right ) {
    00168                 return_slot = &result;
    00169             } else {
    00170                 destroy( result );
    00171             }
    00172             if( right_zombie && !sum && !result.right ) destroy(*right_zombie);
    00173             return NULL;
    00174         }
    00175
    00176         finish_scan( sum_node_type*& return_slot_, final_sum_type** sum_, sum_node_type& result_ ) :
    00177             sum(sum_),
    00178             return_slot(return_slot_),
    00179             right_zombie(NULL),
    00180             result(result_)
    00181         {
    00182             __TBB_ASSERT( !return_slot, NULL );
    00183         }
    00184     };
    00185
    00187
    00188     template<typename Range, typename Body, typename Partitioner=simple_partitioner>
    00189     class start_scan: public task {
    00190         typedef sum_node<Range,Body> sum_node_type;
    00191         typedef final_sum<Range,Body> final_sum_type;
    00192         final_sum_type* body;
    00194         final_sum_type** sum;
    00195         sum_node_type** return_slot;
    00197         sum_node_type* parent_sum;
    00198         bool is_final;
    00199         bool is_right_child;
    00200         Range range;
    00201         typename Partitioner::partition_type partition;
    00202         /*override*/ task* execute();
    00203     public:
    00204         start_scan( sum_node_type*& return_slot_, start_scan& parent_, sum_node_type* parent_sum_ ) :
    00205             body(parent_.body),
    00206             sum(parent_.sum),
    00207             return_slot(&return_slot_),
    00208             parent_sum(parent_sum_),
    00209             is_final(parent_.is_final),
    00210             is_right_child(false),
    00211             range(parent_.range,split()),
    00212             partition(parent_.partition,split())
    00213         {
    00214             __TBB_ASSERT( !*return_slot, NULL );
    00215         }
    00216
    00217         start_scan( sum_node_type*& return_slot_, const Range& range_, final_sum_type& body_, const Partitioner& partitioner_) :
    00218             body(&body_),
    00219             sum(NULL),
    00220             return_slot(&return_slot_),
    00221             parent_sum(NULL),
    00222             is_final(true),
    00223             is_right_child(false),
    00224             range(range_),
    00225             partition(partitioner_)
    00226         {
    00227             __TBB_ASSERT( !*return_slot, NULL );
    00228         }
    00229
    00230         static void run( const Range& range_, Body& body_, const Partitioner& partitioner_ ) {
    00231             if( !range_.empty() ) {
    00232                 typedef internal::start_scan<Range,Body,Partitioner> start_pass1_type;
    00233                 internal::sum_node<Range,Body>* root = NULL;
    00234                 typedef internal::final_sum<Range,Body> final_sum_type;
    00235                 final_sum_type* temp_body = new(task::allocate_root()) final_sum_type( body_ );
    00236                 start_pass1_type& pass1 = *new(task::allocate_root()) start_pass1_type(
    00237                     /*return_slot=*/root,
    00238                     range_,
    00239                     *temp_body,
    00240                     partitioner_ );
    00241                 task::spawn_root_and_wait( pass1 );
    00242                 if( root ) {
    00243                     root->body = temp_body;
    00244                     root->incoming = NULL;
    00245                     root->stuff_last = &body_;
    00246                     task::spawn_root_and_wait( *root );
    00247                 } else {
    00248                     body_.assign(temp_body->body);
    00249                     temp_body->finish_construction( range_, NULL );
    00250                     temp_body->destroy(*temp_body);
    00251                 }
    00252             }
    00253         }
    00254     };
    00255
    00256     template<typename Range, typename Body, typename Partitioner>
    00257     task* start_scan<Range,Body,Partitioner>::execute() {
    00258         typedef internal::finish_scan<Range,Body> finish_pass1_type;
    00259         finish_pass1_type* p = parent_sum ? static_cast<finish_pass1_type*>( parent() ) : NULL;
    00260         // Inspecting p->result.left_sum would ordinarily be a race condition.
    00261         // But we inspect it only if we are not a stolen task, in which case we
    00262         // know that task assigning to p->result.left_sum has completed.
    00263         bool treat_as_stolen = is_right_child && (is_stolen_task() || body!=p->result.left_sum);
    00264         if( treat_as_stolen ) {
    00265             // Invocation is for right child that has been really stolen or needs to be virtually stolen
    00266             p->right_zombie = body = new( allocate_root() ) final_sum_type(body->body);
    00267             is_final = false;
    00268         }
    00269         task* next_task = NULL;
    00270         if( (is_right_child && !treat_as_stolen) || !range.is_divisible() || partition.should_execute_range(*this) ) {
    00271             if( is_final )
    00272                 (body->body)( range, final_scan_tag() );
    00273             else if( sum )
    00274                 (body->body)( range, pre_scan_tag() );
    00275             if( sum )
    00276                 *sum = body;
    00277             __TBB_ASSERT( !*return_slot, NULL );
    00278         } else {
    00279             sum_node_type* result;
    00280             if( parent_sum )
    00281                 result = new(allocate_additional_child_of(*parent_sum)) sum_node_type(range,/*left_is_final=*/is_final);
    00282             else
    00283                 result = new(task::allocate_root()) sum_node_type(range,/*left_is_final=*/is_final);
    00284             finish_pass1_type& c = *new( allocate_continuation()) finish_pass1_type(*return_slot,sum,*result);
    00285             // Split off right child
    00286             start_scan& b = *new( c.allocate_child() ) start_scan( /*return_slot=*/result->right, *this, result );
    00287             b.is_right_child = true;
    00288             // Left child is recycling of *this.  Must recycle this before spawning b,
    00289             // otherwise b might complete and decrement c.ref_count() to zero, which
    00290             // would cause c.execute() to run prematurely.
    00291             recycle_as_child_of(c);
    00292             c.set_ref_count(2);
    00293             c.spawn(b);
    00294             sum = &result->left_sum;
    00295             return_slot = &result->left;
    00296             is_right_child = false;
    00297             next_task = this;
    00298             parent_sum = result;
    00299             __TBB_ASSERT( !*return_slot, NULL );
    00300         }
    00301         return next_task;
    00302     }
    00303 } // namespace internal
    00305
    00306 // Requirements on Range concept are documented in blocked_range.h
    00307
    00325
    00327
    00328 template<typename Range, typename Body>
    00329 void parallel_scan( const Range& range, Body& body ) {
    00330     internal::start_scan<Range,Body,__TBB_DEFAULT_PARTITIONER>::run(range,body,__TBB_DEFAULT_PARTITIONER());
    00331 }
    00332
    00334
    00335 template<typename Range, typename Body>
    00336 void parallel_scan( const Range& range, Body& body, const simple_partitioner& partitioner ) {
    00337     internal::start_scan<Range,Body,simple_partitioner>::run(range,body,partitioner);
    00338 }
    00339
    00341
    00342 template<typename Range, typename Body>
    00343 void parallel_scan( const Range& range, Body& body, const auto_partitioner& partitioner ) {
    00344     internal::start_scan<Range,Body,auto_partitioner>::run(range,body,partitioner);
    00345 }
    00347
    00348 } // namespace tbb
    00349
    00350 #endif /* __TBB_parallel_scan_H */
    00351

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.