mirror of
https://github.com/autc04/Retro68.git
synced 2024-12-04 01:50:38 +00:00
1151 lines
41 KiB
C++
1151 lines
41 KiB
C++
/* reducer_list.h -*- C++ -*-
|
|
*
|
|
* Copyright (C) 2009-2016, Intel Corporation
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* * Neither the name of Intel Corporation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
|
|
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* *********************************************************************
|
|
*
|
|
* PLEASE NOTE: This file is a downstream copy of a file mainitained in
|
|
* a repository at cilkplus.org. Changes made to this file that are not
|
|
* submitted through the contribution process detailed at
|
|
* http://www.cilkplus.org/submit-cilk-contribution will be lost the next
|
|
* time that a new version is released. Changes only submitted to the
|
|
* GNU compiler collection or posted to the git repository at
|
|
* https://bitbucket.org/intelcilkruntime/intel-cilk-runtime.git are
|
|
* not tracked.
|
|
*
|
|
* We welcome your contributions to this open source project. Thank you
|
|
* for your assistance in helping us improve Cilk Plus.
|
|
*/
|
|
|
|
/** @file reducer_list.h
|
|
*
|
|
* @brief Defines classes for parallel list creation by appending or
|
|
* prepending reducers.
|
|
*
|
|
* @ingroup ReducersList
|
|
*
|
|
* @see ReducersList
|
|
*/
|
|
|
|
#ifndef REDUCER_LIST_H_INCLUDED
|
|
#define REDUCER_LIST_H_INCLUDED
|
|
|
|
#include <cilk/reducer.h>
|
|
#include <list>
|
|
|
|
/** @defgroup ReducersList List Reducers
|
|
*
|
|
* List-append and list-prepend reducers create standard lists by
|
|
* concatenating a set of lists or values in parallel.
|
|
*
|
|
* @ingroup Reducers
|
|
*
|
|
* You should be familiar with @ref pagereducers "Intel(R) Cilk(TM) Plus reducers"
|
|
* (from file `reducers.md`) and particularly with @ref reducers_using, before
|
|
* trying to use the information in this file.
|
|
*
|
|
* @section redlist_usage Usage Example
|
|
*
|
|
* // Create a list containing the labels of the nodes of a tree in
|
|
* // "inorder" (left subtree, root, right subtree).
|
|
*
|
|
* struct Tree { Tree* left; Tree* right; string label; ... };
|
|
*
|
|
* list<string> x;
|
|
* cilk::reducer< cilk::op_list_append<string> > xr(cilk::move_in(x));
|
|
* collect_labels(tree, xr);
|
|
* xr.move_out(x);
|
|
*
|
|
* void collect_labels(Tree* node,
|
|
* cilk::reducer< cilk::op_list_append<string> >& xr)
|
|
* {
|
|
* if (node) {
|
|
* cilk_spawn collect_labels(node->left, xr);
|
|
* xr->push_back(node->label);
|
|
* collect_labels(node->right, xr);
|
|
* cilk_sync;
|
|
* }
|
|
* }
|
|
*
|
|
* @section redlist_monoid The Monoid
|
|
*
|
|
* @subsection redlist_monoid_values Value Set
|
|
*
|
|
* The __value set__ of a list reducer is the set of values of the class
|
|
* `std::list<Type, Allocator>`, which we refer to as the reducer's _list
|
|
* type_.
|
|
*
|
|
* @subsection redlist_monoid_operator Operator
|
|
*
|
|
* The operator of a list-append reducer is defined as
|
|
*
|
|
* x CAT y == (every element of x, followed by every element of y)
|
|
*
|
|
* The operator of a list-prepend reducer is defined as
|
|
*
|
|
* x RCAT y == (every element of y, followed by every element of x)
|
|
*
|
|
* @subsection redlist_monoid_identity Identity
|
|
*
|
|
* The identity value of a list reducer is the empty list, which is the value
|
|
* of the expression `std::list<Type, Allocator>([allocator])`.
|
|
*
|
|
* @section redlist_operations Operations
|
|
*
|
|
* In the operation descriptions below, the type name `List` refers to the
|
|
* reducer's string type, `std::list<Type, Allocator>`.
|
|
*
|
|
* @subsection redlist_constructors Constructors
|
|
*
|
|
* Any argument list which is valid for a `std::list` constructor is valid for
|
|
* a list reducer constructor. The usual move-in constructor is also provided:
|
|
*
|
|
* reducer(move_in(List& variable))
|
|
*
|
|
* A list reducer with no constructor arguments, or with only an allocator
|
|
* argument, will initially contain the identity value, an empty list.
|
|
*
|
|
* @subsection redlist_get_set Set and Get
|
|
*
|
|
* r.set_value(const List& value)
|
|
* const List& = r.get_value() const
|
|
* r.move_in(List& variable)
|
|
* r.move_out(List& variable)
|
|
*
|
|
* @subsection redlist_view_ops View Operations
|
|
*
|
|
* The view of a list-append reducer provides the following member functions:
|
|
*
|
|
* void push_back(const Type& element)
|
|
* void insert_back(List::size_type n, const Type& element)
|
|
* template <typename Iter> void insert_back(Iter first, Iter last)
|
|
* void splice_back(List& x)
|
|
* void splice_back(List& x, List::iterator i)
|
|
* void splice_back(List& x, List::iterator first, List::iterator last)
|
|
*
|
|
* The view of a list-prepend reducer provides the following member functions:
|
|
*
|
|
* void push_front(const Type& element)
|
|
* void insert_front(List::size_type n, const Type& element)
|
|
* template <typename Iter> void insert_front(Iter first, Iter last)
|
|
* void splice_front(List& x)
|
|
* void splice_front(List& x, List::iterator i)
|
|
* void splice_front(List& x, List::iterator first, List::iterator last)
|
|
*
|
|
* The `push_back` and `push_front` functions are the same as the
|
|
* corresponding `std::list` functions. The `insert_back`, `splice_back`,
|
|
* `insert_front`, and `splice_front` functions are the same as the
|
|
* `std::list` `insert` and `splice` functions, with the first parameter
|
|
* fixed to the end or beginning of the list, respectively.
|
|
*
|
|
* @section redlist_performance Performance Considerations
|
|
*
|
|
* An efficient reducer requires that combining the values of two views (using
|
|
* the view `reduce()` function) be a constant-time operations. Two lists can
|
|
* be merged in constant time using the `splice()` function if they have the
|
|
* same allocator. Therefore, the lists for new views are created (by the view
|
|
* identity constructor) using the same allocator as the list that was created
|
|
* when the reducer was constructed.
|
|
*
|
|
* The performance of adding elements to a list reducer depends on the view
|
|
* operations that are used:
|
|
*
|
|
* * The `push` functions add a single element to the list, and therefore
|
|
* take constant time.
|
|
* * An `insert` function that inserts _N_ elements adds each of them
|
|
* individually, and therefore takes _O(N)_ time.
|
|
* * A `splice` function that inserts _N_ elements just adjusts a couple of
|
|
* pointers, and therefore takes constant time, _if the splice is from a
|
|
* list with the same allocator as the reducer_. Otherwise, it is
|
|
* equivalent to an `insert`, and takes _O(N)_ time.
|
|
*
|
|
* This means that for best performance, if you will be adding elements to a
|
|
* list reducer in batches, you should `splice` them from a list having the
|
|
* same allocator as the reducer.
|
|
*
|
|
* The reducer `move_in` and `move_out` functions do a constant-time `swap` if
|
|
* the variable has the same allocator as the reducer, and a linear-time copy
|
|
* otherwise.
|
|
*
|
|
* Note that the allocator of a list reducer is determined when the reducer is
|
|
* constructed. The following two examples may have very different behavior:
|
|
*
|
|
* list<Element, Allocator> a_list;
|
|
*
|
|
* reducer< list_append<Element, Allocator> reducer1(move_in(a_list));
|
|
* ... parallel computation ...
|
|
* reducer1.move_out(a_list);
|
|
*
|
|
* reducer< list_append<Element, Allocator> reducer2;
|
|
* reducer2.move_in(a_list);
|
|
* ... parallel computation ...
|
|
* reducer2.move_out(a_list);
|
|
*
|
|
* * `reducer1` will be constructed with the same allocator as `a_list`,
|
|
* because the list was specified in the constructor. The `move_in`
|
|
* and `move_out` can therefore be done with a `swap` in constant time.
|
|
* * `reducer2` will be constructed with a _default_ allocator,
|
|
* "`Allocator()`", which may or may not be the same as the allocator of
|
|
* `a_list`. Therefore, the `move_in` and `move_out` may have to be done
|
|
* with a copy in _O(N)_ time.
|
|
*
|
|
* (All instances of an allocator type with no internal state (like
|
|
* `std::allocator`) are "the same". You only need to worry about the "same
|
|
* allocator" issue when you create list reducers with custom allocator types.)
|
|
*
|
|
* @section redlist_types Type and Operator Requirements
|
|
*
|
|
* `std::list<Type, Allocator>` must be a valid type.
|
|
*/
|
|
|
|
|
|
namespace cilk {
|
|
|
|
namespace internal {
|
|
|
|
/** @ingroup ReducersList */
|
|
//@{
|
|
|
|
/** Base class for list-append and prepend view classes.
|
|
*
|
|
* @note This class provides the definitions that are required for a class
|
|
* that will be used as the parameter of a @ref list_monoid_base
|
|
* specialization.
|
|
*
|
|
* @tparam Type The list element type (not the list type).
|
|
* @tparam Allocator The list's allocator class.
|
|
*
|
|
* @see ReducersList
|
|
* @see list_monoid_base
|
|
*/
|
|
template <typename Type, typename Allocator>
|
|
class list_view_base
|
|
{
|
|
protected:
|
|
/// The type of the contained list.
|
|
typedef std::list<Type, Allocator> list_type;
|
|
|
|
/// The list accumulator variable.
|
|
list_type m_value;
|
|
|
|
public:
|
|
|
|
/** @name Monoid support.
|
|
*/
|
|
//@{
|
|
|
|
/// Required by @ref monoid_with_view
|
|
typedef list_type value_type;
|
|
|
|
/// Required by @ref list_monoid_base
|
|
Allocator get_allocator() const
|
|
{
|
|
return m_value.get_allocator();
|
|
}
|
|
|
|
//@}
|
|
|
|
|
|
/** @name Constructors.
|
|
*/
|
|
//@{
|
|
|
|
/// Standard list constructor.
|
|
explicit list_view_base(const Allocator& a = Allocator()) : m_value(a) {}
|
|
explicit list_view_base(
|
|
typename list_type::size_type n,
|
|
const Type& value = Type(),
|
|
const Allocator& a = Allocator() ) : m_value(n, value, a) {}
|
|
template <typename Iter>
|
|
list_view_base(Iter first, Iter last, const Allocator& a = Allocator()) :
|
|
m_value(first, last, a) {}
|
|
list_view_base(const list_type& list) : m_value(list) {}
|
|
|
|
/// Move-in constructor.
|
|
explicit list_view_base(move_in_wrapper<value_type> w)
|
|
: m_value(w.value().get_allocator())
|
|
{
|
|
m_value.swap(w.value());
|
|
}
|
|
|
|
//@}
|
|
|
|
/** @name Reducer support.
|
|
*/
|
|
//@{
|
|
|
|
/// Required by reducer::move_in()
|
|
void view_move_in(value_type& v)
|
|
{
|
|
if (m_value.get_allocator() == v.get_allocator())
|
|
// Equal allocators. Do a (fast) swap.
|
|
m_value.swap(v);
|
|
else
|
|
// Unequal allocators. Do a (slow) copy.
|
|
m_value = v;
|
|
v.clear();
|
|
}
|
|
|
|
/// Required by reducer::move_out()
|
|
void view_move_out(value_type& v)
|
|
{
|
|
if (m_value.get_allocator() == v.get_allocator())
|
|
// Equal allocators. Do a (fast) swap.
|
|
m_value.swap(v);
|
|
else
|
|
// Unequal allocators. Do a (slow) copy.
|
|
v = m_value;
|
|
m_value.clear();
|
|
}
|
|
|
|
/// Required by reducer::set_value()
|
|
void view_set_value(const value_type& v) { m_value = v; }
|
|
|
|
/// Required by reducer::get_value()
|
|
value_type const& view_get_value() const { return m_value; }
|
|
|
|
/// Type returned by view_get_value.
|
|
typedef value_type const& return_type_for_get_value;
|
|
|
|
// Required by legacy wrapper get_reference()
|
|
value_type & view_get_reference() { return m_value; }
|
|
value_type const& view_get_reference() const { return m_value; }
|
|
|
|
//@}
|
|
};
|
|
|
|
|
|
/** Base class for list-append and prepend monoid classes.
|
|
*
|
|
* The key to efficient reducers is that the `identity` operation, which
|
|
* creates a new per-strand view, and the `reduce` operation, which combines
|
|
* two per-strand views, must be constant-time operations. Two lists can be
|
|
* concatenated in constant time only if they have the same allocator.
|
|
* Therefore, all the per-strand list accumulator variables must be created
|
|
* with the same allocator as the leftmost view list.
|
|
*
|
|
* This means that a list reduction monoid must have a copy of the allocator
|
|
* of the leftmost view's list, so that it can use it in the `identity`
|
|
* operation. This, in turn, requires that list reduction monoids have a
|
|
* specialized `construct()` function, which constructs the leftmost view
|
|
* before the monoid, and then passes the leftmost view's allocator to the
|
|
* monoid constructor.
|
|
*
|
|
* @tparam View The list-append or prepend view class.
|
|
* @tparam Align If `false` (the default), reducers instantiated on this
|
|
* monoid will be naturally aligned (the Intel Cilk Plus library 1.0
|
|
* behavior). If `true`, reducers instantiated on this monoid
|
|
* will be cache-aligned for binary compatibility with
|
|
* reducers in Intel Cilk Plus library version 0.9.
|
|
*
|
|
* @see ReducersList
|
|
* @see list_view_base
|
|
*/
|
|
template <typename View, bool Align>
|
|
class list_monoid_base : public monoid_with_view<View, Align>
|
|
{
|
|
typedef typename View::value_type list_type;
|
|
typedef typename list_type::allocator_type allocator_type;
|
|
typedef provisional_guard<View> view_guard;
|
|
|
|
allocator_type m_allocator;
|
|
|
|
public:
|
|
|
|
/** Constructor.
|
|
*
|
|
* There is no default constructor for list monoids, because the allocator
|
|
* must always be specified.
|
|
*
|
|
* @param allocator The list allocator to be used when
|
|
* identity-constructing new views.
|
|
*/
|
|
list_monoid_base(const allocator_type& allocator = allocator_type()) :
|
|
m_allocator(allocator) {}
|
|
|
|
/** Creates an identity view.
|
|
*
|
|
* List view identity constructors take the list allocator as an argument.
|
|
*
|
|
* @param v The address of the uninitialized memory in which the view
|
|
* will be constructed.
|
|
*/
|
|
void identity(View *v) const { ::new((void*) v) View(m_allocator); }
|
|
|
|
/** @name construct functions
|
|
*
|
|
* All `construct()` functions first construct the leftmost view, using
|
|
* the optional @a x1, @a x2, and @a x3 arguments that were passed in from
|
|
* the reducer constructor. They then call the view's `get_allocator()`
|
|
* function to get the list allocator from its contained list, and pass it
|
|
* to the monoid constructor.
|
|
*/
|
|
//@{
|
|
|
|
template <typename Monoid>
|
|
static void construct(Monoid* monoid, View* view)
|
|
{
|
|
view_guard vg( new((void*) view) View() );
|
|
vg.confirm_if( new((void*) monoid) Monoid(view->get_allocator()) );
|
|
}
|
|
|
|
template <typename Monoid, typename T1>
|
|
static void construct(Monoid* monoid, View* view, const T1& x1)
|
|
{
|
|
view_guard vg( new((void*) view) View(x1) );
|
|
vg.confirm_if( new((void*) monoid) Monoid(view->get_allocator()) );
|
|
}
|
|
|
|
template <typename Monoid, typename T1, typename T2>
|
|
static void construct(Monoid* monoid, View* view,
|
|
const T1& x1, const T2& x2)
|
|
{
|
|
view_guard vg( new((void*) view) View(x1, x2) );
|
|
vg.confirm_if( new((void*) monoid) Monoid(view->get_allocator()) );
|
|
}
|
|
|
|
template <typename Monoid, typename T1, typename T2, typename T3>
|
|
static void construct(Monoid* monoid, View* view,
|
|
const T1& x1, const T2& x2, const T3& x3)
|
|
{
|
|
view_guard vg( new((void*) view) View(x1, x2, x3) );
|
|
vg.confirm_if( new((void*) monoid) Monoid(view->get_allocator()) );
|
|
}
|
|
|
|
//@}
|
|
};
|
|
|
|
//@}
|
|
|
|
} // namespace internal
|
|
|
|
|
|
/** @ingroup ReducersList */
|
|
//@{
|
|
|
|
/** The list-append reducer view class.
|
|
*
|
|
* This is the view class for reducers created with
|
|
* `cilk::reducer< cilk::op_list_append<Type, Allocator> >`. It holds the
|
|
* accumulator variable for the reduction, and allows only append operations
|
|
* to be performed on it.
|
|
*
|
|
* @note The reducer "dereference" operation (`reducer::operator *()`)
|
|
* yields a reference to the view. Thus, for example, the view class's
|
|
* `push_back` operation would be used in an expression like
|
|
* `r->push_back(a)`, where `r` is a list-append reducer variable.
|
|
*
|
|
* @tparam Type The list element type (not the list type).
|
|
* @tparam Allocator The list allocator type.
|
|
*
|
|
* @see ReducersList
|
|
* @see op_list_append
|
|
*/
|
|
template <class Type,
|
|
class Allocator = typename std::list<Type>::allocator_type>
|
|
class op_list_append_view : public internal::list_view_base<Type, Allocator>
|
|
{
|
|
typedef internal::list_view_base<Type, Allocator> base;
|
|
typedef std::list<Type, Allocator> list_type;
|
|
typedef typename list_type::iterator iterator;
|
|
|
|
iterator end() { return this->m_value.end(); }
|
|
|
|
public:
|
|
|
|
/** @name Constructors.
|
|
*
|
|
* All op_list_append_view constructors simply pass their arguments on to
|
|
* the @ref internal::list_view_base base class constructor.
|
|
*
|
|
* @ref internal::list_view_base supports all the std::list constructor
|
|
* forms, as well as the reducer move_in constructor form.
|
|
*/
|
|
//@{
|
|
|
|
op_list_append_view() : base() {}
|
|
|
|
template <typename T1>
|
|
op_list_append_view(const T1& x1) : base(x1) {}
|
|
|
|
template <typename T1, typename T2>
|
|
op_list_append_view(const T1& x1, const T2& x2) : base(x1, x2) {}
|
|
|
|
template <typename T1, typename T2, typename T3>
|
|
op_list_append_view(const T1& x1, const T2& x2, const T3& x3) :
|
|
base(x1, x2, x3) {}
|
|
|
|
//@}
|
|
|
|
/** @name View modifier operations.
|
|
*/
|
|
//@{
|
|
|
|
/** Adds an element at the end of the list.
|
|
*
|
|
* This is equivalent to `list.push_back(element)`
|
|
*/
|
|
void push_back(const Type& element)
|
|
{ this->m_value.push_back(element); }
|
|
|
|
/** Inserts elements at the end of the list.
|
|
*
|
|
* This is equivalent to `list.insert(list.end(), n, element)`
|
|
*/
|
|
void insert_back(typename list_type::size_type n, const Type& element)
|
|
{ this->m_value.insert(end(), n, element); }
|
|
|
|
/** Inserts elements at the end of the list.
|
|
*
|
|
* This is equivalent to `list.insert(list.end(), first, last)`
|
|
*/
|
|
template <typename Iter>
|
|
void insert_back(Iter first, Iter last)
|
|
{ this->m_value.insert(end(), first, last); }
|
|
|
|
/** Splices elements at the end of the list.
|
|
*
|
|
* This is equivalent to `list.splice(list.end(), x)`
|
|
*/
|
|
void splice_back(list_type& x) {
|
|
if (x.get_allocator() == this->m_value.get_allocator())
|
|
this->m_value.splice(end(), x);
|
|
else {
|
|
insert_back(x.begin(), x.end());
|
|
x.clear();
|
|
}
|
|
}
|
|
|
|
/** Splices elements at the end of the list.
|
|
*
|
|
* This is equivalent to `list.splice(list.end(), x, i)`
|
|
*/
|
|
void splice_back(list_type& x, iterator i) {
|
|
if (x.get_allocator() == this->m_value.get_allocator())
|
|
this->m_value.splice(end(), x, i);
|
|
else {
|
|
push_back(*i);
|
|
x.erase(i);
|
|
}
|
|
}
|
|
|
|
/** Splices elements at the end of the list.
|
|
*
|
|
* This is equivalent to `list.splice(list.end(), x, first, last)`
|
|
*/
|
|
void splice_back(list_type& x, iterator first, iterator last) {
|
|
if (x.get_allocator() == this->m_value.get_allocator())
|
|
this->m_value.splice(end(), x, first, last);
|
|
else {
|
|
insert_back(first, last);
|
|
x.erase(first, last);
|
|
}
|
|
}
|
|
|
|
//@}
|
|
|
|
/** Reduces the views of two strands.
|
|
*
|
|
* This function is invoked by the @ref op_list_append monoid to combine
|
|
* the views of two strands when the right strand merges with the left
|
|
* one. It appends the value contained in the right-strand view to the
|
|
* value contained in the left-strand view, and leaves the value in the
|
|
* right-strand view undefined.
|
|
*
|
|
* @param right A pointer to the right-strand view. (`this` points to
|
|
* the left-strand view.)
|
|
*
|
|
* @note Used only by the @ref op_list_append monoid to implement the
|
|
* monoid reduce operation.
|
|
*/
|
|
void reduce(op_list_append_view* right)
|
|
{
|
|
__CILKRTS_ASSERT(
|
|
this->m_value.get_allocator() == right->m_value.get_allocator());
|
|
this->m_value.splice(end(), right->m_value);
|
|
}
|
|
};
|
|
|
|
|
|
/** The list-prepend reducer view class.
|
|
*
|
|
* This is the view class for reducers created with
|
|
* `cilk::reducer< cilk::op_list_prepend<Type, Allocator> >`. It holds the
|
|
* accumulator variable for the reduction, and allows only prepend operations
|
|
* to be performed on it.
|
|
*
|
|
* @note The reducer "dereference" operation (`reducer::operator *()`)
|
|
* yields a reference to the view. Thus, for example, the view class's
|
|
* `push_front` operation would be used in an expression like
|
|
* `r->push_front(a)`, where `r` is a list-prepend reducer variable.
|
|
*
|
|
* @tparam Type The list element type (not the list type).
|
|
* @tparam Allocator The list allocator type.
|
|
*
|
|
* @see ReducersList
|
|
* @see op_list_prepend
|
|
*/
|
|
template <class Type,
|
|
class Allocator = typename std::list<Type>::allocator_type>
|
|
class op_list_prepend_view : public internal::list_view_base<Type, Allocator>
|
|
{
|
|
typedef internal::list_view_base<Type, Allocator> base;
|
|
typedef std::list<Type, Allocator> list_type;
|
|
typedef typename list_type::iterator iterator;
|
|
|
|
iterator begin() { return this->m_value.begin(); }
|
|
|
|
public:
|
|
|
|
/** @name Constructors.
|
|
*
|
|
* All op_list_prepend_view constructors simply pass their arguments on to
|
|
* the @ref internal::list_view_base base class constructor.
|
|
*
|
|
* @ref internal::list_view_base supports all the std::list constructor
|
|
* forms, as well as the reducer move_in constructor form.
|
|
*
|
|
*/
|
|
//@{
|
|
|
|
op_list_prepend_view() : base() {}
|
|
|
|
template <typename T1>
|
|
op_list_prepend_view(const T1& x1) : base(x1) {}
|
|
|
|
template <typename T1, typename T2>
|
|
op_list_prepend_view(const T1& x1, const T2& x2) : base(x1, x2) {}
|
|
|
|
template <typename T1, typename T2, typename T3>
|
|
op_list_prepend_view(const T1& x1, const T2& x2, const T3& x3) :
|
|
base(x1, x2, x3) {}
|
|
|
|
//@}
|
|
|
|
/** @name View modifier operations.
|
|
*/
|
|
//@{
|
|
|
|
/** Adds an element at the beginning of the list.
|
|
*
|
|
* This is equivalent to `list.push_front(element)`
|
|
*/
|
|
void push_front(const Type& element)
|
|
{ this->m_value.push_front(element); }
|
|
|
|
/** Inserts elements at the beginning of the list.
|
|
*
|
|
* This is equivalent to `list.insert(list.begin(), n, element)`
|
|
*/
|
|
void insert_front(typename list_type::size_type n, const Type& element)
|
|
{ this->m_value.insert(begin(), n, element); }
|
|
|
|
/** Inserts elements at the beginning of the list.
|
|
*
|
|
* This is equivalent to `list.insert(list.begin(), first, last)`
|
|
*/
|
|
template <typename Iter>
|
|
void insert_front(Iter first, Iter last)
|
|
{ this->m_value.insert(begin(), first, last); }
|
|
|
|
/** Splices elements at the beginning of the list.
|
|
*
|
|
* This is equivalent to `list.splice(list.begin(), x)`
|
|
*/
|
|
void splice_front(list_type& x) {
|
|
if (x.get_allocator() == this->m_value.get_allocator())
|
|
this->m_value.splice(begin(), x);
|
|
else {
|
|
insert_front(x.begin(), x.begin());
|
|
x.clear();
|
|
}
|
|
}
|
|
|
|
/** Splices elements at the beginning of the list.
|
|
*
|
|
* This is equivalent to `list.splice(list.begin(), x, i)`
|
|
*/
|
|
void splice_front(list_type& x, iterator i) {
|
|
if (x.get_allocator() == this->m_value.get_allocator())
|
|
this->m_value.splice(begin(), x, i);
|
|
else {
|
|
push_front(*i);
|
|
x.erase(i);
|
|
}
|
|
}
|
|
|
|
/** Splices elements at the beginning of the list.
|
|
*
|
|
* This is equivalent to `list.splice(list.begin(), x, first, last)`
|
|
*/
|
|
void splice_front(list_type& x, iterator first, iterator last) {
|
|
if (x.get_allocator() == this->m_value.get_allocator())
|
|
this->m_value.splice(begin(), x, first, last);
|
|
else {
|
|
insert_front(first, last);
|
|
x.erase(first, last);
|
|
}
|
|
}
|
|
|
|
//@}
|
|
|
|
/** Reduces the views of two strands.
|
|
*
|
|
* This function is invoked by the @ref op_list_prepend monoid to combine
|
|
* the views of two strands when the right strand merges with the left
|
|
* one. It prepends the value contained in the right-strand view to the
|
|
* value contained in the left-strand view, and leaves the value in the
|
|
* right-strand view undefined.
|
|
*
|
|
* @param right A pointer to the right-strand view. (`this` points to
|
|
* the left-strand view.)
|
|
*
|
|
* @note Used only by the @ref op_list_prepend monoid to implement the
|
|
* monoid reduce operation.
|
|
*/
|
|
/** Reduce operation.
|
|
*
|
|
* Required by @ref monoid_base.
|
|
*/
|
|
void reduce(op_list_prepend_view* right)
|
|
{
|
|
__CILKRTS_ASSERT(
|
|
this->m_value.get_allocator() == right->m_value.get_allocator());
|
|
this->m_value.splice(begin(), right->m_value);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/** Monoid class for list-append reductions. Instantiate the cilk::reducer
|
|
* template class with a op_list_append monoid to create a list-append reducer
|
|
* class. For example, to create a list of strings:
|
|
*
|
|
* cilk::reducer< cilk::op_list_append<std::string> > r;
|
|
*
|
|
* @tparam Type The list element type (not the list type).
|
|
* @tparam Alloc The list allocator type.
|
|
* @tparam Align If `false` (the default), reducers instantiated on this
|
|
* monoid will be naturally aligned (the Intel Cilk Plus library 1.0
|
|
* behavior). If `true`, reducers instantiated on this monoid
|
|
* will be cache-aligned for binary compatibility with
|
|
* reducers in Intel Cilk Plus library version 0.9.
|
|
*
|
|
* @see ReducersList
|
|
* @see op_list_append_view
|
|
*/
|
|
template <typename Type,
|
|
typename Allocator = typename std::list<Type>::allocator_type,
|
|
bool Align = false>
|
|
struct op_list_append :
|
|
public internal::list_monoid_base<op_list_append_view<Type, Allocator>, Align>
|
|
{
|
|
/// Construct with default allocator.
|
|
op_list_append() {}
|
|
/// Construct with specified allocator.
|
|
op_list_append(const Allocator& alloc) :
|
|
internal::list_monoid_base<op_list_append_view<Type, Allocator>, Align>(alloc) {}
|
|
};
|
|
|
|
/** Monoid class for list-prepend reductions. Instantiate the cilk::reducer
|
|
* template class with a op_list_prepend monoid to create a list-prepend
|
|
* reducer class. For example, to create a list of strings:
|
|
*
|
|
* cilk::reducer< cilk::op_list_prepend<std::string> > r;
|
|
*
|
|
* @tparam Type The list element type (not the list type).
|
|
* @tparam Alloc The list allocator type.
|
|
* @tparam Align If `false` (the default), reducers instantiated on this
|
|
* monoid will be naturally aligned (the Intel Cilk Plus library 1.0
|
|
* behavior). If `true`, reducers instantiated on this monoid
|
|
* will be cache-aligned for binary compatibility with
|
|
* reducers in Intel Cilk Plus library version 0.9.
|
|
*
|
|
* @see ReducersList
|
|
* @see op_list_prepend_view
|
|
*/
|
|
template <typename Type,
|
|
typename Allocator = typename std::list<Type>::allocator_type,
|
|
bool Align = false>
|
|
struct op_list_prepend :
|
|
public internal::list_monoid_base<op_list_prepend_view<Type, Allocator>, Align>
|
|
{
|
|
/// Construct with default allocator.
|
|
op_list_prepend() {}
|
|
/// Construct with specified allocator.
|
|
op_list_prepend(const Allocator& alloc) :
|
|
internal::list_monoid_base<op_list_prepend_view<Type, Allocator>, Align>(alloc) {}
|
|
};
|
|
|
|
|
|
/** Deprecated list-append reducer wrapper class.
|
|
*
|
|
* reducer_list_append is the same as
|
|
* @ref reducer<@ref op_list_append>, except that reducer_list_append is a
|
|
* proxy for the contained view, so that accumulator variable update
|
|
* operations can be applied directly to the reducer. For example, an element
|
|
* is appended to a `reducer<%op_list_append>` with `r->push_back(a)`, but an
|
|
* element can be appended to a `%reducer_list_append` with `r.push_back(a)`.
|
|
*
|
|
* @deprecated Users are strongly encouraged to use `reducer<monoid>`
|
|
* reducers rather than the old wrappers like reducer_list_append.
|
|
* The `reducer<monoid>` reducers show the reducer/monoid/view
|
|
* architecture more clearly, are more consistent in their
|
|
* implementation, and present a simpler model for new
|
|
* user-implemented reducers.
|
|
*
|
|
* @note Implicit conversions are provided between `%reducer_list_append`
|
|
* and `reducer<%op_list_append>`. This allows incremental code
|
|
* conversion: old code that used `%reducer_list_append` can pass a
|
|
* `%reducer_list_append` to a converted function that now expects a
|
|
* pointer or reference to a `reducer<%op_list_append>`, and vice
|
|
* versa.
|
|
*
|
|
* @tparam Type The value type of the list.
|
|
* @tparam Allocator The allocator type of the list.
|
|
*
|
|
* @see op_list_append
|
|
* @see reducer
|
|
* @see ReducersList
|
|
*/
|
|
template <class Type, class Allocator = std::allocator<Type> >
|
|
class reducer_list_append :
|
|
public reducer<op_list_append<Type, Allocator, true> >
|
|
{
|
|
typedef reducer<op_list_append<Type, Allocator, true> > base;
|
|
using base::view;
|
|
public:
|
|
|
|
/// The reducer's list type.
|
|
typedef typename base::value_type list_type;
|
|
|
|
/// The list's element type.
|
|
typedef Type list_value_type;
|
|
|
|
/// The reducer's primitive component type.
|
|
typedef Type basic_value_type;
|
|
|
|
/// The monoid type.
|
|
typedef typename base::monoid_type Monoid;
|
|
|
|
/** @name Constructors
|
|
*/
|
|
//@{
|
|
|
|
/** Constructs a reducer with an empty list.
|
|
*/
|
|
reducer_list_append() {}
|
|
|
|
/** Constructs a reducer with a specified initial list value.
|
|
*/
|
|
reducer_list_append(const std::list<Type, Allocator> &initial_value) :
|
|
base(initial_value) {}
|
|
|
|
//@}
|
|
|
|
|
|
/** @name Forwarded functions
|
|
* @details Functions that update the contained accumulator variable are
|
|
* simply forwarded to the contained @ref op_and_view. */
|
|
//@{
|
|
|
|
/// @copydoc op_list_append_view::push_back(const Type&)
|
|
void push_back(const Type& element) { view().push_back(element); }
|
|
|
|
//@}
|
|
|
|
/** Allows mutable access to the list within the current view.
|
|
*
|
|
* @warning If this method is called before the parallel calculation is
|
|
* complete, the list returned by this method will be a partial
|
|
* result.
|
|
*
|
|
* @returns A mutable reference to the list within the current view.
|
|
*/
|
|
list_type &get_reference() { return view().view_get_reference(); }
|
|
|
|
/** Allows read-only access to the list within the current view.
|
|
*
|
|
* @warning If this method is called before the parallel calculation is
|
|
* complete, the list returned by this method will be a partial
|
|
* result.
|
|
*
|
|
* @returns A const reference to the list within the current view.
|
|
*/
|
|
list_type const &get_reference() const { return view().view_get_reference(); }
|
|
|
|
/// @name Dereference
|
|
//@{
|
|
/** Dereferencing a wrapper is a no-op. It simply returns the wrapper.
|
|
* Combined with the rule that a wrapper forwards view operations to the
|
|
* view, this means that view operations can be written the same way on
|
|
* reducers and wrappers, which is convenient for incrementally
|
|
* converting code using wrappers to code using reducers. That is:
|
|
*
|
|
* reducer< op_list_append<int> > r;
|
|
* r->push_back(a); // *r returns the view
|
|
* // push_back is a view member function
|
|
*
|
|
* reducer_list_append<int> w;
|
|
* w->push_back(a); // *w returns the wrapper
|
|
* // push_back is a wrapper member function that
|
|
* // calls the corresponding view function
|
|
*/
|
|
//@{
|
|
reducer_list_append& operator*() { return *this; }
|
|
reducer_list_append const& operator*() const { return *this; }
|
|
|
|
reducer_list_append* operator->() { return this; }
|
|
reducer_list_append const* operator->() const { return this; }
|
|
//@}
|
|
|
|
/** @name Upcast
|
|
* @details In Intel Cilk Plus library 0.9, reducers were always cache-aligned.
|
|
* In library 1.0, reducer cache alignment is optional. By default,
|
|
* reducers are unaligned (i.e., just naturally aligned), but legacy
|
|
* wrappers inherit from cache-aligned reducers for binary compatibility.
|
|
*
|
|
* This means that a wrapper will automatically be upcast to its aligned
|
|
* reducer base class. The following conversion operators provide
|
|
* pseudo-upcasts to the corresponding unaligned reducer class.
|
|
*/
|
|
//@{
|
|
operator reducer< op_list_append<Type, Allocator, false> >& ()
|
|
{
|
|
return *reinterpret_cast<
|
|
reducer< op_list_append<Type, Allocator, false> >*
|
|
>(this);
|
|
}
|
|
operator const reducer< op_list_append<Type, Allocator, false> >& () const
|
|
{
|
|
return *reinterpret_cast<
|
|
const reducer< op_list_append<Type, Allocator, false> >*
|
|
>(this);
|
|
}
|
|
//@}
|
|
|
|
};
|
|
|
|
|
|
/** Deprecated list-prepend reducer wrapper class.
|
|
*
|
|
* reducer_list_prepend is the same as
|
|
* @ref reducer<@ref op_list_prepend>, except that reducer_list_prepend is a
|
|
* proxy for the contained view, so that accumulator variable update operations
|
|
* can be applied directly to the reducer. For example, an element is prepended
|
|
* to a `reducer<op_list_prepend>` with `r->push_back(a)`, but an element is
|
|
* prepended to a `reducer_list_prepend` with `r.push_back(a)`.
|
|
*
|
|
* @deprecated Users are strongly encouraged to use `reducer<monoid>`
|
|
* reducers rather than the old wrappers like reducer_list_prepend.
|
|
* The `reducer<monoid>` reducers show the reducer/monoid/view
|
|
* architecture more clearly, are more consistent in their
|
|
* implementation, and present a simpler model for new
|
|
* user-implemented reducers.
|
|
*
|
|
* @note Implicit conversions are provided between `%reducer_list_prepend`
|
|
* and `reducer<%op_list_prepend>`. This allows incremental code
|
|
* conversion: old code that used `%reducer_list_prepend` can pass a
|
|
* `%reducer_list_prepend` to a converted function that now expects a
|
|
* pointer or reference to a `reducer<%op_list_prepend>`, and vice
|
|
* versa.
|
|
*
|
|
* @tparam Type The value type of the list.
|
|
* @tparam Allocator The allocator type of the list.
|
|
*
|
|
* @see op_list_prepend
|
|
* @see reducer
|
|
* @see ReducersList
|
|
*/
|
|
template <class Type, class Allocator = std::allocator<Type> >
|
|
class reducer_list_prepend :
|
|
public reducer<op_list_prepend<Type, Allocator, true> >
|
|
{
|
|
typedef reducer<op_list_prepend<Type, Allocator, true> > base;
|
|
using base::view;
|
|
public:
|
|
|
|
/** The reducer's list type.
|
|
*/
|
|
typedef typename base::value_type list_type;
|
|
|
|
/** The list's element type.
|
|
*/
|
|
typedef Type list_value_type;
|
|
|
|
/** The reducer's primitive component type.
|
|
*/
|
|
typedef Type basic_value_type;
|
|
|
|
/** The monoid type.
|
|
*/
|
|
typedef typename base::monoid_type Monoid;
|
|
|
|
/** @name Constructors
|
|
*/
|
|
//@{
|
|
|
|
/** Constructs a reducer with an empty list.
|
|
*/
|
|
reducer_list_prepend() {}
|
|
|
|
/** Constructs a reducer with a specified initial list value.
|
|
*/
|
|
reducer_list_prepend(const std::list<Type, Allocator> &initial_value) :
|
|
base(initial_value) {}
|
|
|
|
//@}
|
|
|
|
/** @name Forwarded functions
|
|
* @details Functions that update the contained accumulator variable are
|
|
* simply forwarded to the contained @ref op_and_view.
|
|
*/
|
|
//@{
|
|
|
|
/// @copydoc op_list_prepend_view::push_front(const Type&)
|
|
void push_front(const Type& element) { view().push_front(element); }
|
|
|
|
//@}
|
|
|
|
/** Allows mutable access to the list within the current view.
|
|
*
|
|
* @warning If this method is called before the parallel calculation is
|
|
* complete, the list returned by this method will be a partial
|
|
* result.
|
|
*
|
|
* @returns A mutable reference to the list within the current view.
|
|
*/
|
|
list_type &get_reference() { return view().view_get_reference(); }
|
|
|
|
/** Allows read-only access to the list within the current view.
|
|
*
|
|
* @warning If this method is called before the parallel calculation is
|
|
* complete, the list returned by this method will be a partial
|
|
* result.
|
|
*
|
|
* @returns A const reference to the list within the current view.
|
|
*/
|
|
list_type const &get_reference() const { return view().view_get_reference(); }
|
|
|
|
/// @name Dereference
|
|
/** Dereferencing a wrapper is a no-op. It simply returns the wrapper.
|
|
* Combined with the rule that a wrapper forwards view operations to the
|
|
* view, this means that view operations can be written the same way on
|
|
* reducers and wrappers, which is convenient for incrementally
|
|
* converting code using wrappers to code using reducers. That is:
|
|
*
|
|
* reducer< op_list_prepend<int> > r;
|
|
* r->push_front(a); // *r returns the view
|
|
* // push_front is a view member function
|
|
*
|
|
* reducer_list_prepend<int> w;
|
|
* w->push_front(a); // *w returns the wrapper
|
|
* // push_front is a wrapper member function that
|
|
* // calls the corresponding view function
|
|
*/
|
|
//@{
|
|
reducer_list_prepend& operator*() { return *this; }
|
|
reducer_list_prepend const& operator*() const { return *this; }
|
|
|
|
reducer_list_prepend* operator->() { return this; }
|
|
reducer_list_prepend const* operator->() const { return this; }
|
|
//@}
|
|
|
|
/** @name Upcast
|
|
* @details In Intel Cilk Plus library 0.9, reducers were always cache-aligned.
|
|
* In library 1.0, reducer cache alignment is optional. By default,
|
|
* reducers are unaligned (i.e., just naturally aligned), but legacy
|
|
* wrappers inherit from cache-aligned reducers for binary compatibility.
|
|
*
|
|
* This means that a wrapper will automatically be upcast to its aligned
|
|
* reducer base class. The following conversion operators provide
|
|
* pseudo-upcasts to the corresponding unaligned reducer class.
|
|
*/
|
|
//@{
|
|
operator reducer< op_list_prepend<Type, Allocator, false> >& ()
|
|
{
|
|
return *reinterpret_cast<
|
|
reducer< op_list_prepend<Type, Allocator, false> >*
|
|
>(this);
|
|
}
|
|
operator const reducer< op_list_prepend<Type, Allocator, false> >& () const
|
|
{
|
|
return *reinterpret_cast<
|
|
const reducer< op_list_prepend<Type, Allocator, false> >*
|
|
>(this);
|
|
}
|
|
//@}
|
|
|
|
};
|
|
|
|
/// @cond internal
|
|
|
|
/** Metafunction specialization for reducer conversion.
|
|
*
|
|
* This specialization of the @ref legacy_reducer_downcast template class
|
|
* defined in reducer.h causes the `reducer< op_list_append<Type, Allocator> >`
|
|
* class to have an `operator reducer_list_append<Type, Allocator>& ()`
|
|
* conversion operator that statically downcasts the `reducer<op_list_append>`
|
|
* to the corresponding `reducer_list_append` type. (The reverse conversion,
|
|
* from `reducer_list_append` to `reducer<op_list_append>`, is just an upcast,
|
|
* which is provided for free by the language.)
|
|
*/
|
|
template <class Type, class Allocator, bool Align>
|
|
struct legacy_reducer_downcast<reducer<op_list_append<Type, Allocator, Align> > >
|
|
{
|
|
typedef reducer_list_append<Type, Allocator> type;
|
|
};
|
|
|
|
/** Metafunction specialization for reducer conversion.
|
|
*
|
|
* This specialization of the @ref legacy_reducer_downcast template class
|
|
* defined in reducer.h causes the
|
|
* `reducer< op_list_prepend<Type, Allocator> >` class to have an
|
|
* `operator reducer_list_prepend<Type, Allocator>& ()` conversion operator
|
|
* that statically downcasts the `reducer<op_list_prepend>` to the
|
|
* corresponding `reducer_list_prepend` type. (The reverse conversion, from
|
|
* `reducer_list_prepend` to `reducer<op_list_prepend>`, is just an upcast,
|
|
* which is provided for free by the language.)
|
|
*/
|
|
template <class Type, class Allocator, bool Align>
|
|
struct legacy_reducer_downcast<reducer<op_list_prepend<Type, Allocator, Align> > >
|
|
{
|
|
typedef reducer_list_prepend<Type, Allocator> type;
|
|
};
|
|
|
|
/// @endcond
|
|
|
|
//@}
|
|
|
|
} // Close namespace cilk
|
|
|
|
#endif // REDUCER_LIST_H_INCLUDED
|