Base subreg rules on REGMODE_NATURAL_SIZE rather than UNITS_PER_WORD
[official-gcc.git] / libcilkrts / include / cilk / reducer_vector.h
blobfa53eee1d24383ea939a5082576d98ecf4daa7e8
1 /* reducer_vector.h -*- C++ -*-
3 * Copyright (C) 2009-2016, Intel Corporation
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
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
15 * distribution.
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
42 * not tracked.
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
54 * @see ReducersVector
57 #ifndef REDUCER_VECTOR_H_INCLUDED
58 #define REDUCER_VECTOR_H_INCLUDED
60 #include <cilk/reducer.h>
61 #include <vector>
62 #include <list>
64 /** @defgroup ReducersVector Vector Reducers
66 * Vector reducers allow the creation of a standard vector by
67 * appending a set of elements in parallel.
69 * @ingroup Reducers
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]));
84 * }
85 * vector result;
86 * r.move_out(result);
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
94 * type".
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
114 * provided:
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:
153 * vector<T> a;
154 * reducer<op_vector<T> > r;
155 * cilk_for (int i = 0; i != a.length(); ++i) {
156 * r->push_back(a[i]);
158 * vector<T> result;
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
169 * behavior:
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.
200 namespace cilk {
202 /** @ingroup ReducersVector */
203 //@{
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
221 * @see op_vector
223 template<typename Type, typename Alloc>
224 class op_vector_view
226 typedef std::vector<Type, Alloc> vector_type;
227 typedef std::list<vector_type, typename Alloc::template rebind<vector_type>::other>
228 list_type;
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.
243 void flatten() const
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)
251 len += i->size();
253 vector_type result(get_allocator());
254 result.reserve(len);
256 for (i = m_list.begin(); i != m_list.end(); ++i)
257 result.insert(result.end(), i->begin(), i->end());
258 m_list.clear();
260 result.insert(result.end(), m_vector.begin(), m_vector.end());
261 result.swap(m_vector);
264 public:
266 /** @name Monoid support.
268 //@{
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);
308 //@}
310 /** @name Passes constructor arguments to the vector constructor.
312 //@{
314 op_vector_view() :
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()) {}
333 //@}
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.
346 //@{
348 void view_move_in(vector_type& v)
350 m_list.clear();
351 if (get_allocator() == v.get_allocator()) {
352 // Equal allocators. Do a (fast) swap.
353 m_vector.swap(v);
355 else {
356 // Unequal allocators. Do a (slow) copy.
357 m_vector = v;
359 v.clear();
362 void view_move_out(vector_type& v)
364 flatten();
365 if (get_allocator() == v.get_allocator()) {
366 // Equal allocators. Do a (fast) swap.
367 m_vector.swap(v);
369 else {
370 // Unequal allocators. Do a (slow) copy.
371 v = m_vector;
372 m_vector.clear();
376 void view_set_value(const vector_type& v)
378 m_list.clear();
379 m_vector = v;
382 vector_type const& view_get_value() const
384 flatten();
385 return m_vector;
388 typedef vector_type const& return_type_for_get_value;
390 //@}
392 /** @name View modifier operations.
394 * @details These simply wrap the corresponding operations on the
395 * underlying vector.
397 //@{
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(), …)`
412 //@{
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); }
424 //@}
426 //@}
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> >
446 class op_vector :
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.
453 Alloc m_allocator;
455 public:
457 /// View type.
458 typedef typename base::view_type view_type;
460 /** Constructor.
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
473 * argument.
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.
496 //@{
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()) );
527 //@}
531 } // namespace cilk
533 #endif // REDUCER_VECTOR_H_INCLUDED