1 /* 2 Copyright (c) 2005-2022 Intel Corporation 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 #ifndef __TBB_partitioner_H 18 #define __TBB_partitioner_H 19 20 #ifndef __TBB_INITIAL_CHUNKS 21 // initial task divisions per thread 22 #define __TBB_INITIAL_CHUNKS 2 23 #endif 24 #ifndef __TBB_RANGE_POOL_CAPACITY 25 // maximum number of elements in range pool 26 #define __TBB_RANGE_POOL_CAPACITY 8 27 #endif 28 #ifndef __TBB_INIT_DEPTH 29 // initial value for depth of range pool 30 #define __TBB_INIT_DEPTH 5 31 #endif 32 #ifndef __TBB_DEMAND_DEPTH_ADD 33 // when imbalance is found range splits this value times more 34 #define __TBB_DEMAND_DEPTH_ADD 1 35 #endif 36 37 #include "detail/_config.h" 38 #include "detail/_namespace_injection.h" 39 #include "detail/_aligned_space.h" 40 #include "detail/_utils.h" 41 #include "detail/_template_helpers.h" 42 #include "detail/_range_common.h" 43 #include "detail/_task.h" 44 #include "detail/_small_object_pool.h" 45 46 #include "cache_aligned_allocator.h" 47 #include "task_group.h" // task_group_context 48 #include "task_arena.h" 49 50 #include <algorithm> 51 #include <atomic> 52 #include <type_traits> 53 54 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) 55 // Workaround for overzealous compiler warnings 56 #pragma warning (push) 57 #pragma warning (disable: 4244) 58 #endif 59 60 namespace tbb { 61 namespace detail { 62 63 namespace d1 { 64 class auto_partitioner; 65 class simple_partitioner; 66 class static_partitioner; 67 class affinity_partitioner; 68 class affinity_partition_type; 69 class affinity_partitioner_base; 70 71 inline std::size_t get_initial_auto_partitioner_divisor() { 72 const std::size_t factor = 4; 73 return factor * max_concurrency(); 74 } 75 76 //! Defines entry point for affinity partitioner into oneTBB run-time library. 77 class affinity_partitioner_base: no_copy { 78 friend class affinity_partitioner; 79 friend class affinity_partition_type; 80 //! Array that remembers affinities of tree positions to affinity_id. 81 /** nullptr if my_size==0. */ 82 slot_id* my_array; 83 //! Number of elements in my_array. 84 std::size_t my_size; 85 //! Zeros the fields. 86 affinity_partitioner_base() : my_array(nullptr), my_size(0) {} 87 //! Deallocates my_array. 88 ~affinity_partitioner_base() { resize(0); } 89 //! Resize my_array. 90 /** Retains values if resulting size is the same. */ 91 void resize(unsigned factor) { 92 // Check factor to avoid asking for number of workers while there might be no arena. 93 unsigned max_threads_in_arena = max_concurrency(); 94 std::size_t new_size = factor ? factor * max_threads_in_arena : 0; 95 if (new_size != my_size) { 96 if (my_array) { 97 r1::cache_aligned_deallocate(my_array); 98 // Following two assignments must be done here for sake of exception safety. 99 my_array = nullptr; 100 my_size = 0; 101 } 102 if (new_size) { 103 my_array = static_cast<slot_id*>(r1::cache_aligned_allocate(new_size * sizeof(slot_id))); 104 std::fill_n(my_array, new_size, no_slot); 105 my_size = new_size; 106 } 107 } 108 } 109 }; 110 111 template<typename Range, typename Body, typename Partitioner> struct start_for; 112 template<typename Range, typename Body, typename Partitioner> struct start_scan; 113 template<typename Range, typename Body, typename Partitioner> struct start_reduce; 114 template<typename Range, typename Body, typename Partitioner> struct start_deterministic_reduce; 115 116 struct node { 117 node* my_parent{}; 118 std::atomic<int> m_ref_count{}; 119 120 node() = default; 121 node(node* parent, int ref_count) : 122 my_parent{parent}, m_ref_count{ref_count} { 123 __TBB_ASSERT(ref_count > 0, "The ref count must be positive"); 124 } 125 }; 126 127 struct wait_node : node { 128 wait_node() : node{ nullptr, 1 } {} 129 wait_context m_wait{1}; 130 }; 131 132 //! Join task node that contains shared flag for stealing feedback 133 struct tree_node : public node { 134 small_object_allocator m_allocator; 135 std::atomic<bool> m_child_stolen{false}; 136 137 tree_node(node* parent, int ref_count, small_object_allocator& alloc) 138 : node{parent, ref_count} 139 , m_allocator{alloc} {} 140 141 void join(task_group_context*) {/*dummy, required only for reduction algorithms*/}; 142 143 template <typename Task> 144 static void mark_task_stolen(Task &t) { 145 std::atomic<bool> &flag = static_cast<tree_node*>(t.my_parent)->m_child_stolen; 146 #if TBB_USE_PROFILING_TOOLS 147 // Threading tools respect lock prefix but report false-positive data-race via plain store 148 flag.exchange(true); 149 #else 150 flag.store(true, std::memory_order_relaxed); 151 #endif // TBB_USE_PROFILING_TOOLS 152 } 153 template <typename Task> 154 static bool is_peer_stolen(Task &t) { 155 return static_cast<tree_node*>(t.my_parent)->m_child_stolen.load(std::memory_order_relaxed); 156 } 157 }; 158 159 // Context used to check cancellation state during reduction join process 160 template<typename TreeNodeType> 161 void fold_tree(node* n, const execution_data& ed) { 162 for (;;) { 163 __TBB_ASSERT(n->m_ref_count.load(std::memory_order_relaxed) > 0, "The refcount must be positive."); 164 call_itt_task_notify(releasing, n); 165 if (--n->m_ref_count > 0) { 166 return; 167 } 168 node* parent = n->my_parent; 169 if (!parent) { 170 break; 171 }; 172 173 call_itt_task_notify(acquired, n); 174 TreeNodeType* self = static_cast<TreeNodeType*>(n); 175 self->join(ed.context); 176 self->m_allocator.delete_object(self, ed); 177 n = parent; 178 } 179 // Finish parallel for execution when the root (last node) is reached 180 static_cast<wait_node*>(n)->m_wait.release(); 181 } 182 183 //! Depth is a relative depth of recursive division inside a range pool. Relative depth allows 184 //! infinite absolute depth of the recursion for heavily unbalanced workloads with range represented 185 //! by a number that cannot fit into machine word. 186 typedef unsigned char depth_t; 187 188 //! Range pool stores ranges of type T in a circular buffer with MaxCapacity 189 template <typename T, depth_t MaxCapacity> 190 class range_vector { 191 depth_t my_head; 192 depth_t my_tail; 193 depth_t my_size; 194 depth_t my_depth[MaxCapacity]; // relative depths of stored ranges 195 tbb::detail::aligned_space<T, MaxCapacity> my_pool; 196 197 public: 198 //! initialize via first range in pool 199 range_vector(const T& elem) : my_head(0), my_tail(0), my_size(1) { 200 my_depth[0] = 0; 201 new( static_cast<void *>(my_pool.begin()) ) T(elem);//TODO: std::move? 202 } 203 ~range_vector() { 204 while( !empty() ) pop_back(); 205 } 206 bool empty() const { return my_size == 0; } 207 depth_t size() const { return my_size; } 208 //! Populates range pool via ranges up to max depth or while divisible 209 //! max_depth starts from 0, e.g. value 2 makes 3 ranges in the pool up to two 1/4 pieces 210 void split_to_fill(depth_t max_depth) { 211 while( my_size < MaxCapacity && is_divisible(max_depth) ) { 212 depth_t prev = my_head; 213 my_head = (my_head + 1) % MaxCapacity; 214 new(my_pool.begin()+my_head) T(my_pool.begin()[prev]); // copy TODO: std::move? 215 my_pool.begin()[prev].~T(); // instead of assignment 216 new(my_pool.begin()+prev) T(my_pool.begin()[my_head], detail::split()); // do 'inverse' split 217 my_depth[my_head] = ++my_depth[prev]; 218 my_size++; 219 } 220 } 221 void pop_back() { 222 __TBB_ASSERT(my_size > 0, "range_vector::pop_back() with empty size"); 223 my_pool.begin()[my_head].~T(); 224 my_size--; 225 my_head = (my_head + MaxCapacity - 1) % MaxCapacity; 226 } 227 void pop_front() { 228 __TBB_ASSERT(my_size > 0, "range_vector::pop_front() with empty size"); 229 my_pool.begin()[my_tail].~T(); 230 my_size--; 231 my_tail = (my_tail + 1) % MaxCapacity; 232 } 233 T& back() { 234 __TBB_ASSERT(my_size > 0, "range_vector::back() with empty size"); 235 return my_pool.begin()[my_head]; 236 } 237 T& front() { 238 __TBB_ASSERT(my_size > 0, "range_vector::front() with empty size"); 239 return my_pool.begin()[my_tail]; 240 } 241 //! similarly to front(), returns depth of the first range in the pool 242 depth_t front_depth() { 243 __TBB_ASSERT(my_size > 0, "range_vector::front_depth() with empty size"); 244 return my_depth[my_tail]; 245 } 246 depth_t back_depth() { 247 __TBB_ASSERT(my_size > 0, "range_vector::back_depth() with empty size"); 248 return my_depth[my_head]; 249 } 250 bool is_divisible(depth_t max_depth) { 251 return back_depth() < max_depth && back().is_divisible(); 252 } 253 }; 254 255 //! Provides default methods for partition objects and common algorithm blocks. 256 template <typename Partition> 257 struct partition_type_base { 258 typedef detail::split split_type; 259 // decision makers 260 void note_affinity( slot_id ) {} 261 template <typename Task> 262 bool check_being_stolen(Task&, const execution_data&) { return false; } // part of old should_execute_range() 263 template <typename Range> split_type get_split() { return split(); } 264 Partition& self() { return *static_cast<Partition*>(this); } // CRTP helper 265 266 template<typename StartType, typename Range> 267 void work_balance(StartType &start, Range &range, const execution_data&) { 268 start.run_body( range ); // static partitioner goes here 269 } 270 271 template<typename StartType, typename Range> 272 void execute(StartType &start, Range &range, execution_data& ed) { 273 // The algorithm in a few words ([]-denotes calls to decision methods of partitioner): 274 // [If this task is stolen, adjust depth and divisions if necessary, set flag]. 275 // If range is divisible { 276 // Spread the work while [initial divisions left]; 277 // Create trap task [if necessary]; 278 // } 279 // If not divisible or [max depth is reached], execute, else do the range pool part 280 if ( range.is_divisible() ) { 281 if ( self().is_divisible() ) { 282 do { // split until is divisible 283 typename Partition::split_type split_obj = self().template get_split<Range>(); 284 start.offer_work( split_obj, ed ); 285 } while ( range.is_divisible() && self().is_divisible() ); 286 } 287 } 288 self().work_balance(start, range, ed); 289 } 290 }; 291 292 //! Provides default splitting strategy for partition objects. 293 template <typename Partition> 294 struct adaptive_mode : partition_type_base<Partition> { 295 typedef Partition my_partition; 296 std::size_t my_divisor; 297 // For affinity_partitioner, my_divisor indicates the number of affinity array indices the task reserves. 298 // A task which has only one index must produce the right split without reserved index in order to avoid 299 // it to be overwritten in note_affinity() of the created (right) task. 300 // I.e. a task created deeper than the affinity array can remember must not save its affinity (LIFO order) 301 static const unsigned factor = 1; 302 adaptive_mode() : my_divisor(get_initial_auto_partitioner_divisor() / 4 * my_partition::factor) {} 303 adaptive_mode(adaptive_mode &src, split) : my_divisor(do_split(src, split())) {} 304 adaptive_mode(adaptive_mode&, const proportional_split&) : my_divisor(0) 305 { 306 // left blank as my_divisor gets overridden in the successors' constructors 307 } 308 /*! Override do_split methods in order to specify splitting strategy */ 309 std::size_t do_split(adaptive_mode &src, split) { 310 return src.my_divisor /= 2u; 311 } 312 }; 313 314 //! Helper type for checking availability of proportional_split constructor 315 template <typename T> using supports_proportional_splitting = typename std::is_constructible<T, T&, proportional_split&>; 316 317 //! A helper class to create a proportional_split object for a given type of Range. 318 /** If the Range has proportional_split constructor, 319 then created object splits a provided value in an implemenation-defined proportion; 320 otherwise it represents equal-size split. */ 321 // TODO: check if this helper can be a nested class of proportional_mode. 322 template <typename Range, typename = void> 323 struct proportion_helper { 324 static proportional_split get_split(std::size_t) { return proportional_split(1,1); } 325 }; 326 327 template <typename Range> 328 struct proportion_helper<Range, typename std::enable_if<supports_proportional_splitting<Range>::value>::type> { 329 static proportional_split get_split(std::size_t n) { 330 std::size_t right = n / 2; 331 std::size_t left = n - right; 332 return proportional_split(left, right); 333 } 334 }; 335 336 //! Provides proportional splitting strategy for partition objects 337 template <typename Partition> 338 struct proportional_mode : adaptive_mode<Partition> { 339 typedef Partition my_partition; 340 using partition_type_base<Partition>::self; // CRTP helper to get access to derived classes 341 342 proportional_mode() : adaptive_mode<Partition>() {} 343 proportional_mode(proportional_mode &src, split) : adaptive_mode<Partition>(src, split()) {} 344 proportional_mode(proportional_mode &src, const proportional_split& split_obj) 345 : adaptive_mode<Partition>(src, split_obj) 346 { 347 self().my_divisor = do_split(src, split_obj); 348 } 349 std::size_t do_split(proportional_mode &src, const proportional_split& split_obj) { 350 std::size_t portion = split_obj.right() * my_partition::factor; 351 portion = (portion + my_partition::factor/2) & (0ul - my_partition::factor); 352 src.my_divisor -= portion; 353 return portion; 354 } 355 bool is_divisible() { // part of old should_execute_range() 356 return self().my_divisor > my_partition::factor; 357 } 358 template <typename Range> 359 proportional_split get_split() { 360 // Create a proportion for the number of threads expected to handle "this" subrange 361 return proportion_helper<Range>::get_split( self().my_divisor / my_partition::factor ); 362 } 363 }; 364 365 static std::size_t get_initial_partition_head() { 366 int current_index = tbb::this_task_arena::current_thread_index(); 367 if (current_index == tbb::task_arena::not_initialized) 368 current_index = 0; 369 return size_t(current_index); 370 } 371 372 //! Provides default linear indexing of partitioner's sequence 373 template <typename Partition> 374 struct linear_affinity_mode : proportional_mode<Partition> { 375 std::size_t my_head; 376 std::size_t my_max_affinity; 377 using proportional_mode<Partition>::self; 378 linear_affinity_mode() : proportional_mode<Partition>(), my_head(get_initial_partition_head()), 379 my_max_affinity(self().my_divisor) {} 380 linear_affinity_mode(linear_affinity_mode &src, split) : proportional_mode<Partition>(src, split()) 381 , my_head((src.my_head + src.my_divisor) % src.my_max_affinity), my_max_affinity(src.my_max_affinity) {} 382 linear_affinity_mode(linear_affinity_mode &src, const proportional_split& split_obj) : proportional_mode<Partition>(src, split_obj) 383 , my_head((src.my_head + src.my_divisor) % src.my_max_affinity), my_max_affinity(src.my_max_affinity) {} 384 void spawn_task(task& t, task_group_context& ctx) { 385 if (self().my_divisor) { 386 spawn(t, ctx, slot_id(my_head)); 387 } else { 388 spawn(t, ctx); 389 } 390 } 391 }; 392 393 static bool is_stolen_task(const execution_data& ed) { 394 return execution_slot(ed) != original_slot(ed); 395 } 396 397 /*! Determine work-balance phase implementing splitting & stealing actions */ 398 template<class Mode> 399 struct dynamic_grainsize_mode : Mode { 400 using Mode::self; 401 enum { 402 begin = 0, 403 run, 404 pass 405 } my_delay; 406 depth_t my_max_depth; 407 static const unsigned range_pool_size = __TBB_RANGE_POOL_CAPACITY; 408 dynamic_grainsize_mode(): Mode() 409 , my_delay(begin) 410 , my_max_depth(__TBB_INIT_DEPTH) {} 411 dynamic_grainsize_mode(dynamic_grainsize_mode& p, split) 412 : Mode(p, split()) 413 , my_delay(pass) 414 , my_max_depth(p.my_max_depth) {} 415 dynamic_grainsize_mode(dynamic_grainsize_mode& p, const proportional_split& split_obj) 416 : Mode(p, split_obj) 417 , my_delay(begin) 418 , my_max_depth(p.my_max_depth) {} 419 template <typename Task> 420 bool check_being_stolen(Task &t, const execution_data& ed) { // part of old should_execute_range() 421 if( !(self().my_divisor / Mode::my_partition::factor) ) { // if not from the top P tasks of binary tree 422 self().my_divisor = 1; // TODO: replace by on-stack flag (partition_state's member)? 423 if( is_stolen_task(ed) && t.my_parent->m_ref_count >= 2 ) { // runs concurrently with the left task 424 #if __TBB_USE_OPTIONAL_RTTI 425 // RTTI is available, check whether the cast is valid 426 // TODO: TBB_REVAMP_TODO __TBB_ASSERT(dynamic_cast<tree_node*>(t.m_parent), 0); 427 // correctness of the cast relies on avoiding the root task for which: 428 // - initial value of my_divisor != 0 (protected by separate assertion) 429 // - is_stolen_task() always returns false for the root task. 430 #endif 431 tree_node::mark_task_stolen(t); 432 if( !my_max_depth ) my_max_depth++; 433 my_max_depth += __TBB_DEMAND_DEPTH_ADD; 434 return true; 435 } 436 } 437 return false; 438 } 439 depth_t max_depth() { return my_max_depth; } 440 void align_depth(depth_t base) { 441 __TBB_ASSERT(base <= my_max_depth, nullptr); 442 my_max_depth -= base; 443 } 444 template<typename StartType, typename Range> 445 void work_balance(StartType &start, Range &range, execution_data& ed) { 446 if( !range.is_divisible() || !self().max_depth() ) { 447 start.run_body( range ); 448 } 449 else { // do range pool 450 range_vector<Range, range_pool_size> range_pool(range); 451 do { 452 range_pool.split_to_fill(self().max_depth()); // fill range pool 453 if( self().check_for_demand( start ) ) { 454 if( range_pool.size() > 1 ) { 455 start.offer_work( range_pool.front(), range_pool.front_depth(), ed ); 456 range_pool.pop_front(); 457 continue; 458 } 459 if( range_pool.is_divisible(self().max_depth()) ) // was not enough depth to fork a task 460 continue; // note: next split_to_fill() should split range at least once 461 } 462 start.run_body( range_pool.back() ); 463 range_pool.pop_back(); 464 } while( !range_pool.empty() && !ed.context->is_group_execution_cancelled() ); 465 } 466 } 467 template <typename Task> 468 bool check_for_demand(Task& t) { 469 if ( pass == my_delay ) { 470 if ( self().my_divisor > 1 ) // produce affinitized tasks while they have slot in array 471 return true; // do not do my_max_depth++ here, but be sure range_pool is splittable once more 472 else if ( self().my_divisor && my_max_depth ) { // make balancing task 473 self().my_divisor = 0; // once for each task; depth will be decreased in align_depth() 474 return true; 475 } 476 else if ( tree_node::is_peer_stolen(t) ) { 477 my_max_depth += __TBB_DEMAND_DEPTH_ADD; 478 return true; 479 } 480 } else if( begin == my_delay ) { 481 my_delay = pass; 482 } 483 return false; 484 } 485 }; 486 487 class auto_partition_type: public dynamic_grainsize_mode<adaptive_mode<auto_partition_type> > { 488 public: 489 auto_partition_type( const auto_partitioner& ) { 490 my_divisor *= __TBB_INITIAL_CHUNKS; 491 } 492 auto_partition_type( auto_partition_type& src, split) 493 : dynamic_grainsize_mode<adaptive_mode<auto_partition_type> >(src, split()) {} 494 bool is_divisible() { // part of old should_execute_range() 495 if( my_divisor > 1 ) return true; 496 if( my_divisor && my_max_depth ) { // can split the task. TODO: on-stack flag instead 497 // keep same fragmentation while splitting for the local task pool 498 my_max_depth--; 499 my_divisor = 0; // decrease max_depth once per task 500 return true; 501 } else return false; 502 } 503 template <typename Task> 504 bool check_for_demand(Task& t) { 505 if (tree_node::is_peer_stolen(t)) { 506 my_max_depth += __TBB_DEMAND_DEPTH_ADD; 507 return true; 508 } else return false; 509 } 510 void spawn_task(task& t, task_group_context& ctx) { 511 spawn(t, ctx); 512 } 513 }; 514 515 class simple_partition_type: public partition_type_base<simple_partition_type> { 516 public: 517 simple_partition_type( const simple_partitioner& ) {} 518 simple_partition_type( const simple_partition_type&, split ) {} 519 //! simplified algorithm 520 template<typename StartType, typename Range> 521 void execute(StartType &start, Range &range, execution_data& ed) { 522 split_type split_obj = split(); // start.offer_work accepts split_type as reference 523 while( range.is_divisible() ) 524 start.offer_work( split_obj, ed ); 525 start.run_body( range ); 526 } 527 void spawn_task(task& t, task_group_context& ctx) { 528 spawn(t, ctx); 529 } 530 }; 531 532 class static_partition_type : public linear_affinity_mode<static_partition_type> { 533 public: 534 typedef detail::proportional_split split_type; 535 static_partition_type( const static_partitioner& ) {} 536 static_partition_type( static_partition_type& p, const proportional_split& split_obj ) 537 : linear_affinity_mode<static_partition_type>(p, split_obj) {} 538 }; 539 540 class affinity_partition_type : public dynamic_grainsize_mode<linear_affinity_mode<affinity_partition_type> > { 541 static const unsigned factor_power = 4; // TODO: get a unified formula based on number of computing units 542 slot_id* my_array; 543 public: 544 static const unsigned factor = 1 << factor_power; // number of slots in affinity array per task 545 typedef detail::proportional_split split_type; 546 affinity_partition_type( affinity_partitioner_base& ap ) { 547 __TBB_ASSERT( (factor&(factor-1))==0, "factor must be power of two" ); 548 ap.resize(factor); 549 my_array = ap.my_array; 550 my_max_depth = factor_power + 1; 551 __TBB_ASSERT( my_max_depth < __TBB_RANGE_POOL_CAPACITY, nullptr ); 552 } 553 affinity_partition_type(affinity_partition_type& p, split) 554 : dynamic_grainsize_mode<linear_affinity_mode<affinity_partition_type> >(p, split()) 555 , my_array(p.my_array) {} 556 affinity_partition_type(affinity_partition_type& p, const proportional_split& split_obj) 557 : dynamic_grainsize_mode<linear_affinity_mode<affinity_partition_type> >(p, split_obj) 558 , my_array(p.my_array) {} 559 void note_affinity(slot_id id) { 560 if( my_divisor ) 561 my_array[my_head] = id; 562 } 563 void spawn_task(task& t, task_group_context& ctx) { 564 if (my_divisor) { 565 if (!my_array[my_head]) { 566 // TODO: consider new ideas with my_array for both affinity and static partitioner's, then code reuse 567 spawn(t, ctx, slot_id(my_head / factor)); 568 } else { 569 spawn(t, ctx, my_array[my_head]); 570 } 571 } else { 572 spawn(t, ctx); 573 } 574 } 575 }; 576 577 //! A simple partitioner 578 /** Divides the range until the range is not divisible. 579 @ingroup algorithms */ 580 class simple_partitioner { 581 public: 582 simple_partitioner() {} 583 private: 584 template<typename Range, typename Body, typename Partitioner> friend struct start_for; 585 template<typename Range, typename Body, typename Partitioner> friend struct start_reduce; 586 template<typename Range, typename Body, typename Partitioner> friend struct start_deterministic_reduce; 587 template<typename Range, typename Body, typename Partitioner> friend struct start_scan; 588 // new implementation just extends existing interface 589 typedef simple_partition_type task_partition_type; 590 // TODO: consider to make split_type public 591 typedef simple_partition_type::split_type split_type; 592 593 // for parallel_scan only 594 class partition_type { 595 public: 596 bool should_execute_range(const execution_data& ) {return false;} 597 partition_type( const simple_partitioner& ) {} 598 partition_type( const partition_type&, split ) {} 599 }; 600 }; 601 602 //! An auto partitioner 603 /** The range is initial divided into several large chunks. 604 Chunks are further subdivided into smaller pieces if demand detected and they are divisible. 605 @ingroup algorithms */ 606 class auto_partitioner { 607 public: 608 auto_partitioner() {} 609 610 private: 611 template<typename Range, typename Body, typename Partitioner> friend struct start_for; 612 template<typename Range, typename Body, typename Partitioner> friend struct start_reduce; 613 template<typename Range, typename Body, typename Partitioner> friend struct start_deterministic_reduce; 614 template<typename Range, typename Body, typename Partitioner> friend struct start_scan; 615 // new implementation just extends existing interface 616 typedef auto_partition_type task_partition_type; 617 // TODO: consider to make split_type public 618 typedef auto_partition_type::split_type split_type; 619 620 //! Backward-compatible partition for auto and affinity partition objects. 621 class partition_type { 622 size_t num_chunks; 623 static const size_t VICTIM_CHUNKS = 4; 624 public: 625 bool should_execute_range(const execution_data& ed) { 626 if( num_chunks<VICTIM_CHUNKS && is_stolen_task(ed) ) 627 num_chunks = VICTIM_CHUNKS; 628 return num_chunks==1; 629 } 630 partition_type( const auto_partitioner& ) 631 : num_chunks(get_initial_auto_partitioner_divisor()*__TBB_INITIAL_CHUNKS/4) {} 632 partition_type( partition_type& pt, split ) { 633 num_chunks = pt.num_chunks = (pt.num_chunks+1u) / 2u; 634 } 635 }; 636 }; 637 638 //! A static partitioner 639 class static_partitioner { 640 public: 641 static_partitioner() {} 642 private: 643 template<typename Range, typename Body, typename Partitioner> friend struct start_for; 644 template<typename Range, typename Body, typename Partitioner> friend struct start_reduce; 645 template<typename Range, typename Body, typename Partitioner> friend struct start_deterministic_reduce; 646 template<typename Range, typename Body, typename Partitioner> friend struct start_scan; 647 // new implementation just extends existing interface 648 typedef static_partition_type task_partition_type; 649 // TODO: consider to make split_type public 650 typedef static_partition_type::split_type split_type; 651 }; 652 653 //! An affinity partitioner 654 class affinity_partitioner : affinity_partitioner_base { 655 public: 656 affinity_partitioner() {} 657 658 private: 659 template<typename Range, typename Body, typename Partitioner> friend struct start_for; 660 template<typename Range, typename Body, typename Partitioner> friend struct start_reduce; 661 template<typename Range, typename Body, typename Partitioner> friend struct start_deterministic_reduce; 662 template<typename Range, typename Body, typename Partitioner> friend struct start_scan; 663 // new implementation just extends existing interface 664 typedef affinity_partition_type task_partition_type; 665 // TODO: consider to make split_type public 666 typedef affinity_partition_type::split_type split_type; 667 }; 668 669 } // namespace d1 670 } // namespace detail 671 672 inline namespace v1 { 673 // Partitioners 674 using detail::d1::auto_partitioner; 675 using detail::d1::simple_partitioner; 676 using detail::d1::static_partitioner; 677 using detail::d1::affinity_partitioner; 678 // Split types 679 using detail::split; 680 using detail::proportional_split; 681 } // namespace v1 682 683 } // namespace tbb 684 685 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) 686 #pragma warning (pop) 687 #endif // warning 4244 is back 688 689 #undef __TBB_INITIAL_CHUNKS 690 #undef __TBB_RANGE_POOL_CAPACITY 691 #undef __TBB_INIT_DEPTH 692 693 #endif /* __TBB_partitioner_H */ 694