gcc/libcilkrts/include/cilk/reducer_vector.h

534 lines
18 KiB
C++

/* reducer_vector.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_vector.h
*
* @brief Defines classes for doing parallel vector creation by appending.
*
* @ingroup ReducersVector
*
* @see ReducersVector
*/
#ifndef REDUCER_VECTOR_H_INCLUDED
#define REDUCER_VECTOR_H_INCLUDED
#include <cilk/reducer.h>
#include <vector>
#include <list>
/** @defgroup ReducersVector Vector Reducers
*
* Vector reducers allow the creation of a standard vector by
* appending a set of elements in parallel.
*
* @ingroup Reducers
*
* You should be familiar with @ref pagereducers "Intel(R) Cilk(TM) Plus reducers",
* described in file `reducers.md`, and particularly with @ref reducers_using,
* before trying to use the information in this file.
*
* @section redvector_usage Usage Example
*
* typedef ... SourceData;
* typedef ... ResultData;
* vector<SourceData> input;
* ResultData expensive_computation(const SourceData& x);
* cilk::reducer< cilk::op_vector<ResultData> > r;
* cilk_for (int i = 0; i != input.size(); ++i) {
* r->push_back(expensive_computation(input[i]));
* }
* vector result;
* r.move_out(result);
*
* @section redvector_monoid The Monoid
*
* @subsection redvector_monoid_values Value Set
*
* The value set of a vector reducer is the set of values of the class
* `std::vector<Type, Alloc>`, which we refer to as "the reducer's vector
* type".
*
* @subsection redvector_monoid_operator Operator
*
* The operator of a vector reducer is vector concatenation.
*
* @subsection redvector_monoid_identity Identity
*
* The identity value of a vector reducer is the empty vector, which is the
* value of the expression `std::vector<Type, Alloc>([allocator])`.
*
* @section redvector_operations Operations
*
* In the operation descriptions below, the type name `Vector` refers to
* the reducer's vector type, `std::vector<Type, Alloc>`.
*
* @subsection redvector_constructors Constructors
*
* Any argument list which is valid for a `std::vector` constructor is valid
* for a vector reducer constructor. The usual move-in constructor is also
* provided:
*
* reducer(move_in(Vector& variable))
*
* @subsection redvector_get_set Set and Get
*
* void r.set_value(const Vector& value)
* const Vector& = r.get_value() const
* void r.move_in(Vector& variable)
* void r.move_out(Vector& variable)
*
* @subsection redvector_initial Initial Values
*
* A vector reducer with no constructor arguments, or with only an allocator
* argument, will initially contain the identity value, an empty vector.
*
* @subsection redvector_view_ops View Operations
*
* The view of a vector reducer provides the following member functions:
*
* void push_back(const Type& element)
* void insert_back(const Type& element)
* void insert_back(Vector::size_type n, const Type& element)
* template <typename Iter> void insert_back(Iter first, Iter last)
*
* The `push_back` functions is the same as the corresponding `std::vector`
* function. The `insert_back` function is the same as the `std::vector`
* `insert` function, with the first parameter fixed to the end of the vector.
*
* @section redvector_performance Performance Considerations
*
* Vector reducers work by creating a vector for each view, collecting those
* vectors in a list, and then concatenating them into a single result vector
* at the end of the computation. This last step takes place in serial code,
* and necessarily takes time proportional to the length of the result vector.
* Thus, a parallel vector reducer cannot actually speed up the time spent
* directly creating the vector. This trivial example would probably be slower
* (because of reducer overhead) than the corresponding serial code:
*
* vector<T> a;
* reducer<op_vector<T> > r;
* cilk_for (int i = 0; i != a.length(); ++i) {
* r->push_back(a[i]);
* }
* vector<T> result;
* r.move_out(result);
*
* What a vector reducer _can_ do is to allow the _remainder_ of the
* computation to be done in parallel, without having to worry about
* managing the vector computation.
*
* The vectors for new views are created (by the view identity constructor)
* using the same allocator as the vector that was created when the reducer
* was constructed. Note that this allocator is determined when the reducer
* is constructed. The following two examples may have very different
* behavior:
*
* vector<Type, Allocator> a_vector;
*
* reducer< op_vector<Type, Allocator> reducer1(move_in(a_vector));
* ... parallel computation ...
* reducer1.move_out(a_vector);
*
* reducer< op_vector<Type, Allocator> reducer2;
* reducer2.move_in(a_vector);
* ... parallel computation ...
* reducer2.move_out(a_vector);
*
* * `reducer1` will be constructed with the same allocator as `a_vector`,
* because the vector 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 of type
* `Allocator`, which may not be the same as the allocator of `a_vector`.
* Therefore, the `move_in` and `move_out` may have to be done with a
* copy in _O(N)_ time.
*
* (All instances of an allocator class with no internal state (like
* `std::allocator`) are "the same". You only need to worry about the "same
* allocator" issue when you create vector reducers with a custom allocator
* class that has data members.)
*
* @section redvector_types Type and Operator Requirements
*
* `std::vector<Type, Alloc>` must be a valid type.
*/
namespace cilk {
/** @ingroup ReducersVector */
//@{
/** @brief The vector reducer view class.
*
* This is the view class for reducers created with
* `cilk::reducer< cilk::op_vector<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 vector reducer variable.
*
* @tparam Type The vector element type (not the vector type).
* @tparam Alloc The vector allocator type.
*
* @see @ref ReducersVector
* @see op_vector
*/
template<typename Type, typename Alloc>
class op_vector_view
{
typedef std::vector<Type, Alloc> vector_type;
typedef std::list<vector_type, typename Alloc::template rebind<vector_type>::other>
list_type;
typedef typename vector_type::size_type size_type;
// The view's value is represented by a list of vectors and a single
// vector. The value is the concatenation of the vectors in the list with
// the single vector at the end. All vector operations apply to the single
// vector; reduce operations cause lists of partial vectors from multiple
// strands to be combined.
//
mutable vector_type m_vector;
mutable list_type m_list;
// Before returning the value of the reducer, concatenate all the vectors
// in the list with the single vector.
//
void flatten() const
{
if (m_list.empty()) return;
typename list_type::iterator i;
size_type len = m_vector.size();
for (i = m_list.begin(); i != m_list.end(); ++i)
len += i->size();
vector_type result(get_allocator());
result.reserve(len);
for (i = m_list.begin(); i != m_list.end(); ++i)
result.insert(result.end(), i->begin(), i->end());
m_list.clear();
result.insert(result.end(), m_vector.begin(), m_vector.end());
result.swap(m_vector);
}
public:
/** @name Monoid support.
*/
//@{
/// Required by cilk::monoid_with_view
typedef vector_type value_type;
/// Required by @ref op_vector
Alloc get_allocator() const
{
return m_vector.get_allocator();
}
/** Reduces the views of two strands.
*
* This function is invoked by the @ref op_vector 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 other A pointer to the right-strand view. (`this` points to
* the left-strand view.)
*
* @note Used only by the @ref op_vector monoid to implement the
* monoid reduce operation.
*/
void reduce(op_vector_view* other)
{
if (!other->m_vector.empty() || !other->m_list.empty()) {
// (list, string) + (other_list, other_string) =>
// (list + {string} + other_list, other_string)
if (!m_vector.empty()) {
// simulate m_list.push_back(std::move(m_vector))
m_list.push_back(vector_type(get_allocator()));
m_list.back().swap(m_vector);
}
m_list.splice(m_list.end(), other->m_list);
m_vector.swap(other->m_vector);
}
}
//@}
/** @name Passes constructor arguments to the vector constructor.
*/
//@{
op_vector_view() :
m_vector(), m_list(get_allocator()) {}
template <typename T1>
op_vector_view(const T1& x1) :
m_vector(x1), m_list(get_allocator()) {}
template <typename T1, typename T2>
op_vector_view(const T1& x1, const T2& x2) :
m_vector(x1, x2), m_list(get_allocator()) {}
template <typename T1, typename T2, typename T3>
op_vector_view(const T1& x1, const T2& x2, const T3& x3) :
m_vector(x1, x2, x3), m_list(get_allocator()) {}
template <typename T1, typename T2, typename T3, typename T4>
op_vector_view(const T1& x1, const T2& x2, const T3& x3, const T4& x4) :
m_vector(x1, x2, x3, x4), m_list(get_allocator()) {}
//@}
/** Move-in constructor.
*/
explicit op_vector_view(cilk::move_in_wrapper<value_type> w) :
m_vector(w.value().get_allocator()),
m_list(w.value().get_allocator())
{
m_vector.swap(w.value());
}
/** @name Reducer support.
*/
//@{
void view_move_in(vector_type& v)
{
m_list.clear();
if (get_allocator() == v.get_allocator()) {
// Equal allocators. Do a (fast) swap.
m_vector.swap(v);
}
else {
// Unequal allocators. Do a (slow) copy.
m_vector = v;
}
v.clear();
}
void view_move_out(vector_type& v)
{
flatten();
if (get_allocator() == v.get_allocator()) {
// Equal allocators. Do a (fast) swap.
m_vector.swap(v);
}
else {
// Unequal allocators. Do a (slow) copy.
v = m_vector;
m_vector.clear();
}
}
void view_set_value(const vector_type& v)
{
m_list.clear();
m_vector = v;
}
vector_type const& view_get_value() const
{
flatten();
return m_vector;
}
typedef vector_type const& return_type_for_get_value;
//@}
/** @name View modifier operations.
*
* @details These simply wrap the corresponding operations on the
* underlying vector.
*/
//@{
/** Adds an element at the end of the list.
*
* Equivalent to `vector.push_back(…)`
*/
void push_back(const Type x)
{
m_vector.push_back(x);
}
/** @name Insert elements at the end of the vector.
*
* Equivalent to `vector.insert(vector.end(), …)`
*/
//@{
void insert_back(const Type& element)
{ m_vector.insert(m_vector.end(), element); }
void insert_back(typename vector_type::size_type n, const Type& element)
{ m_vector.insert(m_vector.end(), n, element); }
template <typename Iter>
void insert_back(Iter first, Iter last)
{ m_vector.insert(m_vector.end(), first, last); }
//@}
//@}
};
/** @brief The vector append monoid class.
*
* Instantiate the cilk::reducer template class with an op_vector monoid to
* create a vector reducer class. For example, to concatenate a
* collection of integers:
*
* cilk::reducer< cilk::op_vector<int> > r;
*
* @tparam Type The vector element type (not the vector type).
* @tparam Alloc The vector allocator type.
*
* @see ReducersVector
* @see op_vector_view
* @ingroup ReducersVector
*/
template<typename Type, typename Alloc = std::allocator<Type> >
class op_vector :
public cilk::monoid_with_view< op_vector_view<Type, Alloc>, false >
{
typedef cilk::monoid_with_view< op_vector_view<Type, Alloc>, false > base;
typedef provisional_guard<typename base::view_type> view_guard;
// The allocator to be used when constructing new views.
Alloc m_allocator;
public:
/// View type.
typedef typename base::view_type view_type;
/** Constructor.
*
* There is no default constructor for vector monoids, because the
* allocator must always be specified.
*
* @param allocator The list allocator to be used when
* identity-constructing new views.
*/
op_vector(const Alloc& allocator = Alloc()) : m_allocator(allocator) {}
/** Creates an identity view.
*
* Vector view identity constructors take the vector allocator as an
* argument.
*
* @param v The address of the uninitialized memory in which the view
* will be constructed.
*/
void identity(view_type *v) const
{
::new((void*) v) view_type(m_allocator);
}
/** @name construct functions
*
* A vector append monoid must have a copy of the allocator of
* the leftmost view's vector, so that it can use it in the `identity`
* operation. This, in turn, requires that vector append monoids have a
* specialized `construct()` function.
*
* All vector append monoid `construct()` functions first construct the
* leftmost view, using the arguments that were passed in from the reducer
* constructor. They then call the view's `get_allocator()` function to
* get the vector allocator from the vector in the leftmost view, and pass
* that to the monoid constructor.
*/
//@{
static void construct(op_vector* monoid, view_type* view)
{
view_guard vg( new((void*) view) view_type() );
vg.confirm_if( new((void*) monoid) op_vector(view->get_allocator()) );
}
template <typename T1>
static void construct(op_vector* monoid, view_type* view, const T1& x1)
{
view_guard vg( new((void*) view) view_type(x1) );
vg.confirm_if( new((void*) monoid) op_vector(view->get_allocator()) );
}
template <typename T1, typename T2>
static void construct(op_vector* monoid, view_type* view,
const T1& x1, const T2& x2)
{
view_guard vg( new((void*) view) view_type(x1, x2) );
vg.confirm_if( new((void*) monoid) op_vector(view->get_allocator()) );
}
template <typename T1, typename T2, typename T3>
static void construct(op_vector* monoid, view_type* view,
const T1& x1, const T2& x2, const T3& x3)
{
view_guard vg( new((void*) view) view_type(x1, x2, x3) );
vg.confirm_if( new((void*) monoid) op_vector(view->get_allocator()) );
}
//@}
};
} // namespace cilk
#endif // REDUCER_VECTOR_H_INCLUDED