1 /* reducer_vector.h -*- C++ -*-
3 * Copyright (C) 2009-2016, Intel Corporation
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
16 * * Neither the name of Intel Corporation nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
30 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
33 * *********************************************************************
35 * PLEASE NOTE: This file is a downstream copy of a file mainitained in
36 * a repository at cilkplus.org. Changes made to this file that are not
37 * submitted through the contribution process detailed at
38 * http://www.cilkplus.org/submit-cilk-contribution will be lost the next
39 * time that a new version is released. Changes only submitted to the
40 * GNU compiler collection or posted to the git repository at
41 * https://bitbucket.org/intelcilkruntime/intel-cilk-runtime.git are
44 * We welcome your contributions to this open source project. Thank you
45 * for your assistance in helping us improve Cilk Plus.
48 /** @file reducer_vector.h
50 * @brief Defines classes for doing parallel vector creation by appending.
52 * @ingroup ReducersVector
57 #ifndef REDUCER_VECTOR_H_INCLUDED
58 #define REDUCER_VECTOR_H_INCLUDED
60 #include <cilk/reducer.h>
64 /** @defgroup ReducersVector Vector Reducers
66 * Vector reducers allow the creation of a standard vector by
67 * appending a set of elements in parallel.
71 * You should be familiar with @ref pagereducers "Intel(R) Cilk(TM) Plus reducers",
72 * described in file `reducers.md`, and particularly with @ref reducers_using,
73 * before trying to use the information in this file.
75 * @section redvector_usage Usage Example
77 * typedef ... SourceData;
78 * typedef ... ResultData;
79 * vector<SourceData> input;
80 * ResultData expensive_computation(const SourceData& x);
81 * cilk::reducer< cilk::op_vector<ResultData> > r;
82 * cilk_for (int i = 0; i != input.size(); ++i) {
83 * r->push_back(expensive_computation(input[i]));
88 * @section redvector_monoid The Monoid
90 * @subsection redvector_monoid_values Value Set
92 * The value set of a vector reducer is the set of values of the class
93 * `std::vector<Type, Alloc>`, which we refer to as "the reducer's vector
96 * @subsection redvector_monoid_operator Operator
98 * The operator of a vector reducer is vector concatenation.
100 * @subsection redvector_monoid_identity Identity
102 * The identity value of a vector reducer is the empty vector, which is the
103 * value of the expression `std::vector<Type, Alloc>([allocator])`.
105 * @section redvector_operations Operations
107 * In the operation descriptions below, the type name `Vector` refers to
108 * the reducer's vector type, `std::vector<Type, Alloc>`.
110 * @subsection redvector_constructors Constructors
112 * Any argument list which is valid for a `std::vector` constructor is valid
113 * for a vector reducer constructor. The usual move-in constructor is also
116 * reducer(move_in(Vector& variable))
118 * @subsection redvector_get_set Set and Get
120 * void r.set_value(const Vector& value)
121 * const Vector& = r.get_value() const
122 * void r.move_in(Vector& variable)
123 * void r.move_out(Vector& variable)
125 * @subsection redvector_initial Initial Values
127 * A vector reducer with no constructor arguments, or with only an allocator
128 * argument, will initially contain the identity value, an empty vector.
130 * @subsection redvector_view_ops View Operations
132 * The view of a vector reducer provides the following member functions:
134 * void push_back(const Type& element)
135 * void insert_back(const Type& element)
136 * void insert_back(Vector::size_type n, const Type& element)
137 * template <typename Iter> void insert_back(Iter first, Iter last)
139 * The `push_back` functions is the same as the corresponding `std::vector`
140 * function. The `insert_back` function is the same as the `std::vector`
141 * `insert` function, with the first parameter fixed to the end of the vector.
143 * @section redvector_performance Performance Considerations
145 * Vector reducers work by creating a vector for each view, collecting those
146 * vectors in a list, and then concatenating them into a single result vector
147 * at the end of the computation. This last step takes place in serial code,
148 * and necessarily takes time proportional to the length of the result vector.
149 * Thus, a parallel vector reducer cannot actually speed up the time spent
150 * directly creating the vector. This trivial example would probably be slower
151 * (because of reducer overhead) than the corresponding serial code:
154 * reducer<op_vector<T> > r;
155 * cilk_for (int i = 0; i != a.length(); ++i) {
156 * r->push_back(a[i]);
159 * r.move_out(result);
161 * What a vector reducer _can_ do is to allow the _remainder_ of the
162 * computation to be done in parallel, without having to worry about
163 * managing the vector computation.
165 * The vectors for new views are created (by the view identity constructor)
166 * using the same allocator as the vector that was created when the reducer
167 * was constructed. Note that this allocator is determined when the reducer
168 * is constructed. The following two examples may have very different
171 * vector<Type, Allocator> a_vector;
173 * reducer< op_vector<Type, Allocator> reducer1(move_in(a_vector));
174 * ... parallel computation ...
175 * reducer1.move_out(a_vector);
177 * reducer< op_vector<Type, Allocator> reducer2;
178 * reducer2.move_in(a_vector);
179 * ... parallel computation ...
180 * reducer2.move_out(a_vector);
182 * * `reducer1` will be constructed with the same allocator as `a_vector`,
183 * because the vector was specified in the constructor. The `move_in`
184 * and`move_out` can therefore be done with a `swap` in constant time.
185 * * `reducer2` will be constructed with a _default_ allocator of type
186 * `Allocator`, which may not be the same as the allocator of `a_vector`.
187 * Therefore, the `move_in` and `move_out` may have to be done with a
188 * copy in _O(N)_ time.
190 * (All instances of an allocator class with no internal state (like
191 * `std::allocator`) are "the same". You only need to worry about the "same
192 * allocator" issue when you create vector reducers with a custom allocator
193 * class that has data members.)
195 * @section redvector_types Type and Operator Requirements
197 * `std::vector<Type, Alloc>` must be a valid type.
202 /** @ingroup ReducersVector */
205 /** @brief The vector reducer view class.
207 * This is the view class for reducers created with
208 * `cilk::reducer< cilk::op_vector<Type, Allocator> >`. It holds the
209 * accumulator variable for the reduction, and allows only append operations
210 * to be performed on it.
212 * @note The reducer "dereference" operation (`reducer::operator *()`)
213 * yields a reference to the view. Thus, for example, the view
214 * class's `push_back` operation would be used in an expression like
215 * `r->push_back(a)`, where `r` is a vector reducer variable.
217 * @tparam Type The vector element type (not the vector type).
218 * @tparam Alloc The vector allocator type.
220 * @see @ref ReducersVector
223 template<typename Type
, typename Alloc
>
226 typedef std::vector
<Type
, Alloc
> vector_type
;
227 typedef std::list
<vector_type
, typename
Alloc::template rebind
<vector_type
>::other
>
229 typedef typename
vector_type::size_type size_type
;
231 // The view's value is represented by a list of vectors and a single
232 // vector. The value is the concatenation of the vectors in the list with
233 // the single vector at the end. All vector operations apply to the single
234 // vector; reduce operations cause lists of partial vectors from multiple
235 // strands to be combined.
237 mutable vector_type m_vector
;
238 mutable list_type m_list
;
240 // Before returning the value of the reducer, concatenate all the vectors
241 // in the list with the single vector.
245 if (m_list
.empty()) return;
247 typename
list_type::iterator i
;
249 size_type len
= m_vector
.size();
250 for (i
= m_list
.begin(); i
!= m_list
.end(); ++i
)
253 vector_type
result(get_allocator());
256 for (i
= m_list
.begin(); i
!= m_list
.end(); ++i
)
257 result
.insert(result
.end(), i
->begin(), i
->end());
260 result
.insert(result
.end(), m_vector
.begin(), m_vector
.end());
261 result
.swap(m_vector
);
266 /** @name Monoid support.
270 /// Required by cilk::monoid_with_view
271 typedef vector_type value_type
;
273 /// Required by @ref op_vector
274 Alloc
get_allocator() const
276 return m_vector
.get_allocator();
279 /** Reduces the views of two strands.
281 * This function is invoked by the @ref op_vector monoid to combine
282 * the views of two strands when the right strand merges with the left
283 * one. It appends the value contained in the right-strand view to the
284 * value contained in the left-strand view, and leaves the value in the
285 * right-strand view undefined.
287 * @param other A pointer to the right-strand view. (`this` points to
288 * the left-strand view.)
290 * @note Used only by the @ref op_vector monoid to implement the
291 * monoid reduce operation.
293 void reduce(op_vector_view
* other
)
295 if (!other
->m_vector
.empty() || !other
->m_list
.empty()) {
296 // (list, string) + (other_list, other_string) =>
297 // (list + {string} + other_list, other_string)
298 if (!m_vector
.empty()) {
299 // simulate m_list.push_back(std::move(m_vector))
300 m_list
.push_back(vector_type(get_allocator()));
301 m_list
.back().swap(m_vector
);
303 m_list
.splice(m_list
.end(), other
->m_list
);
304 m_vector
.swap(other
->m_vector
);
310 /** @name Passes constructor arguments to the vector constructor.
315 m_vector(), m_list(get_allocator()) {}
317 template <typename T1
>
318 op_vector_view(const T1
& x1
) :
319 m_vector(x1
), m_list(get_allocator()) {}
321 template <typename T1
, typename T2
>
322 op_vector_view(const T1
& x1
, const T2
& x2
) :
323 m_vector(x1
, x2
), m_list(get_allocator()) {}
325 template <typename T1
, typename T2
, typename T3
>
326 op_vector_view(const T1
& x1
, const T2
& x2
, const T3
& x3
) :
327 m_vector(x1
, x2
, x3
), m_list(get_allocator()) {}
329 template <typename T1
, typename T2
, typename T3
, typename T4
>
330 op_vector_view(const T1
& x1
, const T2
& x2
, const T3
& x3
, const T4
& x4
) :
331 m_vector(x1
, x2
, x3
, x4
), m_list(get_allocator()) {}
335 /** Move-in constructor.
337 explicit op_vector_view(cilk::move_in_wrapper
<value_type
> w
) :
338 m_vector(w
.value().get_allocator()),
339 m_list(w
.value().get_allocator())
341 m_vector
.swap(w
.value());
344 /** @name Reducer support.
348 void view_move_in(vector_type
& v
)
351 if (get_allocator() == v
.get_allocator()) {
352 // Equal allocators. Do a (fast) swap.
356 // Unequal allocators. Do a (slow) copy.
362 void view_move_out(vector_type
& v
)
365 if (get_allocator() == v
.get_allocator()) {
366 // Equal allocators. Do a (fast) swap.
370 // Unequal allocators. Do a (slow) copy.
376 void view_set_value(const vector_type
& v
)
382 vector_type
const& view_get_value() const
388 typedef vector_type
const& return_type_for_get_value
;
392 /** @name View modifier operations.
394 * @details These simply wrap the corresponding operations on the
399 /** Adds an element at the end of the list.
401 * Equivalent to `vector.push_back(…)`
403 void push_back(const Type x
)
405 m_vector
.push_back(x
);
408 /** @name Insert elements at the end of the vector.
410 * Equivalent to `vector.insert(vector.end(), …)`
414 void insert_back(const Type
& element
)
415 { m_vector
.insert(m_vector
.end(), element
); }
417 void insert_back(typename
vector_type::size_type n
, const Type
& element
)
418 { m_vector
.insert(m_vector
.end(), n
, element
); }
420 template <typename Iter
>
421 void insert_back(Iter first
, Iter last
)
422 { m_vector
.insert(m_vector
.end(), first
, last
); }
430 /** @brief The vector append monoid class.
432 * Instantiate the cilk::reducer template class with an op_vector monoid to
433 * create a vector reducer class. For example, to concatenate a
434 * collection of integers:
436 * cilk::reducer< cilk::op_vector<int> > r;
438 * @tparam Type The vector element type (not the vector type).
439 * @tparam Alloc The vector allocator type.
441 * @see ReducersVector
442 * @see op_vector_view
443 * @ingroup ReducersVector
445 template<typename Type
, typename Alloc
= std::allocator
<Type
> >
447 public cilk::monoid_with_view
< op_vector_view
<Type
, Alloc
>, false >
449 typedef cilk::monoid_with_view
< op_vector_view
<Type
, Alloc
>, false > base
;
450 typedef provisional_guard
<typename
base::view_type
> view_guard
;
452 // The allocator to be used when constructing new views.
458 typedef typename
base::view_type view_type
;
462 * There is no default constructor for vector monoids, because the
463 * allocator must always be specified.
465 * @param allocator The list allocator to be used when
466 * identity-constructing new views.
468 op_vector(const Alloc
& allocator
= Alloc()) : m_allocator(allocator
) {}
470 /** Creates an identity view.
472 * Vector view identity constructors take the vector allocator as an
475 * @param v The address of the uninitialized memory in which the view
476 * will be constructed.
478 void identity(view_type
*v
) const
480 ::new((void*) v
) view_type(m_allocator
);
483 /** @name construct functions
485 * A vector append monoid must have a copy of the allocator of
486 * the leftmost view's vector, so that it can use it in the `identity`
487 * operation. This, in turn, requires that vector append monoids have a
488 * specialized `construct()` function.
490 * All vector append monoid `construct()` functions first construct the
491 * leftmost view, using the arguments that were passed in from the reducer
492 * constructor. They then call the view's `get_allocator()` function to
493 * get the vector allocator from the vector in the leftmost view, and pass
494 * that to the monoid constructor.
498 static void construct(op_vector
* monoid
, view_type
* view
)
500 view_guard
vg( new((void*) view
) view_type() );
501 vg
.confirm_if( new((void*) monoid
) op_vector(view
->get_allocator()) );
504 template <typename T1
>
505 static void construct(op_vector
* monoid
, view_type
* view
, const T1
& x1
)
507 view_guard
vg( new((void*) view
) view_type(x1
) );
508 vg
.confirm_if( new((void*) monoid
) op_vector(view
->get_allocator()) );
511 template <typename T1
, typename T2
>
512 static void construct(op_vector
* monoid
, view_type
* view
,
513 const T1
& x1
, const T2
& x2
)
515 view_guard
vg( new((void*) view
) view_type(x1
, x2
) );
516 vg
.confirm_if( new((void*) monoid
) op_vector(view
->get_allocator()) );
519 template <typename T1
, typename T2
, typename T3
>
520 static void construct(op_vector
* monoid
, view_type
* view
,
521 const T1
& x1
, const T2
& x2
, const T3
& x3
)
523 view_guard
vg( new((void*) view
) view_type(x1
, x2
, x3
) );
524 vg
.confirm_if( new((void*) monoid
) op_vector(view
->get_allocator()) );
533 #endif // REDUCER_VECTOR_H_INCLUDED