1*49e08aacStbbdev /* 2*49e08aacStbbdev Copyright (c) 2005-2020 Intel Corporation 3*49e08aacStbbdev 4*49e08aacStbbdev Licensed under the Apache License, Version 2.0 (the "License"); 5*49e08aacStbbdev you may not use this file except in compliance with the License. 6*49e08aacStbbdev You may obtain a copy of the License at 7*49e08aacStbbdev 8*49e08aacStbbdev http://www.apache.org/licenses/LICENSE-2.0 9*49e08aacStbbdev 10*49e08aacStbbdev Unless required by applicable law or agreed to in writing, software 11*49e08aacStbbdev distributed under the License is distributed on an "AS IS" BASIS, 12*49e08aacStbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*49e08aacStbbdev See the License for the specific language governing permissions and 14*49e08aacStbbdev limitations under the License. 15*49e08aacStbbdev */ 16*49e08aacStbbdev 17*49e08aacStbbdev #ifndef __TBB_parallel_for_each_H 18*49e08aacStbbdev #define __TBB_parallel_for_each_H 19*49e08aacStbbdev 20*49e08aacStbbdev #include "detail/_config.h" 21*49e08aacStbbdev #include "detail/_namespace_injection.h" 22*49e08aacStbbdev #include "detail/_exception.h" 23*49e08aacStbbdev #include "detail/_task.h" 24*49e08aacStbbdev #include "detail/_aligned_space.h" 25*49e08aacStbbdev #include "detail/_small_object_pool.h" 26*49e08aacStbbdev 27*49e08aacStbbdev #include "parallel_for.h" 28*49e08aacStbbdev #include "task_group.h" // task_group_context 29*49e08aacStbbdev 30*49e08aacStbbdev #include <iterator> 31*49e08aacStbbdev #include <type_traits> 32*49e08aacStbbdev 33*49e08aacStbbdev namespace tbb { 34*49e08aacStbbdev namespace detail { 35*49e08aacStbbdev namespace d1 { 36*49e08aacStbbdev 37*49e08aacStbbdev template<typename Body, typename Item> class feeder_impl; 38*49e08aacStbbdev 39*49e08aacStbbdev //! Class the user supplied algorithm body uses to add new tasks 40*49e08aacStbbdev template<typename Item> 41*49e08aacStbbdev class feeder { 42*49e08aacStbbdev feeder() {} 43*49e08aacStbbdev feeder(const feeder&) = delete; 44*49e08aacStbbdev void operator=( const feeder&) = delete; 45*49e08aacStbbdev 46*49e08aacStbbdev virtual ~feeder () {} 47*49e08aacStbbdev virtual void internal_add_copy(const Item& item) = 0; 48*49e08aacStbbdev virtual void internal_add_move(Item&& item) = 0; 49*49e08aacStbbdev 50*49e08aacStbbdev template<typename Body_, typename Item_> friend class detail::d1::feeder_impl; 51*49e08aacStbbdev public: 52*49e08aacStbbdev //! Add a work item to a running parallel_for_each. 53*49e08aacStbbdev void add(const Item& item) {internal_add_copy(item);} 54*49e08aacStbbdev void add(Item&& item) {internal_add_move(std::move(item));} 55*49e08aacStbbdev }; 56*49e08aacStbbdev 57*49e08aacStbbdev /** Selects one of the two possible forms of function call member operator. 58*49e08aacStbbdev @ingroup algorithms **/ 59*49e08aacStbbdev template<class Body, typename Item> 60*49e08aacStbbdev class parallel_for_each_operator_selector { 61*49e08aacStbbdev public: 62*49e08aacStbbdev //! Hack for resolve ambiguity between calls that may be forwarded or just passed by value 63*49e08aacStbbdev //! in this case overload with forward declaration must have higher priority. 64*49e08aacStbbdev using first_priority = int; 65*49e08aacStbbdev using second_priority = double; 66*49e08aacStbbdev 67*49e08aacStbbdev template<typename ItemArg, typename FeederArg> 68*49e08aacStbbdev static auto select_call (const Body& body, ItemArg&& item, FeederArg&, first_priority) 69*49e08aacStbbdev -> decltype(body(std::forward<ItemArg>(item)), void()) { 70*49e08aacStbbdev #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) 71*49e08aacStbbdev // Suppression of Microsoft non-standard extension warnings 72*49e08aacStbbdev #pragma warning (push) 73*49e08aacStbbdev #pragma warning (disable: 4239) 74*49e08aacStbbdev #endif 75*49e08aacStbbdev 76*49e08aacStbbdev body(std::forward<ItemArg>(item)); 77*49e08aacStbbdev 78*49e08aacStbbdev #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) 79*49e08aacStbbdev #pragma warning (push) 80*49e08aacStbbdev #endif 81*49e08aacStbbdev } 82*49e08aacStbbdev 83*49e08aacStbbdev template<typename ItemArg, typename FeederArg> 84*49e08aacStbbdev static auto select_call (const Body& body, ItemArg&& item, FeederArg& feeder, first_priority) 85*49e08aacStbbdev -> decltype(body(std::forward<ItemArg>(item), feeder), void()) { 86*49e08aacStbbdev #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) 87*49e08aacStbbdev // Suppression of Microsoft non-standard extension warnings 88*49e08aacStbbdev #pragma warning (push) 89*49e08aacStbbdev #pragma warning (disable: 4239) 90*49e08aacStbbdev #endif 91*49e08aacStbbdev 92*49e08aacStbbdev body(std::forward<ItemArg>(item), feeder); 93*49e08aacStbbdev 94*49e08aacStbbdev #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) 95*49e08aacStbbdev #pragma warning (push) 96*49e08aacStbbdev #endif 97*49e08aacStbbdev } 98*49e08aacStbbdev 99*49e08aacStbbdev template<typename ItemArg, typename FeederArg> 100*49e08aacStbbdev static auto select_call (const Body& body, ItemArg&& item, FeederArg&, second_priority) 101*49e08aacStbbdev -> decltype(body(item), void()) { 102*49e08aacStbbdev body(item); 103*49e08aacStbbdev } 104*49e08aacStbbdev 105*49e08aacStbbdev template<typename ItemArg, typename FeederArg> 106*49e08aacStbbdev static auto select_call (const Body& body, ItemArg&& item, FeederArg& feeder, second_priority) 107*49e08aacStbbdev -> decltype(body(item, feeder), void()){ 108*49e08aacStbbdev body(item, feeder); 109*49e08aacStbbdev } 110*49e08aacStbbdev public: 111*49e08aacStbbdev template<typename ItemArg, typename FeederArg> 112*49e08aacStbbdev static void call(const Body& body, ItemArg&& item, FeederArg& feeder) { 113*49e08aacStbbdev select_call(body, std::forward<ItemArg>(item), feeder, first_priority{}); 114*49e08aacStbbdev } 115*49e08aacStbbdev }; 116*49e08aacStbbdev 117*49e08aacStbbdev template<typename Body, typename Item> 118*49e08aacStbbdev struct feeder_item_task: public task { 119*49e08aacStbbdev using feeder_type = feeder_impl<Body, Item>; 120*49e08aacStbbdev 121*49e08aacStbbdev template <typename ItemType> 122*49e08aacStbbdev feeder_item_task(ItemType&& input_item, feeder_type& feeder, small_object_allocator& alloc) : 123*49e08aacStbbdev item(std::forward<ItemType>(input_item)), 124*49e08aacStbbdev my_feeder(feeder), 125*49e08aacStbbdev my_allocator(alloc) 126*49e08aacStbbdev {} 127*49e08aacStbbdev 128*49e08aacStbbdev void finalize(const execution_data& ed) { 129*49e08aacStbbdev my_feeder.my_wait_context.release(); 130*49e08aacStbbdev my_allocator.delete_object(this, ed); 131*49e08aacStbbdev } 132*49e08aacStbbdev 133*49e08aacStbbdev task* execute(execution_data& ed) override { 134*49e08aacStbbdev parallel_for_each_operator_selector<Body, Item>::call(my_feeder.my_body, std::move(item), my_feeder); 135*49e08aacStbbdev finalize(ed); 136*49e08aacStbbdev return nullptr; 137*49e08aacStbbdev } 138*49e08aacStbbdev 139*49e08aacStbbdev task* cancel(execution_data& ed) override { 140*49e08aacStbbdev finalize(ed); 141*49e08aacStbbdev return nullptr; 142*49e08aacStbbdev } 143*49e08aacStbbdev 144*49e08aacStbbdev Item item; 145*49e08aacStbbdev feeder_type& my_feeder; 146*49e08aacStbbdev small_object_allocator my_allocator; 147*49e08aacStbbdev }; // class feeder_item_task 148*49e08aacStbbdev 149*49e08aacStbbdev /** Implements new task adding procedure. 150*49e08aacStbbdev @ingroup algorithms **/ 151*49e08aacStbbdev template<class Body, typename Item> 152*49e08aacStbbdev class feeder_impl : public feeder<Item> { 153*49e08aacStbbdev // Avoiding use of copy constructor in a virtual method if the type does not support it 154*49e08aacStbbdev void internal_add_copy_impl(std::true_type, const Item& item) { 155*49e08aacStbbdev using feeder_task = feeder_item_task<Body, Item>; 156*49e08aacStbbdev small_object_allocator alloc; 157*49e08aacStbbdev auto task = alloc.new_object<feeder_task>(item, *this, alloc); 158*49e08aacStbbdev 159*49e08aacStbbdev my_wait_context.reserve(); 160*49e08aacStbbdev spawn(*task, my_execution_context); 161*49e08aacStbbdev } 162*49e08aacStbbdev 163*49e08aacStbbdev void internal_add_copy_impl(std::false_type, const Item&) { 164*49e08aacStbbdev __TBB_ASSERT(false, "Overloading for r-value reference doesn't work or it's not movable and not copyable object"); 165*49e08aacStbbdev } 166*49e08aacStbbdev 167*49e08aacStbbdev void internal_add_copy(const Item& item) override { 168*49e08aacStbbdev internal_add_copy_impl(typename std::is_copy_constructible<Item>::type(), item); 169*49e08aacStbbdev } 170*49e08aacStbbdev 171*49e08aacStbbdev void internal_add_move(Item&& item) override { 172*49e08aacStbbdev using feeder_task = feeder_item_task<Body, Item>; 173*49e08aacStbbdev small_object_allocator alloc{}; 174*49e08aacStbbdev auto task = alloc.new_object<feeder_task>(std::move(item), *this, alloc); 175*49e08aacStbbdev 176*49e08aacStbbdev my_wait_context.reserve(); 177*49e08aacStbbdev spawn(*task, my_execution_context); 178*49e08aacStbbdev } 179*49e08aacStbbdev public: 180*49e08aacStbbdev feeder_impl(task_group_context &context, const Body& body) 181*49e08aacStbbdev : my_wait_context(0) 182*49e08aacStbbdev , my_body(body) 183*49e08aacStbbdev , my_execution_context(context) 184*49e08aacStbbdev {} 185*49e08aacStbbdev 186*49e08aacStbbdev wait_context my_wait_context; 187*49e08aacStbbdev const Body& my_body; 188*49e08aacStbbdev task_group_context& my_execution_context; 189*49e08aacStbbdev }; // class feeder_impl 190*49e08aacStbbdev 191*49e08aacStbbdev /** Execute computation under one element of the range 192*49e08aacStbbdev @ingroup algorithms **/ 193*49e08aacStbbdev template<typename Iterator, typename Body, typename Item> 194*49e08aacStbbdev struct for_each_iteration_task: public task { 195*49e08aacStbbdev using feeder_type = feeder_impl<Body, Item>; 196*49e08aacStbbdev 197*49e08aacStbbdev for_each_iteration_task(Iterator input_item_ptr, feeder_type& feeder, wait_context& wait_context) : 198*49e08aacStbbdev item_ptr(input_item_ptr), my_feeder(feeder), parent_wait_context(wait_context) 199*49e08aacStbbdev {} 200*49e08aacStbbdev 201*49e08aacStbbdev void finalize() { 202*49e08aacStbbdev parent_wait_context.release(); 203*49e08aacStbbdev } 204*49e08aacStbbdev 205*49e08aacStbbdev task* execute(execution_data&) override { 206*49e08aacStbbdev parallel_for_each_operator_selector<Body, Item>::call(my_feeder.my_body, std::move(*item_ptr), my_feeder); 207*49e08aacStbbdev finalize(); 208*49e08aacStbbdev return nullptr; 209*49e08aacStbbdev } 210*49e08aacStbbdev 211*49e08aacStbbdev task* cancel(execution_data&) override { 212*49e08aacStbbdev finalize(); 213*49e08aacStbbdev return nullptr; 214*49e08aacStbbdev } 215*49e08aacStbbdev 216*49e08aacStbbdev Iterator item_ptr; 217*49e08aacStbbdev feeder_type& my_feeder; 218*49e08aacStbbdev wait_context& parent_wait_context; 219*49e08aacStbbdev }; // class do_iteration_task_iter 220*49e08aacStbbdev 221*49e08aacStbbdev /** Split one block task to several(max_block_size) iteration tasks for input iterators 222*49e08aacStbbdev @ingroup algorithms **/ 223*49e08aacStbbdev template <typename Body, typename Item> 224*49e08aacStbbdev struct input_block_handling_task : public task { 225*49e08aacStbbdev static constexpr size_t max_block_size = 4; 226*49e08aacStbbdev 227*49e08aacStbbdev using feeder_type = feeder_impl<Body, Item>; 228*49e08aacStbbdev using iteration_task = for_each_iteration_task<Item*, Body, Item>; 229*49e08aacStbbdev 230*49e08aacStbbdev input_block_handling_task(feeder_type& feeder, small_object_allocator& alloc) 231*49e08aacStbbdev :my_size(0), my_wait_context(0), my_feeder(feeder), my_allocator(alloc) 232*49e08aacStbbdev { 233*49e08aacStbbdev auto* item_it = block_iteration_space.begin(); 234*49e08aacStbbdev for (auto* it = task_pool.begin(); it != task_pool.end(); ++it) { 235*49e08aacStbbdev new (it) iteration_task(item_it++, feeder, my_wait_context); 236*49e08aacStbbdev } 237*49e08aacStbbdev } 238*49e08aacStbbdev 239*49e08aacStbbdev void finalize(const execution_data& ed) { 240*49e08aacStbbdev my_feeder.my_wait_context.release(); 241*49e08aacStbbdev my_allocator.delete_object(this, ed); 242*49e08aacStbbdev } 243*49e08aacStbbdev 244*49e08aacStbbdev task* execute(execution_data& ed) override { 245*49e08aacStbbdev __TBB_ASSERT( my_size > 0, "Negative size was passed to task"); 246*49e08aacStbbdev for (std::size_t counter = 1; counter < my_size; ++counter) { 247*49e08aacStbbdev my_wait_context.reserve(); 248*49e08aacStbbdev spawn(*(task_pool.begin() + counter), my_feeder.my_execution_context); 249*49e08aacStbbdev } 250*49e08aacStbbdev my_wait_context.reserve(); 251*49e08aacStbbdev execute_and_wait(*task_pool.begin(), my_feeder.my_execution_context, 252*49e08aacStbbdev my_wait_context, my_feeder.my_execution_context); 253*49e08aacStbbdev 254*49e08aacStbbdev // deallocate current task after children execution 255*49e08aacStbbdev finalize(ed); 256*49e08aacStbbdev return nullptr; 257*49e08aacStbbdev } 258*49e08aacStbbdev 259*49e08aacStbbdev task* cancel(execution_data& ed) override { 260*49e08aacStbbdev finalize(ed); 261*49e08aacStbbdev return nullptr; 262*49e08aacStbbdev } 263*49e08aacStbbdev 264*49e08aacStbbdev ~input_block_handling_task() { 265*49e08aacStbbdev for(std::size_t counter = 0; counter < max_block_size; ++counter) { 266*49e08aacStbbdev (task_pool.begin() + counter)->~iteration_task(); 267*49e08aacStbbdev (block_iteration_space.begin() + counter)->~Item(); 268*49e08aacStbbdev } 269*49e08aacStbbdev } 270*49e08aacStbbdev 271*49e08aacStbbdev aligned_space<Item, max_block_size> block_iteration_space; 272*49e08aacStbbdev aligned_space<iteration_task, max_block_size> task_pool; 273*49e08aacStbbdev std::size_t my_size; 274*49e08aacStbbdev wait_context my_wait_context; 275*49e08aacStbbdev feeder_type& my_feeder; 276*49e08aacStbbdev small_object_allocator my_allocator; 277*49e08aacStbbdev }; // class input_block_execution_task 278*49e08aacStbbdev 279*49e08aacStbbdev /** Split one block task to several(max_block_size) iteration tasks for forward iterators 280*49e08aacStbbdev @ingroup algorithms **/ 281*49e08aacStbbdev template <typename Iterator, typename Body, typename Item> 282*49e08aacStbbdev struct forward_block_handling_task : public task { 283*49e08aacStbbdev static constexpr size_t max_block_size = 4; 284*49e08aacStbbdev 285*49e08aacStbbdev using feeder_type = feeder_impl<Body, Item>; 286*49e08aacStbbdev using iteration_task = for_each_iteration_task<Iterator, Body, Item>; 287*49e08aacStbbdev 288*49e08aacStbbdev forward_block_handling_task(Iterator first, std::size_t size, feeder_type& feeder, small_object_allocator& alloc) 289*49e08aacStbbdev : my_size(size), my_wait_context(0), my_feeder(feeder), my_allocator(alloc) 290*49e08aacStbbdev { 291*49e08aacStbbdev auto* task_it = task_pool.begin(); 292*49e08aacStbbdev for (std::size_t i = 0; i < size; i++) { 293*49e08aacStbbdev new (task_it++) iteration_task(first, feeder, my_wait_context); 294*49e08aacStbbdev ++first; 295*49e08aacStbbdev } 296*49e08aacStbbdev } 297*49e08aacStbbdev 298*49e08aacStbbdev void finalize(const execution_data& ed) { 299*49e08aacStbbdev my_feeder.my_wait_context.release(); 300*49e08aacStbbdev my_allocator.delete_object(this, ed); 301*49e08aacStbbdev } 302*49e08aacStbbdev 303*49e08aacStbbdev task* execute(execution_data& ed) override { 304*49e08aacStbbdev __TBB_ASSERT( my_size > 0, "Negative size was passed to task"); 305*49e08aacStbbdev for(std::size_t counter = 1; counter < my_size; ++counter) { 306*49e08aacStbbdev my_wait_context.reserve(); 307*49e08aacStbbdev spawn(*(task_pool.begin() + counter), my_feeder.my_execution_context); 308*49e08aacStbbdev } 309*49e08aacStbbdev my_wait_context.reserve(); 310*49e08aacStbbdev execute_and_wait(*task_pool.begin(), my_feeder.my_execution_context, 311*49e08aacStbbdev my_wait_context, my_feeder.my_execution_context); 312*49e08aacStbbdev 313*49e08aacStbbdev // deallocate current task after children execution 314*49e08aacStbbdev finalize(ed); 315*49e08aacStbbdev return nullptr; 316*49e08aacStbbdev } 317*49e08aacStbbdev 318*49e08aacStbbdev task* cancel(execution_data& ed) override { 319*49e08aacStbbdev finalize(ed); 320*49e08aacStbbdev return nullptr; 321*49e08aacStbbdev } 322*49e08aacStbbdev 323*49e08aacStbbdev ~forward_block_handling_task() { 324*49e08aacStbbdev for(std::size_t counter = 0; counter < my_size; ++counter) { 325*49e08aacStbbdev (task_pool.begin() + counter)->~iteration_task(); 326*49e08aacStbbdev } 327*49e08aacStbbdev } 328*49e08aacStbbdev 329*49e08aacStbbdev aligned_space<iteration_task, max_block_size> task_pool; 330*49e08aacStbbdev std::size_t my_size; 331*49e08aacStbbdev wait_context my_wait_context; 332*49e08aacStbbdev feeder_type& my_feeder; 333*49e08aacStbbdev small_object_allocator my_allocator; 334*49e08aacStbbdev }; // class forward_block_handling_task 335*49e08aacStbbdev 336*49e08aacStbbdev /** Body for parallel_for algorithm. 337*49e08aacStbbdev * Allows to redirect operations under random access iterators range to the parallel_for algorithm. 338*49e08aacStbbdev @ingroup algorithms **/ 339*49e08aacStbbdev template <typename Iterator, typename Body, typename Item> 340*49e08aacStbbdev class parallel_for_body_wrapper { 341*49e08aacStbbdev using feeder_type = feeder_impl<Body, Item>; 342*49e08aacStbbdev 343*49e08aacStbbdev Iterator my_first; 344*49e08aacStbbdev feeder_type& my_feeder; 345*49e08aacStbbdev public: 346*49e08aacStbbdev parallel_for_body_wrapper(Iterator first, feeder_type& feeder) : my_first(first), my_feeder(feeder) {} 347*49e08aacStbbdev 348*49e08aacStbbdev void operator()(tbb::blocked_range<std::size_t> range) const { 349*49e08aacStbbdev #if __INTEL_COMPILER 350*49e08aacStbbdev #pragma ivdep 351*49e08aacStbbdev #endif 352*49e08aacStbbdev for (std::size_t count = range.begin(); count != range.end(); count++) { 353*49e08aacStbbdev parallel_for_each_operator_selector<Body, Item>::call(my_feeder.my_body, std::move(*(my_first + count)), my_feeder); 354*49e08aacStbbdev } 355*49e08aacStbbdev } 356*49e08aacStbbdev }; // class parallel_for_body_wrapper 357*49e08aacStbbdev 358*49e08aacStbbdev 359*49e08aacStbbdev /** Helper for getting iterators tag including inherited custom tags 360*49e08aacStbbdev @ingroup algorithms */ 361*49e08aacStbbdev template<typename It> 362*49e08aacStbbdev using tag = typename std::iterator_traits<It>::iterator_category; 363*49e08aacStbbdev 364*49e08aacStbbdev template<typename It> 365*49e08aacStbbdev using iterator_tag_dispatch = typename 366*49e08aacStbbdev std::conditional< 367*49e08aacStbbdev std::is_base_of<std::random_access_iterator_tag, tag<It>>::value, 368*49e08aacStbbdev std::random_access_iterator_tag, 369*49e08aacStbbdev typename std::conditional< 370*49e08aacStbbdev std::is_base_of<std::forward_iterator_tag, tag<It>>::value, 371*49e08aacStbbdev std::forward_iterator_tag, 372*49e08aacStbbdev std::input_iterator_tag 373*49e08aacStbbdev >::type 374*49e08aacStbbdev >::type; 375*49e08aacStbbdev 376*49e08aacStbbdev /** parallel_for_each algorithm root task - most generic version 377*49e08aacStbbdev * Splits input range to blocks 378*49e08aacStbbdev @ingroup algorithms **/ 379*49e08aacStbbdev template <typename Iterator, typename Body, typename Item, typename IteratorTag = iterator_tag_dispatch<Iterator>> 380*49e08aacStbbdev class for_each_root_task : public task { 381*49e08aacStbbdev using feeder_type = feeder_impl<Body, Item>; 382*49e08aacStbbdev 383*49e08aacStbbdev Iterator my_first; 384*49e08aacStbbdev Iterator my_last; 385*49e08aacStbbdev feeder_type& my_feeder; 386*49e08aacStbbdev public: 387*49e08aacStbbdev for_each_root_task(Iterator first, Iterator last, feeder_type& feeder) : 388*49e08aacStbbdev my_first(first), my_last(last), my_feeder(feeder) 389*49e08aacStbbdev { 390*49e08aacStbbdev my_feeder.my_wait_context.reserve(); 391*49e08aacStbbdev } 392*49e08aacStbbdev private: 393*49e08aacStbbdev task* execute(execution_data& ed) override { 394*49e08aacStbbdev using block_handling_type = input_block_handling_task<Body, Item>; 395*49e08aacStbbdev 396*49e08aacStbbdev if (my_first == my_last) { 397*49e08aacStbbdev my_feeder.my_wait_context.release(); 398*49e08aacStbbdev return nullptr; 399*49e08aacStbbdev } 400*49e08aacStbbdev 401*49e08aacStbbdev my_feeder.my_wait_context.reserve(); 402*49e08aacStbbdev small_object_allocator alloc{}; 403*49e08aacStbbdev auto block_handling_task = alloc.new_object<block_handling_type>(ed, my_feeder, alloc); 404*49e08aacStbbdev 405*49e08aacStbbdev auto* block_iterator = block_handling_task->block_iteration_space.begin(); 406*49e08aacStbbdev for (; !(my_first == my_last) && block_handling_task->my_size < block_handling_type::max_block_size; ++my_first) { 407*49e08aacStbbdev // Move semantics are automatically used when supported by the iterator 408*49e08aacStbbdev new (block_iterator++) Item(*my_first); 409*49e08aacStbbdev ++block_handling_task->my_size; 410*49e08aacStbbdev } 411*49e08aacStbbdev 412*49e08aacStbbdev // Do not access this after spawn to avoid races 413*49e08aacStbbdev spawn(*this, my_feeder.my_execution_context); 414*49e08aacStbbdev return block_handling_task; 415*49e08aacStbbdev } 416*49e08aacStbbdev 417*49e08aacStbbdev task* cancel(execution_data&) override { 418*49e08aacStbbdev my_feeder.my_wait_context.release(); 419*49e08aacStbbdev return nullptr; 420*49e08aacStbbdev } 421*49e08aacStbbdev }; // class for_each_root_task - most generic implementation 422*49e08aacStbbdev 423*49e08aacStbbdev /** parallel_for_each algorithm root task - forward iterator based specialization 424*49e08aacStbbdev * Splits input range to blocks 425*49e08aacStbbdev @ingroup algorithms **/ 426*49e08aacStbbdev template <typename Iterator, typename Body, typename Item> 427*49e08aacStbbdev class for_each_root_task<Iterator, Body, Item, std::forward_iterator_tag> : public task { 428*49e08aacStbbdev using feeder_type = feeder_impl<Body, Item>; 429*49e08aacStbbdev 430*49e08aacStbbdev Iterator my_first; 431*49e08aacStbbdev Iterator my_last; 432*49e08aacStbbdev feeder_type& my_feeder; 433*49e08aacStbbdev public: 434*49e08aacStbbdev for_each_root_task(Iterator first, Iterator last, feeder_type& feeder) : 435*49e08aacStbbdev my_first(first), my_last(last), my_feeder(feeder) 436*49e08aacStbbdev { 437*49e08aacStbbdev my_feeder.my_wait_context.reserve(); 438*49e08aacStbbdev } 439*49e08aacStbbdev private: 440*49e08aacStbbdev task* execute(execution_data& ed) override { 441*49e08aacStbbdev using block_handling_type = forward_block_handling_task<Iterator, Body, Item>; 442*49e08aacStbbdev if (my_first == my_last) { 443*49e08aacStbbdev my_feeder.my_wait_context.release(); 444*49e08aacStbbdev return nullptr; 445*49e08aacStbbdev } 446*49e08aacStbbdev 447*49e08aacStbbdev std::size_t block_size{0}; 448*49e08aacStbbdev Iterator first_block_element = my_first; 449*49e08aacStbbdev for (; !(my_first == my_last) && block_size < block_handling_type::max_block_size; ++my_first) { 450*49e08aacStbbdev ++block_size; 451*49e08aacStbbdev } 452*49e08aacStbbdev 453*49e08aacStbbdev my_feeder.my_wait_context.reserve(); 454*49e08aacStbbdev small_object_allocator alloc{}; 455*49e08aacStbbdev auto block_handling_task = alloc.new_object<block_handling_type>(ed, first_block_element, block_size, my_feeder, alloc); 456*49e08aacStbbdev 457*49e08aacStbbdev // Do not access this after spawn to avoid races 458*49e08aacStbbdev spawn(*this, my_feeder.my_execution_context); 459*49e08aacStbbdev return block_handling_task; 460*49e08aacStbbdev } 461*49e08aacStbbdev 462*49e08aacStbbdev task* cancel(execution_data&) override { 463*49e08aacStbbdev my_feeder.my_wait_context.release(); 464*49e08aacStbbdev return nullptr; 465*49e08aacStbbdev } 466*49e08aacStbbdev }; // class for_each_root_task - forward iterator based specialization 467*49e08aacStbbdev 468*49e08aacStbbdev 469*49e08aacStbbdev /** parallel_for_each algorithm root task - random access iterator based specialization 470*49e08aacStbbdev * Splits input range to blocks 471*49e08aacStbbdev @ingroup algorithms **/ 472*49e08aacStbbdev template <typename Iterator, typename Body, typename Item> 473*49e08aacStbbdev class for_each_root_task<Iterator, Body, Item, std::random_access_iterator_tag> : public task { 474*49e08aacStbbdev using feeder_type = feeder_impl<Body, Item>; 475*49e08aacStbbdev 476*49e08aacStbbdev Iterator my_first; 477*49e08aacStbbdev Iterator my_last; 478*49e08aacStbbdev feeder_type& my_feeder; 479*49e08aacStbbdev public: 480*49e08aacStbbdev for_each_root_task(Iterator first, Iterator last, feeder_type& feeder) : 481*49e08aacStbbdev my_first(first), my_last(last), my_feeder(feeder) 482*49e08aacStbbdev { 483*49e08aacStbbdev my_feeder.my_wait_context.reserve(); 484*49e08aacStbbdev } 485*49e08aacStbbdev private: 486*49e08aacStbbdev task* execute(execution_data&) override { 487*49e08aacStbbdev tbb::parallel_for( 488*49e08aacStbbdev tbb::blocked_range<std::size_t>(0, std::distance(my_first, my_last)), 489*49e08aacStbbdev parallel_for_body_wrapper<Iterator, Body, Item>(my_first, my_feeder) 490*49e08aacStbbdev , my_feeder.my_execution_context 491*49e08aacStbbdev ); 492*49e08aacStbbdev 493*49e08aacStbbdev my_feeder.my_wait_context.release(); 494*49e08aacStbbdev return nullptr; 495*49e08aacStbbdev } 496*49e08aacStbbdev 497*49e08aacStbbdev task* cancel(execution_data&) override { 498*49e08aacStbbdev my_feeder.my_wait_context.release(); 499*49e08aacStbbdev return nullptr; 500*49e08aacStbbdev } 501*49e08aacStbbdev }; // class for_each_root_task - random access iterator based specialization 502*49e08aacStbbdev 503*49e08aacStbbdev /** Helper for getting item type. If item type can be deduced from feeder - got it from feeder, 504*49e08aacStbbdev if feeder is generic - got item type from range. 505*49e08aacStbbdev @ingroup algorithms */ 506*49e08aacStbbdev template<typename Body, typename Item, typename FeederArg> 507*49e08aacStbbdev auto feeder_argument_parser(void (Body::*)(Item, feeder<FeederArg>&) const) -> FeederArg; 508*49e08aacStbbdev 509*49e08aacStbbdev template<typename Body, typename> 510*49e08aacStbbdev decltype(feeder_argument_parser<Body>(&Body::operator())) get_item_type_impl(int); // for (T, feeder<T>) 511*49e08aacStbbdev template<typename Body, typename Item> Item get_item_type_impl(...); // stub 512*49e08aacStbbdev 513*49e08aacStbbdev template <typename Body, typename Item> 514*49e08aacStbbdev using get_item_type = decltype(get_item_type_impl<Body, Item>(0)); 515*49e08aacStbbdev 516*49e08aacStbbdev /** Implements parallel iteration over a range. 517*49e08aacStbbdev @ingroup algorithms */ 518*49e08aacStbbdev template<typename Iterator, typename Body> 519*49e08aacStbbdev void run_parallel_for_each( Iterator first, Iterator last, const Body& body, task_group_context& context) 520*49e08aacStbbdev { 521*49e08aacStbbdev if (!(first == last)) { 522*49e08aacStbbdev using ItemType = get_item_type<Body, typename std::decay<decltype(*first)>::type>; 523*49e08aacStbbdev feeder_impl<Body, ItemType> feeder(context, body); 524*49e08aacStbbdev 525*49e08aacStbbdev for_each_root_task<Iterator, Body, ItemType> root_task(first, last, feeder); 526*49e08aacStbbdev 527*49e08aacStbbdev execute_and_wait(root_task, context, feeder.my_wait_context, context); 528*49e08aacStbbdev } 529*49e08aacStbbdev } 530*49e08aacStbbdev 531*49e08aacStbbdev /** \page parallel_for_each_body_req Requirements on parallel_for_each body 532*49e08aacStbbdev Class \c Body implementing the concept of parallel_for_each body must define: 533*49e08aacStbbdev - \code 534*49e08aacStbbdev B::operator()( 535*49e08aacStbbdev cv_item_type item, 536*49e08aacStbbdev feeder<item_type>& feeder 537*49e08aacStbbdev ) const 538*49e08aacStbbdev 539*49e08aacStbbdev OR 540*49e08aacStbbdev 541*49e08aacStbbdev B::operator()( cv_item_type& item ) const 542*49e08aacStbbdev \endcode Process item. 543*49e08aacStbbdev May be invoked concurrently for the same \c this but different \c item. 544*49e08aacStbbdev 545*49e08aacStbbdev - \code item_type( const item_type& ) \endcode 546*49e08aacStbbdev Copy a work item. 547*49e08aacStbbdev - \code ~item_type() \endcode Destroy a work item 548*49e08aacStbbdev **/ 549*49e08aacStbbdev 550*49e08aacStbbdev /** \name parallel_for_each 551*49e08aacStbbdev See also requirements on \ref parallel_for_each_body_req "parallel_for_each Body". **/ 552*49e08aacStbbdev //@{ 553*49e08aacStbbdev //! Parallel iteration over a range, with optional addition of more work. 554*49e08aacStbbdev /** @ingroup algorithms */ 555*49e08aacStbbdev template<typename Iterator, typename Body> 556*49e08aacStbbdev void parallel_for_each(Iterator first, Iterator last, const Body& body) { 557*49e08aacStbbdev task_group_context context(PARALLEL_FOR_EACH); 558*49e08aacStbbdev run_parallel_for_each<Iterator, Body>(first, last, body, context); 559*49e08aacStbbdev } 560*49e08aacStbbdev 561*49e08aacStbbdev template<typename Range, typename Body> 562*49e08aacStbbdev void parallel_for_each(Range& rng, const Body& body) { 563*49e08aacStbbdev parallel_for_each(std::begin(rng), std::end(rng), body); 564*49e08aacStbbdev } 565*49e08aacStbbdev 566*49e08aacStbbdev template<typename Range, typename Body> 567*49e08aacStbbdev void parallel_for_each(const Range& rng, const Body& body) { 568*49e08aacStbbdev parallel_for_each(std::begin(rng), std::end(rng), body); 569*49e08aacStbbdev } 570*49e08aacStbbdev 571*49e08aacStbbdev //! Parallel iteration over a range, with optional addition of more work and user-supplied context 572*49e08aacStbbdev /** @ingroup algorithms */ 573*49e08aacStbbdev template<typename Iterator, typename Body> 574*49e08aacStbbdev void parallel_for_each(Iterator first, Iterator last, const Body& body, task_group_context& context) { 575*49e08aacStbbdev run_parallel_for_each<Iterator, Body>(first, last, body, context); 576*49e08aacStbbdev } 577*49e08aacStbbdev 578*49e08aacStbbdev template<typename Range, typename Body> 579*49e08aacStbbdev void parallel_for_each(Range& rng, const Body& body, task_group_context& context) { 580*49e08aacStbbdev parallel_for_each(std::begin(rng), std::end(rng), body, context); 581*49e08aacStbbdev } 582*49e08aacStbbdev 583*49e08aacStbbdev template<typename Range, typename Body> 584*49e08aacStbbdev void parallel_for_each(const Range& rng, const Body& body, task_group_context& context) { 585*49e08aacStbbdev parallel_for_each(std::begin(rng), std::end(rng), body, context); 586*49e08aacStbbdev } 587*49e08aacStbbdev 588*49e08aacStbbdev } // namespace d1 589*49e08aacStbbdev } // namespace detail 590*49e08aacStbbdev //! @endcond 591*49e08aacStbbdev //@} 592*49e08aacStbbdev 593*49e08aacStbbdev inline namespace v1 { 594*49e08aacStbbdev using detail::d1::parallel_for_each; 595*49e08aacStbbdev using detail::d1::feeder; 596*49e08aacStbbdev } // namespace v1 597*49e08aacStbbdev 598*49e08aacStbbdev } // namespace tbb 599*49e08aacStbbdev 600*49e08aacStbbdev #endif /* __TBB_parallel_for_each_H */ 601