mirror of
https://github.com/autc04/Retro68.git
synced 2024-11-24 07:31:32 +00:00
534 lines
18 KiB
C++
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
|