1 /* reducer_list.h -*- C++ -*- 2 * 3 * @copyright 4 * Copyright (C) 2009-2013, Intel Corporation 5 * All rights reserved. 6 * 7 * @copyright 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * * Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * * Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * * Neither the name of Intel Corporation nor the names of its 19 * contributors may be used to endorse or promote products derived 20 * from this software without specific prior written permission. 21 * 22 * @copyright 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 30 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 31 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 33 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 * POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37 /** @file reducer_list.h 38 * 39 * @brief Defines classes for doing parallel list creation by appending or 40 * prepending. 41 * 42 * @ingroup ReducersList 43 * 44 * @see ReducersList 45 */ 46 47 #ifndef REDUCER_LIST_H_INCLUDED 48 #define REDUCER_LIST_H_INCLUDED 49 50 #include <cilk/reducer.h> 51 #include <list> 52 53 /** @defgroup ReducersList List Reducers 54 * 55 * List append and prepend reducers allow the creation of a standard list by 56 * concatenating a set of lists or values in parallel. 57 * 58 * @ingroup Reducers 59 * 60 * You should be familiar with @ref pagereducers "Cilk reducers", described in 61 * file `reducers.md`, and particularly with @ref reducers_using, before trying 62 * to use the information in this file. 63 * 64 * @section redlist_usage Usage Example 65 * 66 * // Create a list containing the labels of the nodes of a tree in 67 * // “inorder” (left subtree, root, right subtree). 68 * 69 * struct Tree { Tree* left; Tree* right; string label; ... }; 70 * 71 * list<string> x; 72 * cilk::reducer< cilk::op_list_append<string> > xr(cilk::move_in(x)); 73 * collect_labels(tree, xr); 74 * xr.move_out(x); 75 * 76 * void collect_labels(Tree* node, 77 * cilk::reducer< cilk::op_list_append<string> >& xr) 78 * { 79 * if (node) { 80 * cilk_spawn collect_labels(node->left, xr); 81 * xr->push_back(node->label); 82 * collect_labels(node->right, xr); 83 * cilk_sync; 84 * } 85 * } 86 * 87 * @section redlist_monoid The Monoid 88 * 89 * @subsection redlist_monoid_values Value Set 90 * 91 * The value set of a list reducer is the set of values of the class 92 * `std::list<Type, Allocator>`, which we refer to as “the reducer’s list 93 * type”. 94 * 95 * @subsection redlist_monoid_operator Operator 96 * 97 * The operator of a list append reducer is defined as 98 * 99 * x CAT y == (every element of x, followed by every element of y) 100 * 101 * The operator of a list prepend reducer is defined as 102 * 103 * x RCAT y == (every element of y, followed by every element of x) 104 * 105 * @subsection redlist_monoid_identity Identity 106 * 107 * The identity value of a list reducer is the empty list, which is the value 108 * of the expression `std::list<Type, Allocator>([allocator])`. 109 * 110 * @section redlist_operations Operations 111 * 112 * In the operation descriptions below, the type name `List` refers to the 113 * reducer’s string type, `std::list<Type, Allocator>`. 114 * 115 * @subsection redlist_constructors Constructors 116 * 117 * Any argument list which is valid for a `std::list` constructor is valid for 118 * a list reducer constructor. The usual move-in constructor is also provided: 119 * 120 * reducer(move_in(List& variable)) 121 * 122 * A list reducer with no constructor arguments, or with only an allocator 123 * argument, will initially contain the identity value, an empty list. 124 * 125 * @subsection redlist_get_set Set and Get 126 * 127 * r.set_value(const List& value) 128 * const List& = r.get_value() const 129 * r.move_in(List& variable) 130 * r.move_out(List& variable) 131 * 132 * @subsection redlist_view_ops View Operations 133 * 134 * The view of a list append reducer provides the following member functions: 135 * 136 * void push_back(const Type& element) 137 * void insert_back(List::size_type n, const Type& element) 138 * template <typename Iter> void insert_back(Iter first, Iter last) 139 * void splice_back(List& x) 140 * void splice_back(List& x, List::iterator i) 141 * void splice_back(List& x, List::iterator first, List::iterator last) 142 * 143 * The view of a list prepend reducer provides the following member functions: 144 * 145 * void push_front(const Type& element) 146 * void insert_front(List::size_type n, const Type& element) 147 * template <typename Iter> void insert_front(Iter first, Iter last) 148 * void splice_front(List& x) 149 * void splice_front(List& x, List::iterator i) 150 * void splice_front(List& x, List::iterator first, List::iterator last) 151 * 152 * The `push_back` and `push_front` functions are the same as the 153 * corresponding `std::list` functions. The `insert_back`, `splice_back`, 154 * `insert_front`, and `splice_front` functions are the same as the 155 * `std::list` `insert` and `splice` functions, with the first parameter 156 * fixed to the end or beginning of the list, respectively. 157 * 158 * @section redlist_performance Performance Considerations 159 * 160 * An efficient reducer requires that combining the values of two views (using 161 * the view `reduce()` function) be a constant-time operations. Two lists can 162 * be merged in constant time using the `splice()` function if they have the 163 * same allocator. Therefore, the lists for new views are created (by the view 164 * identity constructor) using the same allocator as the list that was created 165 * when the reducer was constructed. 166 * 167 * The performance of adding elements to a list reducer depends on the view 168 * operations that are used: 169 * 170 * * The `push` functions add a single element to the list, and therefore 171 * take constant time. 172 * * An `insert` function that inserts _N_ elements adds each of them 173 * individually, and therefore takes _O(N)_ time. 174 * * A `splice` function that inserts _N_ elements just adjusts a couple of 175 * pointers, and therefore takes constant time, _if the splice is from a 176 * list with the same allocator as the reducer_. Otherwise, it is 177 * equivalent to an `insert`, and takes _O(N)_ time. 178 * 179 * This means that for best performance, if you will be adding elements to a 180 * list reducer in batches, you should `splice` them from a list having the 181 * same allocator as the reducer. 182 * 183 * The reducer `move_in` and `move_out` functions do a constant-time `swap` if 184 * the variable has the same allocator as the reducer, and a linear-time copy 185 * otherwise. 186 * 187 * Note that the allocator of a list reducer is determined when the reducer is 188 * constructed. The following two examples may have very different behavior: 189 * 190 * list<Element, Allocator> a_list; 191 * 192 * reducer< list_append<Element, Allocator> reducer1(move_in(a_list)); 193 * ... parallel computation ... 194 * reducer1.move_out(a_list); 195 * 196 * reducer< list_append<Element, Allocator> reducer2; 197 * reducer2.move_in(a_list); 198 * ... parallel computation ... 199 * reducer2.move_out(a_list); 200 * 201 * * `reducer1` will be constructed with the same allocator as `a_list`, 202 * because the list was was specified in the constructor. The `move_in` 203 * and`move_out` can therefore be done with a `swap` in constant time. 204 * * `reducer2` will be constructed with a _default_ allocator, 205 * “`Allocator()`”, which may or may not be the same as the allocator of 206 * `a_list`. Therefore, the `move_in` and `move_out` may have to be done 207 * with a copy in _O(N)_ time. 208 * 209 * (All instances of an allocator type with no internal state (like 210 * `std::allocator`) are “the same”. You only need to worry about the “same 211 * allocator” issue when you create list reducers with custom allocator types.) 212 * 213 * @section redlist_types Type and Operator Requirements 214 * 215 * `std::list<Type, Allocator>` must be a valid type. 216 */ 217 218 219 namespace cilk { 220 221 namespace internal { 222 223 /** @ingroup ReducersList */ 224 //@{ 225 226 /** Base class for list append and prepend view classes. 227 * 228 * @note This class provides the definitions that are required for a class 229 * that will be used as the parameter of a @ref list_monoid_base 230 * specialization. 231 * 232 * @tparam Type The list element type (not the list type). 233 * @tparam Allocator The list's allocator class. 234 * 235 * @see ReducersList 236 * @see list_monoid_base 237 */ 238 template <typename Type, typename Allocator> 239 class list_view_base 240 { 241 protected: 242 /// The type of the contained list. 243 typedef std::list<Type, Allocator> list_type; 244 245 /// The list accumulator variable. 246 list_type m_value; 247 248 public: 249 250 /** @name Monoid support. 251 */ 252 //@{ 253 254 /// Required by @ref monoid_with_view 255 typedef list_type value_type; 256 257 /// Required by @ref list_monoid_base get_allocator()258 Allocator get_allocator() const 259 { 260 return m_value.get_allocator(); 261 } 262 263 //@} 264 265 266 /** @name Constructors. 267 */ 268 //@{ 269 270 /// Standard list constructor. m_value(a)271 explicit list_view_base(const Allocator& a = Allocator()) : m_value(a) {} 272 explicit list_view_base( 273 typename list_type::size_type n, 274 const Type& value = Type(), m_value(n,value,a)275 const Allocator& a = Allocator() ) : m_value(n, value, a) {} 276 template <typename Iter> 277 list_view_base(Iter first, Iter last, const Allocator& a = Allocator()) : m_value(first,last,a)278 m_value(first, last, a) {} list_view_base(const list_type & list)279 list_view_base(const list_type& list) : m_value(list) {} 280 281 /// Move-in constructor. list_view_base(move_in_wrapper<value_type> w)282 explicit list_view_base(move_in_wrapper<value_type> w) 283 : m_value(w.value().get_allocator()) 284 { 285 m_value.swap(w.value()); 286 } 287 288 //@} 289 290 /** @name Reducer support. 291 */ 292 //@{ 293 294 /// Required by reducer::move_in() view_move_in(value_type & v)295 void view_move_in(value_type& v) 296 { 297 if (m_value.get_allocator() == v.get_allocator()) 298 // Equal allocators. Do a (fast) swap. 299 m_value.swap(v); 300 else 301 // Unequal allocators. Do a (slow) copy. 302 m_value = v; 303 v.clear(); 304 } 305 306 /// Required by reducer::move_out() view_move_out(value_type & v)307 void view_move_out(value_type& v) 308 { 309 if (m_value.get_allocator() == v.get_allocator()) 310 // Equal allocators. Do a (fast) swap. 311 m_value.swap(v); 312 else 313 // Unequal allocators. Do a (slow) copy. 314 v = m_value; 315 m_value.clear(); 316 } 317 318 /// Required by reducer::set_value() view_set_value(const value_type & v)319 void view_set_value(const value_type& v) { m_value = v; } 320 321 /// Required by reducer::get_value() view_get_value()322 value_type const& view_get_value() const { return m_value; } 323 324 // Required by legacy wrapper get_reference() view_get_reference()325 value_type & view_get_reference() { return m_value; } view_get_reference()326 value_type const& view_get_reference() const { return m_value; } 327 328 //@} 329 }; 330 331 332 /** Base class for list append and prepend monoid classes. 333 * 334 * The key to efficient reducers is that the `identity` operation, which 335 * creates a new per-strand view, and the `reduce` operation, which combines 336 * two per-strand views, must be constant-time operations. Two lists can be 337 * concatenated in constant time only if they have the same allocator. 338 * Therefore, all the per-strand list accumulator variables must be created 339 * with the same allocator as the leftmost view list. 340 * 341 * This means that a list reduction monoid must have a copy of the allocator 342 * of the leftmost view’s list, so that it can use it in the `identity` 343 * operation. This, in turn, requires that list reduction monoids have a 344 * specialized `construct()` function, which constructs the leftmost view 345 * before the monoid, and then passes the leftmost view’s allocator to the 346 * monoid constructor. 347 * 348 * @tparam View The list append or prepend view class. 349 * @tparam Align If `false` (the default), reducers instantiated on this 350 * monoid will be naturally aligned (the Cilk library 1.0 351 * behavior). If `true`, reducers instantiated on this monoid 352 * will be cache-aligned for binary compatibility with 353 * reducers in Cilk library version 0.9. 354 * 355 * @see ReducersList 356 * @see list_view_base 357 */ 358 template <typename View, bool Align> 359 class list_monoid_base : public monoid_with_view<View, Align> 360 { 361 typedef typename View::value_type list_type; 362 typedef typename list_type::allocator_type allocator_type; 363 allocator_type m_allocator; 364 365 using monoid_base<list_type, View>::provisional; 366 367 public: 368 369 /** Constructor. 370 * 371 * There is no default constructor for list monoids, because the allocator 372 * must always be specified. 373 * 374 * @param allocator The list allocator to be used when 375 * identity-constructing new views. 376 */ 377 list_monoid_base(const allocator_type& allocator = allocator_type()) : m_allocator(allocator)378 m_allocator(allocator) {} 379 380 /** Create an identity view. 381 * 382 * List view identity constructors take the list allocator as an argument. 383 * 384 * @param v The address of the uninitialized memory in which the view 385 * will be constructed. 386 */ identity(View * v)387 void identity(View *v) const { ::new((void*) v) View(m_allocator); } 388 389 /** @name construct functions 390 * 391 * All `construct()` functions first construct the leftmost view, using 392 * the optional @a x1, @a x2, and @a x3 arguments that were passed in from 393 * the reducer constructor. They then call the view’s `get_allocator()` 394 * function to get the list allocator from its contained list, and pass it 395 * to the monoid constructor. 396 */ 397 //@{ 398 399 template <typename Monoid> construct(Monoid * monoid,View * view)400 static void construct(Monoid* monoid, View* view) 401 { provisional( new ((void*)view) View() ).confirm_if( 402 new ((void*)monoid) Monoid(view->get_allocator()) ); } 403 404 template <typename Monoid, typename T1> construct(Monoid * monoid,View * view,const T1 & x1)405 static void construct(Monoid* monoid, View* view, const T1& x1) 406 { provisional( new ((void*)view) View(x1) ).confirm_if( 407 new ((void*)monoid) Monoid(view->get_allocator()) ); } 408 409 template <typename Monoid, typename T1, typename T2> construct(Monoid * monoid,View * view,const T1 & x1,const T2 & x2)410 static void construct(Monoid* monoid, View* view, const T1& x1, const T2& x2) 411 { provisional( new ((void*)view) View(x1, x2) ).confirm_if( 412 new ((void*)monoid) Monoid(view->get_allocator()) ); } 413 414 template <typename Monoid, typename T1, typename T2, typename T3> construct(Monoid * monoid,View * view,const T1 & x1,const T2 & x2,const T3 & x3)415 static void construct(Monoid* monoid, View* view, const T1& x1, const T2& x2, 416 const T3& x3) 417 { provisional( new ((void*)view) View(x1, x2, x3) ).confirm_if( 418 new ((void*)monoid) Monoid(view->get_allocator()) ); } 419 420 //@} 421 }; 422 423 //@} 424 425 } // namespace internal 426 427 428 /** @ingroup ReducersList */ 429 //@{ 430 431 /** The list append reducer view class. 432 * 433 * This is the view class for reducers created with 434 * `cilk::reducer< cilk::op_list_append<Type, Allocator> >`. It holds the 435 * accumulator variable for the reduction, and allows only append operations 436 * to be performed on it. 437 * 438 * @note The reducer “dereference” operation (`reducer::operator *()`) 439 * yields a reference to the view. Thus, for example, the view class’s 440 * `push_back` operation would be used in an expression like 441 * `r->push_back(a)`, where `r` is a list append reducer variable. 442 * 443 * @tparam Type The list element type (not the list type). 444 * @tparam Allocator The list allocator type. 445 * 446 * @see ReducersList 447 * @see op_list_append 448 */ 449 template <class Type, 450 class Allocator = typename std::list<Type>::allocator_type> 451 class op_list_append_view : public internal::list_view_base<Type, Allocator> 452 { 453 typedef internal::list_view_base<Type, Allocator> base; 454 typedef std::list<Type, Allocator> list_type; 455 typedef typename list_type::iterator iterator; 456 end()457 iterator end() { return this->m_value.end(); } 458 459 public: 460 461 /** @name Constructors. 462 * 463 * All op_list_append_view constructors simply pass their arguments on to 464 * the @ref internal::list_view_base base class constructor. 465 * 466 * @ref internal::list_view_base supports all the std::list constructor 467 * forms, as well as the reducer move_in constructor form. 468 */ 469 //@{ 470 op_list_append_view()471 op_list_append_view() : base() {} 472 473 template <typename T1> op_list_append_view(const T1 & x1)474 op_list_append_view(const T1& x1) : base(x1) {} 475 476 template <typename T1, typename T2> op_list_append_view(const T1 & x1,const T2 & x2)477 op_list_append_view(const T1& x1, const T2& x2) : base(x1, x2) {} 478 479 template <typename T1, typename T2, typename T3> op_list_append_view(const T1 & x1,const T2 & x2,const T3 & x3)480 op_list_append_view(const T1& x1, const T2& x2, const T3& x3) : 481 base(x1, x2, x3) {} 482 483 //@} 484 485 /** @name View modifier operations. 486 */ 487 //@{ 488 489 /** Add an element at the end of the list. 490 * 491 * This is equivalent to `list.push_back(element)` 492 */ push_back(const Type & element)493 void push_back(const Type& element) 494 { this->m_value.push_back(element); } 495 496 /** Insert elements at the end of the list. 497 * 498 * This is equivalent to `list.insert(list.end(), n, element)` 499 */ insert_back(typename list_type::size_type n,const Type & element)500 void insert_back(typename list_type::size_type n, const Type& element) 501 { this->m_value.insert(end(), n, element); } 502 503 /** Insert elements at the end of the list. 504 * 505 * This is equivalent to `list.insert(list.end(), first, last)` 506 */ 507 template <typename Iter> insert_back(Iter first,Iter last)508 void insert_back(Iter first, Iter last) 509 { this->m_value.insert(end(), first, last); } 510 511 /** Splice elements at the end of the list. 512 * 513 * This is equivalent to `list.splice(list.end(), x)` 514 */ splice_back(list_type & x)515 void splice_back(list_type& x) { 516 if (x.get_allocator() == this->m_value.get_allocator()) 517 this->m_value.splice(end(), x); 518 else { 519 insert_back(x.begin(), x.end()); 520 x.clear(); 521 } 522 } 523 524 /** Splice elements at the end of the list. 525 * 526 * This is equivalent to `list.splice(list.end(), x, i)` 527 */ splice_back(list_type & x,iterator i)528 void splice_back(list_type& x, iterator i) { 529 if (x.get_allocator() == this->m_value.get_allocator()) 530 this->m_value.splice(end(), x, i); 531 else { 532 push_back(*i); 533 x.erase(i); 534 } 535 } 536 537 /** Splice elements at the end of the list. 538 * 539 * This is equivalent to `list.splice(list.end(), x, first, last)` 540 */ splice_back(list_type & x,iterator first,iterator last)541 void splice_back(list_type& x, iterator first, iterator last) { 542 if (x.get_allocator() == this->m_value.get_allocator()) 543 this->m_value.splice(end(), x, first, last); 544 else { 545 insert_back(first, last); 546 x.erase(first, last); 547 } 548 } 549 550 //@} 551 552 /** Reduction operation. 553 * 554 * This function is invoked by the @ref op_list_append monoid to combine 555 * the views of two strands when the right strand merges with the left 556 * one. It appends the value contained in the right-strand view to the 557 * value contained in the left-strand view, and leaves the value in the 558 * right-strand view undefined. 559 * 560 * @param right A pointer to the right-strand view. (`this` points to 561 * the left-strand view.) 562 * 563 * @note Used only by the @ref op_list_append monoid to implement the 564 * monoid reduce operation. 565 */ reduce(op_list_append_view * right)566 void reduce(op_list_append_view* right) 567 { 568 __CILKRTS_ASSERT( 569 this->m_value.get_allocator() == right->m_value.get_allocator()); 570 this->m_value.splice(end(), right->m_value); 571 } 572 }; 573 574 575 /** The list prepend reducer view class. 576 * 577 * This is the view class for reducers created with 578 * `cilk::reducer< cilk::op_list_prepend<Type, Allocator> >`. It holds the 579 * accumulator variable for the reduction, and allows only prepend operations 580 * to be performed on it. 581 * 582 * @note The reducer “dereference” operation (`reducer::operator *()`) 583 * yields a reference to the view. Thus, for example, the view class’s 584 * `push_front` operation would be used in an expression like 585 * `r->push_front(a)`, where `r` is a list prepend reducer variable. 586 * 587 * @tparam Type The list element type (not the list type). 588 * @tparam Allocator The list allocator type. 589 * 590 * @see ReducersList 591 * @see op_list_prepend 592 */ 593 template <class Type, 594 class Allocator = typename std::list<Type>::allocator_type> 595 class op_list_prepend_view : public internal::list_view_base<Type, Allocator> 596 { 597 typedef internal::list_view_base<Type, Allocator> base; 598 typedef std::list<Type, Allocator> list_type; 599 typedef typename list_type::iterator iterator; 600 begin()601 iterator begin() { return this->m_value.begin(); } 602 603 public: 604 605 /** @name Constructors. 606 * 607 * All op_list_prepend_view constructors simply pass their arguments on to 608 * the @ref internal::list_view_base base class constructor. 609 * 610 * @ref internal::list_view_base supports all the std::list constructor 611 * forms, as well as the reducer move_in constructor form. 612 * 613 */ 614 //@{ 615 op_list_prepend_view()616 op_list_prepend_view() : base() {} 617 618 template <typename T1> op_list_prepend_view(const T1 & x1)619 op_list_prepend_view(const T1& x1) : base(x1) {} 620 621 template <typename T1, typename T2> op_list_prepend_view(const T1 & x1,const T2 & x2)622 op_list_prepend_view(const T1& x1, const T2& x2) : base(x1, x2) {} 623 624 template <typename T1, typename T2, typename T3> op_list_prepend_view(const T1 & x1,const T2 & x2,const T3 & x3)625 op_list_prepend_view(const T1& x1, const T2& x2, const T3& x3) : 626 base(x1, x2, x3) {} 627 628 //@} 629 630 /** @name View modifier operations. 631 */ 632 //@{ 633 634 /** Add an element at the beginning of the list. 635 * 636 * This is equivalent to `list.push_front(element)` 637 */ push_front(const Type & element)638 void push_front(const Type& element) 639 { this->m_value.push_front(element); } 640 641 /** Insert elements at the beginning of the list. 642 * 643 * This is equivalent to `list.insert(list.begin(), n, element)` 644 */ insert_front(typename list_type::size_type n,const Type & element)645 void insert_front(typename list_type::size_type n, const Type& element) 646 { this->m_value.insert(begin(), n, element); } 647 648 /** Insert elements at the beginning of the list. 649 * 650 * This is equivalent to `list.insert(list.begin(), first, last)` 651 */ 652 template <typename Iter> insert_front(Iter first,Iter last)653 void insert_front(Iter first, Iter last) 654 { this->m_value.insert(begin(), first, last); } 655 656 /** Splice elements at the beginning of the list. 657 * 658 * This is equivalent to `list.splice(list.begin(), x)` 659 */ splice_front(list_type & x)660 void splice_front(list_type& x) { 661 if (x.get_allocator() == this->m_value.get_allocator()) 662 this->m_value.splice(begin(), x); 663 else { 664 insert_front(x.begin(), x.begin()); 665 x.clear(); 666 } 667 } 668 669 /** Splice elements at the beginning of the list. 670 * 671 * This is equivalent to `list.splice(list.begin(), x, i)` 672 */ splice_front(list_type & x,iterator i)673 void splice_front(list_type& x, iterator i) { 674 if (x.get_allocator() == this->m_value.get_allocator()) 675 this->m_value.splice(begin(), x, i); 676 else { 677 push_front(*i); 678 x.erase(i); 679 } 680 } 681 682 /** Splice elements at the beginning of the list. 683 * 684 * This is equivalent to `list.splice(list.begin(), x, first, last)` 685 */ splice_front(list_type & x,iterator first,iterator last)686 void splice_front(list_type& x, iterator first, iterator last) { 687 if (x.get_allocator() == this->m_value.get_allocator()) 688 this->m_value.splice(begin(), x, first, last); 689 else { 690 insert_front(first, last); 691 x.erase(first, last); 692 } 693 } 694 695 //@} 696 697 /** Reduction operation. 698 * 699 * This function is invoked by the @ref op_list_prepend monoid to combine 700 * the views of two strands when the right strand merges with the left 701 * one. It prepends the value contained in the right-strand view to the 702 * value contained in the left-strand view, and leaves the value in the 703 * right-strand view undefined. 704 * 705 * @param right A pointer to the right-strand view. (`this` points to 706 * the left-strand view.) 707 * 708 * @note Used only by the @ref op_list_prepend monoid to implement the 709 * monoid reduce operation. 710 */ 711 /** Reduce operation. 712 * 713 * Required by @ref monoid_base. 714 */ reduce(op_list_prepend_view * right)715 void reduce(op_list_prepend_view* right) 716 { 717 __CILKRTS_ASSERT( 718 this->m_value.get_allocator() == right->m_value.get_allocator()); 719 this->m_value.splice(begin(), right->m_value); 720 } 721 }; 722 723 724 725 /** Monoid class for list append reductions. Instantiate the cilk::reducer 726 * template class with a op_list_append monoid to create a list append reducer 727 * class. For example, to create a list of strings: 728 * 729 * cilk::reducer< cilk::op_list_append<std::string> > r; 730 * 731 * @tparam Type The list element type (not the list type). 732 * @tparam Alloc The list allocator type. 733 * @tparam Align If `false` (the default), reducers instantiated on this 734 * monoid will be naturally aligned (the Cilk library 1.0 735 * behavior). If `true`, reducers instantiated on this monoid 736 * will be cache-aligned for binary compatibility with 737 * reducers in Cilk library version 0.9. 738 * 739 * @see ReducersList 740 * @see op_list_append_view 741 */ 742 template <typename Type, 743 typename Allocator = typename std::list<Type>::allocator_type, 744 bool Align = false> 745 struct op_list_append : 746 public internal::list_monoid_base<op_list_append_view<Type, Allocator>, Align> 747 { 748 /// Construct with default allocator. op_list_appendop_list_append749 op_list_append() {} 750 /// Construct with specified allocator. op_list_appendop_list_append751 op_list_append(const Allocator& alloc) : 752 internal::list_monoid_base<op_list_append_view<Type, Allocator>, Align>(alloc) {} 753 }; 754 755 /** Monoid class for list prepend reductions. Instantiate the cilk::reducer 756 * template class with a op_list_prepend monoid to create a list prepend 757 * reducer class. For example, to create a list of strings: 758 * 759 * cilk::reducer< cilk::op_list_prepend<std::string> > r; 760 * 761 * @tparam Type The list element type (not the list type). 762 * @tparam Alloc The list allocator type. 763 * @tparam Align If `false` (the default), reducers instantiated on this 764 * monoid will be naturally aligned (the Cilk library 1.0 765 * behavior). If `true`, reducers instantiated on this monoid 766 * will be cache-aligned for binary compatibility with 767 * reducers in Cilk library version 0.9. 768 * 769 * @see ReducersList 770 * @see op_list_prepend_view 771 */ 772 template <typename Type, 773 typename Allocator = typename std::list<Type>::allocator_type, 774 bool Align = false> 775 struct op_list_prepend : 776 public internal::list_monoid_base<op_list_prepend_view<Type, Allocator>, Align> 777 { 778 /// Construct with default allocator. op_list_prependop_list_prepend779 op_list_prepend() {} 780 /// Construct with specified allocator. op_list_prependop_list_prepend781 op_list_prepend(const Allocator& alloc) : 782 internal::list_monoid_base<op_list_prepend_view<Type, Allocator>, Align>(alloc) {} 783 }; 784 785 786 /** Deprecated list append reducer wrapper class. 787 * 788 * reducer_list_append is the same as 789 * @ref reducer<@ref op_list_append>, except that reducer_list_append is a 790 * proxy for the contained view, so that accumulator variable update 791 * operations can be applied directly to the reducer. For example, an element 792 * is appended to a `reducer<%op_list_append>` with `r->push_back(a)`, but an 793 * element can be appended to a `%reducer_list_append` with `r.push_back(a)`. 794 * 795 * @deprecated Users are strongly encouraged to use `reducer<monoid>` 796 * reducers rather than the old wrappers like reducer_list_append. 797 * The `reducer<monoid>` reducers show the reducer/monoid/view 798 * architecture more clearly, are more consistent in their 799 * implementation, and present a simpler model for new 800 * user-implemented reducers. 801 * 802 * @note Implicit conversions are provided between `%reducer_list_append` 803 * and `reducer<%op_list_append>`. This allows incremental code 804 * conversion: old code that used `%reducer_list_append` can pass a 805 * `%reducer_list_append` to a converted function that now expects a 806 * pointer or reference to a `reducer<%op_list_append>`, and vice 807 * versa. 808 * 809 * @tparam Type The value type of the list. 810 * @tparam Allocator The allocator type of the list. 811 * 812 * @see op_list_append 813 * @see reducer 814 * @see ReducersList 815 */ 816 template <class Type, class Allocator = std::allocator<Type> > 817 class reducer_list_append : 818 public reducer<op_list_append<Type, Allocator, true> > 819 { 820 typedef reducer<op_list_append<Type, Allocator, true> > base; 821 using base::view; 822 public: 823 824 /// The reducer’s list type. 825 typedef typename base::value_type list_type; 826 827 /// The list’s element type. 828 typedef Type list_value_type; 829 830 /// The reducer’s primitive component type. 831 typedef Type basic_value_type; 832 833 /// The monoid type. 834 typedef typename base::monoid_type Monoid; 835 836 /** @name Constructors 837 */ 838 //@{ 839 840 /** Construct a reducer with an empty list. 841 */ reducer_list_append()842 reducer_list_append() {} 843 844 /** Construct a reducer with a specified initial list value. 845 */ reducer_list_append(const std::list<Type,Allocator> & initial_value)846 reducer_list_append(const std::list<Type, Allocator> &initial_value) : 847 base(initial_value) {} 848 849 //@} 850 851 852 /** @name Forwarded functions 853 * @details Functions that update the contained accumulator variable are 854 * simply forwarded to the contained @ref op_and_view. */ 855 //@{ 856 857 /// @copydoc op_list_append_view::push_back(const Type&) push_back(const Type & element)858 void push_back(const Type& element) { view().push_back(element); } 859 860 //@} 861 862 /** Allow mutable access to the list within the current view. 863 * 864 * @warning If this method is called before the parallel calculation is 865 * complete, the list returned by this method will be a partial 866 * result. 867 * 868 * @returns A mutable reference to the list within the current view. 869 */ get_reference()870 list_type &get_reference() { return view().view_get_reference(); } 871 872 /** Allow read-only access to the list within the current view. 873 * 874 * @warning If this method is called before the parallel calculation is 875 * complete, the list returned by this method will be a partial 876 * result. 877 * 878 * @returns A const reference to the list within the current view. 879 */ get_reference()880 list_type const &get_reference() const { return view().view_get_reference(); } 881 882 /// @name Dereference 883 //@{ 884 /** Dereferencing a wrapper is a no-op. It simply returns the wrapper. 885 * Combined with the rule that a wrapper forwards view operations to the 886 * view, this means that view operations can be written the same way on 887 * reducers and wrappers, which is convenient for incrementally 888 * converting code using wrappers to code using reducers. That is: 889 * 890 * reducer< op_list_append<int> > r; 891 * r->push_back(a); // *r returns the view 892 * // push_back is a view member function 893 * 894 * reducer_list_append<int> w; 895 * w->push_back(a); // *w returns the wrapper 896 * // push_back is a wrapper member function that 897 * // calls the corresponding view function 898 */ 899 //@{ 900 reducer_list_append& operator*() { return *this; } 901 reducer_list_append const& operator*() const { return *this; } 902 903 reducer_list_append* operator->() { return this; } 904 reducer_list_append const* operator->() const { return this; } 905 //@} 906 907 /** @name Upcast 908 * @details In Cilk library 0.9, reducers were always cache-aligned. In 909 * library 1.0, reducer cache alignment is optional. By default, reducers 910 * are unaligned (i.e., just naturally aligned), but legacy wrappers 911 * inherit from cache-aligned reducers for binary compatibility. 912 * 913 * This means that a wrapper will automatically be upcast to its aligned 914 * reducer base class. The following conversion operators provide 915 * pseudo-upcasts to the corresponding unaligned reducer class. 916 */ 917 //@{ 918 operator reducer< op_list_append<Type, Allocator, false> >& () 919 { 920 return *reinterpret_cast< 921 reducer< op_list_append<Type, Allocator, false> >* 922 >(this); 923 } 924 operator const reducer< op_list_append<Type, Allocator, false> >& () const 925 { 926 return *reinterpret_cast< 927 const reducer< op_list_append<Type, Allocator, false> >* 928 >(this); 929 } 930 //@} 931 932 }; 933 934 935 /** Deprecated list prepend reducer wrapper class. 936 * 937 * reducer_list_prepend is the same as 938 * @ref reducer<@ref op_list_prepend>, except that reducer_list_prepend is a 939 * proxy for the contained view, so that accumulator variable update operations 940 * can be applied directly to the reducer. For example, an element is prepended 941 * to a `reducer<op_list_prepend>` with `r->push_back(a)`, but an element is 942 * prepended to a `reducer_list_prepend` with `r.push_back(a)`. 943 * 944 * @deprecated Users are strongly encouraged to use `reducer<monoid>` 945 * reducers rather than the old wrappers like reducer_list_prepend. 946 * The `reducer<monoid>` reducers show the reducer/monoid/view 947 * architecture more clearly, are more consistent in their 948 * implementation, and present a simpler model for new 949 * user-implemented reducers. 950 * 951 * @note Implicit conversions are provided between `%reducer_list_prepend` 952 * and `reducer<%op_list_prepend>`. This allows incremental code 953 * conversion: old code that used `%reducer_list_prepend` can pass a 954 * `%reducer_list_prepend` to a converted function that now expects a 955 * pointer or reference to a `reducer<%op_list_prepend>`, and vice 956 * versa. 957 * 958 * @tparam Type The value type of the list. 959 * @tparam Allocator The allocator type of the list. 960 * 961 * @see op_list_prepend 962 * @see reducer 963 * @see ReducersList 964 */ 965 template <class Type, class Allocator = std::allocator<Type> > 966 class reducer_list_prepend : 967 public reducer<op_list_prepend<Type, Allocator, true> > 968 { 969 typedef reducer<op_list_prepend<Type, Allocator, true> > base; 970 using base::view; 971 public: 972 973 /** The reducer’s list type. 974 */ 975 typedef typename base::value_type list_type; 976 977 /** The list’s element type. 978 */ 979 typedef Type list_value_type; 980 981 /** The reducer’s primitive component type. 982 */ 983 typedef Type basic_value_type; 984 985 /** The monoid type. 986 */ 987 typedef typename base::monoid_type Monoid; 988 989 /** @name Constructors 990 */ 991 //@{ 992 993 /** Construct a reducer with an empty list. 994 */ reducer_list_prepend()995 reducer_list_prepend() {} 996 997 /** Construct a reducer with a specified initial list value. 998 */ reducer_list_prepend(const std::list<Type,Allocator> & initial_value)999 reducer_list_prepend(const std::list<Type, Allocator> &initial_value) : 1000 base(initial_value) {} 1001 1002 //@} 1003 1004 /** @name Forwarded functions 1005 * @details Functions that update the contained accumulator variable are 1006 * simply forwarded to the contained @ref op_and_view. 1007 */ 1008 //@{ 1009 1010 /// @copydoc op_list_prepend_view::push_front(const Type&) push_front(const Type & element)1011 void push_front(const Type& element) { view().push_front(element); } 1012 1013 //@} 1014 1015 /** Allow mutable access to the list within the current view. 1016 * 1017 * @warning If this method is called before the parallel calculation is 1018 * complete, the list returned by this method will be a partial 1019 * result. 1020 * 1021 * @returns A mutable reference to the list within the current view. 1022 */ get_reference()1023 list_type &get_reference() { return view().view_get_reference(); } 1024 1025 /** Allow read-only access to the list within the current view. 1026 * 1027 * @warning If this method is called before the parallel calculation is 1028 * complete, the list returned by this method will be a partial 1029 * result. 1030 * 1031 * @returns A const reference to the list within the current view. 1032 */ get_reference()1033 list_type const &get_reference() const { return view().view_get_reference(); } 1034 1035 /// @name Dereference 1036 /** Dereferencing a wrapper is a no-op. It simply returns the wrapper. 1037 * Combined with the rule that a wrapper forwards view operations to the 1038 * view, this means that view operations can be written the same way on 1039 * reducers and wrappers, which is convenient for incrementally 1040 * converting code using wrappers to code using reducers. That is: 1041 * 1042 * reducer< op_list_prepend<int> > r; 1043 * r->push_front(a); // *r returns the view 1044 * // push_front is a view member function 1045 * 1046 * reducer_list_prepend<int> w; 1047 * w->push_front(a); // *w returns the wrapper 1048 * // push_front is a wrapper member function that 1049 * // calls the corresponding view function 1050 */ 1051 //@{ 1052 reducer_list_prepend& operator*() { return *this; } 1053 reducer_list_prepend const& operator*() const { return *this; } 1054 1055 reducer_list_prepend* operator->() { return this; } 1056 reducer_list_prepend const* operator->() const { return this; } 1057 //@} 1058 1059 /** @name Upcast 1060 * @details In Cilk library 0.9, reducers were always cache-aligned. In 1061 * library 1.0, reducer cache alignment is optional. By default, reducers 1062 * are unaligned (i.e., just naturally aligned), but legacy wrappers 1063 * inherit from cache-aligned reducers for binary compatibility. 1064 * 1065 * This means that a wrapper will automatically be upcast to its aligned 1066 * reducer base class. The following conversion operators provide 1067 * pseudo-upcasts to the corresponding unaligned reducer class. 1068 */ 1069 //@{ 1070 operator reducer< op_list_prepend<Type, Allocator, false> >& () 1071 { 1072 return *reinterpret_cast< 1073 reducer< op_list_prepend<Type, Allocator, false> >* 1074 >(this); 1075 } 1076 operator const reducer< op_list_prepend<Type, Allocator, false> >& () const 1077 { 1078 return *reinterpret_cast< 1079 const reducer< op_list_prepend<Type, Allocator, false> >* 1080 >(this); 1081 } 1082 //@} 1083 1084 }; 1085 1086 /// @cond internal 1087 1088 /** Metafunction specialization for reducer conversion. 1089 * 1090 * This specialization of the @ref legacy_reducer_downcast template class 1091 * defined in reducer.h causes the `reducer< op_list_append<Type, Allocator> >` 1092 * class to have an `operator reducer_list_append<Type, Allocator>& ()` 1093 * conversion operator that statically downcasts the `reducer<op_list_append>` 1094 * to the corresponding `reducer_list_append` type. (The reverse conversion, 1095 * from `reducer_list_append` to `reducer<op_list_append>`, is just an upcast, 1096 * which is provided for free by the language.) 1097 */ 1098 template <class Type, class Allocator, bool Align> 1099 struct legacy_reducer_downcast<reducer<op_list_append<Type, Allocator, Align> > > 1100 { 1101 typedef reducer_list_append<Type, Allocator> type; 1102 }; 1103 1104 /** Metafunction specialization for reducer conversion. 1105 * 1106 * This specialization of the @ref legacy_reducer_downcast template class 1107 * defined in reducer.h causes the 1108 * `reducer< op_list_prepend<Type, Allocator> >` class to have an 1109 * `operator reducer_list_prepend<Type, Allocator>& ()` conversion operator 1110 * that statically downcasts the `reducer<op_list_prepend>` to the 1111 * corresponding `reducer_list_prepend` type. (The reverse conversion, from 1112 * `reducer_list_prepend` to `reducer<op_list_prepend>`, is just an upcast, 1113 * which is provided for free by the language.) 1114 */ 1115 template <class Type, class Allocator, bool Align> 1116 struct legacy_reducer_downcast<reducer<op_list_prepend<Type, Allocator, Align> > > 1117 { 1118 typedef reducer_list_prepend<Type, Allocator> type; 1119 }; 1120 1121 /// @endcond 1122 1123 //@} 1124 1125 } // Close namespace cilk 1126 1127 #endif // REDUCER_LIST_H_INCLUDED 1128