parallel_reduce.h 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. /*
  2. Copyright (c) 2005-2020 Intel Corporation
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. #ifndef __TBB_parallel_reduce_H
  14. #define __TBB_parallel_reduce_H
  15. #define __TBB_parallel_reduce_H_include_area
  16. #include "internal/_warning_suppress_enable_notice.h"
  17. #include <new>
  18. #include "task.h"
  19. #include "aligned_space.h"
  20. #include "partitioner.h"
  21. #include "tbb_profiling.h"
  22. namespace tbb {
  23. namespace interface9 {
  24. //! @cond INTERNAL
  25. namespace internal {
  26. using namespace tbb::internal;
  27. /** Values for reduction_context. */
  28. enum {
  29. root_task, left_child, right_child
  30. };
  31. /** Represented as a char, not enum, for compactness. */
  32. typedef char reduction_context;
  33. //! Task type used to combine the partial results of parallel_reduce.
  34. /** @ingroup algorithms */
  35. template<typename Body>
  36. class finish_reduce: public flag_task {
  37. //! Pointer to body, or NULL if the left child has not yet finished.
  38. bool has_right_zombie;
  39. const reduction_context my_context;
  40. Body* my_body;
  41. aligned_space<Body> zombie_space;
  42. finish_reduce( reduction_context context_ ) :
  43. has_right_zombie(false), // TODO: substitute by flag_task::child_stolen?
  44. my_context(context_),
  45. my_body(NULL)
  46. {
  47. }
  48. ~finish_reduce() {
  49. if( has_right_zombie )
  50. zombie_space.begin()->~Body();
  51. }
  52. task* execute() __TBB_override {
  53. if( has_right_zombie ) {
  54. // Right child was stolen.
  55. Body* s = zombie_space.begin();
  56. my_body->join( *s );
  57. // Body::join() won't be called if canceled. Defer destruction to destructor
  58. }
  59. if( my_context==left_child )
  60. itt_store_word_with_release( static_cast<finish_reduce*>(parent())->my_body, my_body );
  61. return NULL;
  62. }
  63. template<typename Range,typename Body_, typename Partitioner>
  64. friend class start_reduce;
  65. };
  66. //! allocate right task with new parent
  67. void allocate_sibling(task* start_reduce_task, task *tasks[], size_t start_bytes, size_t finish_bytes);
  68. //! Task type used to split the work of parallel_reduce.
  69. /** @ingroup algorithms */
  70. template<typename Range, typename Body, typename Partitioner>
  71. class start_reduce: public task {
  72. typedef finish_reduce<Body> finish_type;
  73. Body* my_body;
  74. Range my_range;
  75. typename Partitioner::task_partition_type my_partition;
  76. reduction_context my_context;
  77. task* execute() __TBB_override;
  78. //! Update affinity info, if any
  79. void note_affinity( affinity_id id ) __TBB_override {
  80. my_partition.note_affinity( id );
  81. }
  82. template<typename Body_>
  83. friend class finish_reduce;
  84. public:
  85. //! Constructor used for root task
  86. start_reduce( const Range& range, Body* body, Partitioner& partitioner ) :
  87. my_body(body),
  88. my_range(range),
  89. my_partition(partitioner),
  90. my_context(root_task)
  91. {
  92. }
  93. //! Splitting constructor used to generate children.
  94. /** parent_ becomes left child. Newly constructed object is right child. */
  95. start_reduce( start_reduce& parent_, typename Partitioner::split_type& split_obj ) :
  96. my_body(parent_.my_body),
  97. my_range(parent_.my_range, split_obj),
  98. my_partition(parent_.my_partition, split_obj),
  99. my_context(right_child)
  100. {
  101. my_partition.set_affinity(*this);
  102. parent_.my_context = left_child;
  103. }
  104. //! Construct right child from the given range as response to the demand.
  105. /** parent_ remains left child. Newly constructed object is right child. */
  106. start_reduce( start_reduce& parent_, const Range& r, depth_t d ) :
  107. my_body(parent_.my_body),
  108. my_range(r),
  109. my_partition(parent_.my_partition, split()),
  110. my_context(right_child)
  111. {
  112. my_partition.set_affinity(*this);
  113. my_partition.align_depth( d ); // TODO: move into constructor of partitioner
  114. parent_.my_context = left_child;
  115. }
  116. static void run( const Range& range, Body& body, Partitioner& partitioner ) {
  117. if( !range.empty() ) {
  118. #if !__TBB_TASK_GROUP_CONTEXT || TBB_JOIN_OUTER_TASK_GROUP
  119. task::spawn_root_and_wait( *new(task::allocate_root()) start_reduce(range,&body,partitioner) );
  120. #else
  121. // Bound context prevents exceptions from body to affect nesting or sibling algorithms,
  122. // and allows users to handle exceptions safely by wrapping parallel_for in the try-block.
  123. task_group_context context(PARALLEL_REDUCE);
  124. task::spawn_root_and_wait( *new(task::allocate_root(context)) start_reduce(range,&body,partitioner) );
  125. #endif /* __TBB_TASK_GROUP_CONTEXT && !TBB_JOIN_OUTER_TASK_GROUP */
  126. }
  127. }
  128. #if __TBB_TASK_GROUP_CONTEXT
  129. static void run( const Range& range, Body& body, Partitioner& partitioner, task_group_context& context ) {
  130. if( !range.empty() )
  131. task::spawn_root_and_wait( *new(task::allocate_root(context)) start_reduce(range,&body,partitioner) );
  132. }
  133. #endif /* __TBB_TASK_GROUP_CONTEXT */
  134. //! Run body for range
  135. void run_body( Range &r ) { (*my_body)( r ); }
  136. //! spawn right task, serves as callback for partitioner
  137. // TODO: remove code duplication from 'offer_work' methods
  138. void offer_work(typename Partitioner::split_type& split_obj) {
  139. task *tasks[2];
  140. allocate_sibling(static_cast<task*>(this), tasks, sizeof(start_reduce), sizeof(finish_type));
  141. new((void*)tasks[0]) finish_type(my_context);
  142. new((void*)tasks[1]) start_reduce(*this, split_obj);
  143. spawn(*tasks[1]);
  144. }
  145. //! spawn right task, serves as callback for partitioner
  146. void offer_work(const Range& r, depth_t d = 0) {
  147. task *tasks[2];
  148. allocate_sibling(static_cast<task*>(this), tasks, sizeof(start_reduce), sizeof(finish_type));
  149. new((void*)tasks[0]) finish_type(my_context);
  150. new((void*)tasks[1]) start_reduce(*this, r, d);
  151. spawn(*tasks[1]);
  152. }
  153. };
  154. //! allocate right task with new parent
  155. // TODO: 'inline' here is to avoid multiple definition error but for sake of code size this should not be inlined
  156. inline void allocate_sibling(task* start_reduce_task, task *tasks[], size_t start_bytes, size_t finish_bytes) {
  157. tasks[0] = &start_reduce_task->allocate_continuation().allocate(finish_bytes);
  158. start_reduce_task->set_parent(tasks[0]);
  159. tasks[0]->set_ref_count(2);
  160. tasks[1] = &tasks[0]->allocate_child().allocate(start_bytes);
  161. }
  162. template<typename Range, typename Body, typename Partitioner>
  163. task* start_reduce<Range,Body,Partitioner>::execute() {
  164. my_partition.check_being_stolen( *this );
  165. if( my_context==right_child ) {
  166. finish_type* parent_ptr = static_cast<finish_type*>(parent());
  167. if( !itt_load_word_with_acquire(parent_ptr->my_body) ) { // TODO: replace by is_stolen_task() or by parent_ptr->ref_count() == 2???
  168. my_body = new( parent_ptr->zombie_space.begin() ) Body(*my_body,split());
  169. parent_ptr->has_right_zombie = true;
  170. }
  171. } else __TBB_ASSERT(my_context==root_task,NULL);// because left leaf spawns right leafs without recycling
  172. my_partition.execute(*this, my_range);
  173. if( my_context==left_child ) {
  174. finish_type* parent_ptr = static_cast<finish_type*>(parent());
  175. __TBB_ASSERT(my_body!=parent_ptr->zombie_space.begin(),NULL);
  176. itt_store_word_with_release(parent_ptr->my_body, my_body );
  177. }
  178. return NULL;
  179. }
  180. //! Task type used to combine the partial results of parallel_deterministic_reduce.
  181. /** @ingroup algorithms */
  182. template<typename Body>
  183. class finish_deterministic_reduce: public task {
  184. Body &my_left_body;
  185. Body my_right_body;
  186. finish_deterministic_reduce( Body &body ) :
  187. my_left_body( body ),
  188. my_right_body( body, split() )
  189. {
  190. }
  191. task* execute() __TBB_override {
  192. my_left_body.join( my_right_body );
  193. return NULL;
  194. }
  195. template<typename Range,typename Body_, typename Partitioner>
  196. friend class start_deterministic_reduce;
  197. };
  198. //! Task type used to split the work of parallel_deterministic_reduce.
  199. /** @ingroup algorithms */
  200. template<typename Range, typename Body, typename Partitioner>
  201. class start_deterministic_reduce: public task {
  202. typedef finish_deterministic_reduce<Body> finish_type;
  203. Body &my_body;
  204. Range my_range;
  205. typename Partitioner::task_partition_type my_partition;
  206. task* execute() __TBB_override;
  207. //! Constructor used for root task
  208. start_deterministic_reduce( const Range& range, Body& body, Partitioner& partitioner ) :
  209. my_body( body ),
  210. my_range( range ),
  211. my_partition( partitioner )
  212. {
  213. }
  214. //! Splitting constructor used to generate children.
  215. /** parent_ becomes left child. Newly constructed object is right child. */
  216. start_deterministic_reduce( start_deterministic_reduce& parent_, finish_type& c, typename Partitioner::split_type& split_obj ) :
  217. my_body( c.my_right_body ),
  218. my_range( parent_.my_range, split_obj ),
  219. my_partition( parent_.my_partition, split_obj )
  220. {
  221. }
  222. public:
  223. static void run( const Range& range, Body& body, Partitioner& partitioner ) {
  224. if( !range.empty() ) {
  225. #if !__TBB_TASK_GROUP_CONTEXT || TBB_JOIN_OUTER_TASK_GROUP
  226. task::spawn_root_and_wait( *new(task::allocate_root()) start_deterministic_reduce(range,&body,partitioner) );
  227. #else
  228. // Bound context prevents exceptions from body to affect nesting or sibling algorithms,
  229. // and allows users to handle exceptions safely by wrapping parallel_for in the try-block.
  230. task_group_context context(PARALLEL_REDUCE);
  231. task::spawn_root_and_wait( *new(task::allocate_root(context)) start_deterministic_reduce(range,body,partitioner) );
  232. #endif /* __TBB_TASK_GROUP_CONTEXT && !TBB_JOIN_OUTER_TASK_GROUP */
  233. }
  234. }
  235. #if __TBB_TASK_GROUP_CONTEXT
  236. static void run( const Range& range, Body& body, Partitioner& partitioner, task_group_context& context ) {
  237. if( !range.empty() )
  238. task::spawn_root_and_wait( *new(task::allocate_root(context)) start_deterministic_reduce(range,body,partitioner) );
  239. }
  240. #endif /* __TBB_TASK_GROUP_CONTEXT */
  241. void offer_work( typename Partitioner::split_type& split_obj) {
  242. task* tasks[2];
  243. allocate_sibling(static_cast<task*>(this), tasks, sizeof(start_deterministic_reduce), sizeof(finish_type));
  244. new((void*)tasks[0]) finish_type(my_body);
  245. new((void*)tasks[1]) start_deterministic_reduce(*this, *static_cast<finish_type*>(tasks[0]), split_obj);
  246. spawn(*tasks[1]);
  247. }
  248. void run_body( Range &r ) { my_body(r); }
  249. };
  250. template<typename Range, typename Body, typename Partitioner>
  251. task* start_deterministic_reduce<Range,Body, Partitioner>::execute() {
  252. my_partition.execute(*this, my_range);
  253. return NULL;
  254. }
  255. } // namespace internal
  256. //! @endcond
  257. } //namespace interfaceX
  258. //! @cond INTERNAL
  259. namespace internal {
  260. using interface9::internal::start_reduce;
  261. using interface9::internal::start_deterministic_reduce;
  262. //! Auxiliary class for parallel_reduce; for internal use only.
  263. /** The adaptor class that implements \ref parallel_reduce_body_req "parallel_reduce Body"
  264. using given \ref parallel_reduce_lambda_req "anonymous function objects".
  265. **/
  266. /** @ingroup algorithms */
  267. template<typename Range, typename Value, typename RealBody, typename Reduction>
  268. class lambda_reduce_body {
  269. //FIXME: decide if my_real_body, my_reduction, and identity_element should be copied or referenced
  270. // (might require some performance measurements)
  271. const Value& identity_element;
  272. const RealBody& my_real_body;
  273. const Reduction& my_reduction;
  274. Value my_value;
  275. lambda_reduce_body& operator= ( const lambda_reduce_body& other );
  276. public:
  277. lambda_reduce_body( const Value& identity, const RealBody& body, const Reduction& reduction )
  278. : identity_element(identity)
  279. , my_real_body(body)
  280. , my_reduction(reduction)
  281. , my_value(identity)
  282. { }
  283. lambda_reduce_body( const lambda_reduce_body& other )
  284. : identity_element(other.identity_element)
  285. , my_real_body(other.my_real_body)
  286. , my_reduction(other.my_reduction)
  287. , my_value(other.my_value)
  288. { }
  289. lambda_reduce_body( lambda_reduce_body& other, tbb::split )
  290. : identity_element(other.identity_element)
  291. , my_real_body(other.my_real_body)
  292. , my_reduction(other.my_reduction)
  293. , my_value(other.identity_element)
  294. { }
  295. void operator()(Range& range) {
  296. my_value = my_real_body(range, const_cast<const Value&>(my_value));
  297. }
  298. void join( lambda_reduce_body& rhs ) {
  299. my_value = my_reduction(const_cast<const Value&>(my_value), const_cast<const Value&>(rhs.my_value));
  300. }
  301. Value result() const {
  302. return my_value;
  303. }
  304. };
  305. } // namespace internal
  306. //! @endcond
  307. // Requirements on Range concept are documented in blocked_range.h
  308. /** \page parallel_reduce_body_req Requirements on parallel_reduce body
  309. Class \c Body implementing the concept of parallel_reduce body must define:
  310. - \code Body::Body( Body&, split ); \endcode Splitting constructor.
  311. Must be able to run concurrently with operator() and method \c join
  312. - \code Body::~Body(); \endcode Destructor
  313. - \code void Body::operator()( Range& r ); \endcode Function call operator applying body to range \c r
  314. and accumulating the result
  315. - \code void Body::join( Body& b ); \endcode Join results.
  316. The result in \c b should be merged into the result of \c this
  317. **/
  318. /** \page parallel_reduce_lambda_req Requirements on parallel_reduce anonymous function objects (lambda functions)
  319. TO BE DOCUMENTED
  320. **/
  321. /** \name parallel_reduce
  322. See also requirements on \ref range_req "Range" and \ref parallel_reduce_body_req "parallel_reduce Body". **/
  323. //@{
  324. //! Parallel iteration with reduction and default partitioner.
  325. /** @ingroup algorithms **/
  326. template<typename Range, typename Body>
  327. void parallel_reduce( const Range& range, Body& body ) {
  328. internal::start_reduce<Range,Body, const __TBB_DEFAULT_PARTITIONER>::run( range, body, __TBB_DEFAULT_PARTITIONER() );
  329. }
  330. //! Parallel iteration with reduction and simple_partitioner
  331. /** @ingroup algorithms **/
  332. template<typename Range, typename Body>
  333. void parallel_reduce( const Range& range, Body& body, const simple_partitioner& partitioner ) {
  334. internal::start_reduce<Range,Body,const simple_partitioner>::run( range, body, partitioner );
  335. }
  336. //! Parallel iteration with reduction and auto_partitioner
  337. /** @ingroup algorithms **/
  338. template<typename Range, typename Body>
  339. void parallel_reduce( const Range& range, Body& body, const auto_partitioner& partitioner ) {
  340. internal::start_reduce<Range,Body,const auto_partitioner>::run( range, body, partitioner );
  341. }
  342. //! Parallel iteration with reduction and static_partitioner
  343. /** @ingroup algorithms **/
  344. template<typename Range, typename Body>
  345. void parallel_reduce( const Range& range, Body& body, const static_partitioner& partitioner ) {
  346. internal::start_reduce<Range,Body,const static_partitioner>::run( range, body, partitioner );
  347. }
  348. //! Parallel iteration with reduction and affinity_partitioner
  349. /** @ingroup algorithms **/
  350. template<typename Range, typename Body>
  351. void parallel_reduce( const Range& range, Body& body, affinity_partitioner& partitioner ) {
  352. internal::start_reduce<Range,Body,affinity_partitioner>::run( range, body, partitioner );
  353. }
  354. #if __TBB_TASK_GROUP_CONTEXT
  355. //! Parallel iteration with reduction, default partitioner and user-supplied context.
  356. /** @ingroup algorithms **/
  357. template<typename Range, typename Body>
  358. void parallel_reduce( const Range& range, Body& body, task_group_context& context ) {
  359. internal::start_reduce<Range,Body,const __TBB_DEFAULT_PARTITIONER>::run( range, body, __TBB_DEFAULT_PARTITIONER(), context );
  360. }
  361. //! Parallel iteration with reduction, simple partitioner and user-supplied context.
  362. /** @ingroup algorithms **/
  363. template<typename Range, typename Body>
  364. void parallel_reduce( const Range& range, Body& body, const simple_partitioner& partitioner, task_group_context& context ) {
  365. internal::start_reduce<Range,Body,const simple_partitioner>::run( range, body, partitioner, context );
  366. }
  367. //! Parallel iteration with reduction, auto_partitioner and user-supplied context
  368. /** @ingroup algorithms **/
  369. template<typename Range, typename Body>
  370. void parallel_reduce( const Range& range, Body& body, const auto_partitioner& partitioner, task_group_context& context ) {
  371. internal::start_reduce<Range,Body,const auto_partitioner>::run( range, body, partitioner, context );
  372. }
  373. //! Parallel iteration with reduction, static_partitioner and user-supplied context
  374. /** @ingroup algorithms **/
  375. template<typename Range, typename Body>
  376. void parallel_reduce( const Range& range, Body& body, const static_partitioner& partitioner, task_group_context& context ) {
  377. internal::start_reduce<Range,Body,const static_partitioner>::run( range, body, partitioner, context );
  378. }
  379. //! Parallel iteration with reduction, affinity_partitioner and user-supplied context
  380. /** @ingroup algorithms **/
  381. template<typename Range, typename Body>
  382. void parallel_reduce( const Range& range, Body& body, affinity_partitioner& partitioner, task_group_context& context ) {
  383. internal::start_reduce<Range,Body,affinity_partitioner>::run( range, body, partitioner, context );
  384. }
  385. #endif /* __TBB_TASK_GROUP_CONTEXT */
  386. /** parallel_reduce overloads that work with anonymous function objects
  387. (see also \ref parallel_reduce_lambda_req "requirements on parallel_reduce anonymous function objects"). **/
  388. //! Parallel iteration with reduction and default partitioner.
  389. /** @ingroup algorithms **/
  390. template<typename Range, typename Value, typename RealBody, typename Reduction>
  391. Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction ) {
  392. internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction);
  393. internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,const __TBB_DEFAULT_PARTITIONER>
  394. ::run(range, body, __TBB_DEFAULT_PARTITIONER() );
  395. return body.result();
  396. }
  397. //! Parallel iteration with reduction and simple_partitioner.
  398. /** @ingroup algorithms **/
  399. template<typename Range, typename Value, typename RealBody, typename Reduction>
  400. Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction,
  401. const simple_partitioner& partitioner ) {
  402. internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction);
  403. internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,const simple_partitioner>
  404. ::run(range, body, partitioner );
  405. return body.result();
  406. }
  407. //! Parallel iteration with reduction and auto_partitioner
  408. /** @ingroup algorithms **/
  409. template<typename Range, typename Value, typename RealBody, typename Reduction>
  410. Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction,
  411. const auto_partitioner& partitioner ) {
  412. internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction);
  413. internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,const auto_partitioner>
  414. ::run( range, body, partitioner );
  415. return body.result();
  416. }
  417. //! Parallel iteration with reduction and static_partitioner
  418. /** @ingroup algorithms **/
  419. template<typename Range, typename Value, typename RealBody, typename Reduction>
  420. Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction,
  421. const static_partitioner& partitioner ) {
  422. internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction);
  423. internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,const static_partitioner>
  424. ::run( range, body, partitioner );
  425. return body.result();
  426. }
  427. //! Parallel iteration with reduction and affinity_partitioner
  428. /** @ingroup algorithms **/
  429. template<typename Range, typename Value, typename RealBody, typename Reduction>
  430. Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction,
  431. affinity_partitioner& partitioner ) {
  432. internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction);
  433. internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,affinity_partitioner>
  434. ::run( range, body, partitioner );
  435. return body.result();
  436. }
  437. #if __TBB_TASK_GROUP_CONTEXT
  438. //! Parallel iteration with reduction, default partitioner and user-supplied context.
  439. /** @ingroup algorithms **/
  440. template<typename Range, typename Value, typename RealBody, typename Reduction>
  441. Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction,
  442. task_group_context& context ) {
  443. internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction);
  444. internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,const __TBB_DEFAULT_PARTITIONER>
  445. ::run( range, body, __TBB_DEFAULT_PARTITIONER(), context );
  446. return body.result();
  447. }
  448. //! Parallel iteration with reduction, simple partitioner and user-supplied context.
  449. /** @ingroup algorithms **/
  450. template<typename Range, typename Value, typename RealBody, typename Reduction>
  451. Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction,
  452. const simple_partitioner& partitioner, task_group_context& context ) {
  453. internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction);
  454. internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,const simple_partitioner>
  455. ::run( range, body, partitioner, context );
  456. return body.result();
  457. }
  458. //! Parallel iteration with reduction, auto_partitioner and user-supplied context
  459. /** @ingroup algorithms **/
  460. template<typename Range, typename Value, typename RealBody, typename Reduction>
  461. Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction,
  462. const auto_partitioner& partitioner, task_group_context& context ) {
  463. internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction);
  464. internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,const auto_partitioner>
  465. ::run( range, body, partitioner, context );
  466. return body.result();
  467. }
  468. //! Parallel iteration with reduction, static_partitioner and user-supplied context
  469. /** @ingroup algorithms **/
  470. template<typename Range, typename Value, typename RealBody, typename Reduction>
  471. Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction,
  472. const static_partitioner& partitioner, task_group_context& context ) {
  473. internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction);
  474. internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,const static_partitioner>
  475. ::run( range, body, partitioner, context );
  476. return body.result();
  477. }
  478. //! Parallel iteration with reduction, affinity_partitioner and user-supplied context
  479. /** @ingroup algorithms **/
  480. template<typename Range, typename Value, typename RealBody, typename Reduction>
  481. Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction,
  482. affinity_partitioner& partitioner, task_group_context& context ) {
  483. internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction);
  484. internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,affinity_partitioner>
  485. ::run( range, body, partitioner, context );
  486. return body.result();
  487. }
  488. #endif /* __TBB_TASK_GROUP_CONTEXT */
  489. //! Parallel iteration with deterministic reduction and default simple partitioner.
  490. /** @ingroup algorithms **/
  491. template<typename Range, typename Body>
  492. void parallel_deterministic_reduce( const Range& range, Body& body ) {
  493. internal::start_deterministic_reduce<Range, Body, const simple_partitioner>::run(range, body, simple_partitioner());
  494. }
  495. //! Parallel iteration with deterministic reduction and simple partitioner.
  496. /** @ingroup algorithms **/
  497. template<typename Range, typename Body>
  498. void parallel_deterministic_reduce( const Range& range, Body& body, const simple_partitioner& partitioner ) {
  499. internal::start_deterministic_reduce<Range, Body, const simple_partitioner>::run(range, body, partitioner);
  500. }
  501. //! Parallel iteration with deterministic reduction and static partitioner.
  502. /** @ingroup algorithms **/
  503. template<typename Range, typename Body>
  504. void parallel_deterministic_reduce( const Range& range, Body& body, const static_partitioner& partitioner ) {
  505. internal::start_deterministic_reduce<Range, Body, const static_partitioner>::run(range, body, partitioner);
  506. }
  507. #if __TBB_TASK_GROUP_CONTEXT
  508. //! Parallel iteration with deterministic reduction, default simple partitioner and user-supplied context.
  509. /** @ingroup algorithms **/
  510. template<typename Range, typename Body>
  511. void parallel_deterministic_reduce( const Range& range, Body& body, task_group_context& context ) {
  512. internal::start_deterministic_reduce<Range,Body, const simple_partitioner>::run( range, body, simple_partitioner(), context );
  513. }
  514. //! Parallel iteration with deterministic reduction, simple partitioner and user-supplied context.
  515. /** @ingroup algorithms **/
  516. template<typename Range, typename Body>
  517. void parallel_deterministic_reduce( const Range& range, Body& body, const simple_partitioner& partitioner, task_group_context& context ) {
  518. internal::start_deterministic_reduce<Range, Body, const simple_partitioner>::run(range, body, partitioner, context);
  519. }
  520. //! Parallel iteration with deterministic reduction, static partitioner and user-supplied context.
  521. /** @ingroup algorithms **/
  522. template<typename Range, typename Body>
  523. void parallel_deterministic_reduce( const Range& range, Body& body, const static_partitioner& partitioner, task_group_context& context ) {
  524. internal::start_deterministic_reduce<Range, Body, const static_partitioner>::run(range, body, partitioner, context);
  525. }
  526. #endif /* __TBB_TASK_GROUP_CONTEXT */
  527. /** parallel_reduce overloads that work with anonymous function objects
  528. (see also \ref parallel_reduce_lambda_req "requirements on parallel_reduce anonymous function objects"). **/
  529. //! Parallel iteration with deterministic reduction and default simple partitioner.
  530. // TODO: consider making static_partitioner the default
  531. /** @ingroup algorithms **/
  532. template<typename Range, typename Value, typename RealBody, typename Reduction>
  533. Value parallel_deterministic_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction ) {
  534. return parallel_deterministic_reduce(range, identity, real_body, reduction, simple_partitioner());
  535. }
  536. //! Parallel iteration with deterministic reduction and simple partitioner.
  537. /** @ingroup algorithms **/
  538. template<typename Range, typename Value, typename RealBody, typename Reduction>
  539. Value parallel_deterministic_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, const simple_partitioner& partitioner ) {
  540. internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction);
  541. internal::start_deterministic_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>, const simple_partitioner>
  542. ::run(range, body, partitioner);
  543. return body.result();
  544. }
  545. //! Parallel iteration with deterministic reduction and static partitioner.
  546. /** @ingroup algorithms **/
  547. template<typename Range, typename Value, typename RealBody, typename Reduction>
  548. Value parallel_deterministic_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, const static_partitioner& partitioner ) {
  549. internal::lambda_reduce_body<Range, Value, RealBody, Reduction> body(identity, real_body, reduction);
  550. internal::start_deterministic_reduce<Range, internal::lambda_reduce_body<Range, Value, RealBody, Reduction>, const static_partitioner>
  551. ::run(range, body, partitioner);
  552. return body.result();
  553. }
  554. #if __TBB_TASK_GROUP_CONTEXT
  555. //! Parallel iteration with deterministic reduction, default simple partitioner and user-supplied context.
  556. /** @ingroup algorithms **/
  557. template<typename Range, typename Value, typename RealBody, typename Reduction>
  558. Value parallel_deterministic_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction,
  559. task_group_context& context ) {
  560. return parallel_deterministic_reduce(range, identity, real_body, reduction, simple_partitioner(), context);
  561. }
  562. //! Parallel iteration with deterministic reduction, simple partitioner and user-supplied context.
  563. /** @ingroup algorithms **/
  564. template<typename Range, typename Value, typename RealBody, typename Reduction>
  565. Value parallel_deterministic_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction,
  566. const simple_partitioner& partitioner, task_group_context& context ) {
  567. internal::lambda_reduce_body<Range, Value, RealBody, Reduction> body(identity, real_body, reduction);
  568. internal::start_deterministic_reduce<Range, internal::lambda_reduce_body<Range, Value, RealBody, Reduction>, const simple_partitioner>
  569. ::run(range, body, partitioner, context);
  570. return body.result();
  571. }
  572. //! Parallel iteration with deterministic reduction, static partitioner and user-supplied context.
  573. /** @ingroup algorithms **/
  574. template<typename Range, typename Value, typename RealBody, typename Reduction>
  575. Value parallel_deterministic_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction,
  576. const static_partitioner& partitioner, task_group_context& context ) {
  577. internal::lambda_reduce_body<Range, Value, RealBody, Reduction> body(identity, real_body, reduction);
  578. internal::start_deterministic_reduce<Range, internal::lambda_reduce_body<Range, Value, RealBody, Reduction>, const static_partitioner>
  579. ::run(range, body, partitioner, context);
  580. return body.result();
  581. }
  582. #endif /* __TBB_TASK_GROUP_CONTEXT */
  583. //@}
  584. } // namespace tbb
  585. #include "internal/_warning_suppress_disable_notice.h"
  586. #undef __TBB_parallel_reduce_H_include_area
  587. #endif /* __TBB_parallel_reduce_H */