| /* reducer.h -*- C++ -*- |
| * |
| * @copyright |
| * Copyright (C) 2009-2013, Intel Corporation |
| * All rights reserved. |
| * |
| * @copyright |
| * 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. |
| * |
| * @copyright |
| * 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. |
| */ |
| |
| /** @file reducer.h |
| * |
| * @brief Defines foundation classes for creating Cilk reducers. |
| * |
| * @ingroup Reducers |
| * |
| * @see @ref pagereducers |
| * |
| * @defgroup Reducers Reducers |
| */ |
| |
| #ifndef REDUCER_H_INCLUDED |
| #define REDUCER_H_INCLUDED |
| |
| #include "cilk/hyperobject_base.h" |
| #include "cilk/metaprogramming.h" |
| |
| #ifdef __cplusplus |
| |
| //===================== C++ interfaces =================================== |
| |
| #include <new> |
| |
| namespace cilk { |
| |
| /** Base class for defining monoids. |
| * |
| * The monoid_base class template is useful for creating classes that model |
| * the monoid concept. It provides the core type and memory management |
| * functionality. A subclass of monoid_base need only declare and implement |
| * the `identity` and `reduce` functions. |
| * |
| * The monoid_base class also manages the integration between the monoid, the |
| * reducer class that is based on it, and an optional view class which wraps |
| * value objects and restricts access to their operations. |
| * |
| * @tparam Value The value type for the monoid. |
| * @tparam View An optional view class that serves as a proxy for the value |
| * type. |
| * |
| * @see monoid_with_view |
| */ |
| template <typename Value, typename View = Value> |
| class monoid_base |
| { |
| protected: |
| |
| /** Class for provisionally constructed objects. |
| * |
| * The monoid_base::construct() functions manually construct both a monoid |
| * and a view. If one of these is constructed successfully, and the |
| * construction of the other (or some other initialization) fails, then |
| * the first one must be destroyed to avoid a memory leak. Because the |
| * construction is explicit, the destruction must be explicit, too. |
| * |
| * A provisional_guard object wraps a pointer to a newly constructed |
| * object. A call to its confirm() function confirms that the object is |
| * really going to be used. If the guard is destroyed without being |
| * confirmed, then the pointed-to object is destroyed (but not |
| * deallocated). |
| * |
| * Expected usage: |
| * |
| * provisional_guard<T1> x1_provisional( new (x1) T1() ); |
| * … more initialization … |
| * x1_provisional.confirm(); |
| * |
| * or |
| * |
| * provisional_guard<T1> x1_provisional( new (x1) T1() ); |
| * x1_provisional.confirm_if( new (x2) T2() ); |
| * |
| * If an exception is thrown in the “more initialization” code in the |
| * first example, or in the `T2` constructor in the second example, then |
| * `x1_provisional` will not be confirmed, so when its destructor is |
| * called during exception unwinding, the `T1` object that was constructed |
| * in `x1` will be destroyed. |
| * |
| * @see provisional() |
| * |
| * @tparam Type The type of the provisionally constructed object. |
| */ |
| template <typename Type> |
| class provisional_guard { |
| Type* m_ptr; |
| |
| public: |
| |
| /** Constructor. Creates a guard for a provisionally constructed object. |
| * |
| * @param ptr A pointer to the provisionally constructed object. |
| */ |
| provisional_guard(Type* ptr) : m_ptr(ptr) {} |
| |
| /** Destructor. Destroy the object pointed to by the contained pointer |
| * if it has not been confirmed. |
| */ |
| ~provisional_guard() { if (m_ptr) m_ptr->~Type(); } |
| |
| /** Confirm the provisional construction. Do *not* delete the contained |
| * pointer when the guard is destroyed. |
| */ |
| void confirm() { m_ptr = 0; } |
| |
| /** Confirm provisional construction if argument is non-null. Note that |
| * if an exception is thrown during evaluation of the argument |
| * expression, then this function will not be called, and the |
| * provisional object will not be confirmed. This allows the usage: |
| * |
| * x1_provisional.confirm_if( new (x2) T2() ); |
| * |
| * @param cond An arbitrary pointer. The provisional object will be |
| * confirmed if @a cond is not null. |
| * |
| * @returns The value of the @a cond argument. |
| */ |
| template <typename Cond> |
| Cond* confirm_if(Cond* cond) { if (cond) m_ptr = 0; return cond; } |
| }; |
| |
| |
| /** Create a provisional_guard object. This function allows simpler code |
| * when the only use of a provisional_guard is in a |
| * provisional_guard::confirm_if() call immediately following its |
| * creation. Instead of |
| * |
| * provisional_guard<T>guard( new (ptr_to_T) T() ); |
| * guard.confirm_if( new (ptr_to_U) U() ); |
| * |
| * you can just write |
| * |
| * provisional( new (ptr_to_T) T() ).confirm_if( new (ptr_to_U) U() ); |
| * |
| * @tparam Type The type of the provisionally constructed object. |
| * |
| * @param ptr A pointer to a provisionally constructed object. |
| * |
| * @returns A @ref provisional_guard object that guards the |
| * provisionally constructed object pointed to by @a ptr. |
| */ |
| template <typename Type> |
| static provisional_guard<Type> provisional(Type* ptr) |
| { return provisional_guard<Type>(ptr); } |
| |
| public: |
| |
| /** Value type of the monoid. |
| */ |
| typedef Value value_type; |
| |
| /** View type of the monoid. Defaults to be the same as the value type. |
| * @see monoid_with_view |
| */ |
| typedef View view_type; |
| |
| enum { |
| /** Should reducers created with this monoid be aligned? |
| * |
| * @details |
| * “Aligned” means that the view is allocated at a cache-line aligned |
| * offset in the reducer, and the reducer must be cache-line aligned. |
| * “Unaligned” means that the reducer as a whole is just naturally |
| * aligned, but it contains a large enough block of uninitialized |
| * storage for a cache-line aligned view to be allocated in it at |
| * reducer construction time. |
| * |
| * Since the standard heap allocator (new reducer) does not allocate |
| * cache-line aligned storage, only unaligned reducers can be safely |
| * allocated on the heap. |
| * |
| * Default is false (unaligned) unless overridden in a subclass. |
| * |
| * @since 1.02 |
| * (In Cilk library versions 1.0 and 1.01, the default was true. |
| * In Cilk library versions prior to 1.0, reducers were always aligned, |
| * and this data member did not exist.) |
| */ |
| align_reducer = false |
| }; |
| |
| /** Destroy a view. Destroys (without deallocating) the @a View object |
| * pointed to by @a p. |
| * |
| * @param p The address of the @a View object to be destroyed. |
| */ |
| void destroy(view_type* p) const { p->~view_type(); } |
| |
| /** Allocate raw memory. Allocate @a s bytes of memory with no |
| * initialization. |
| * |
| * @param s The number of bytes of memory to allocate. |
| * @return An untyped pointer to the allocated memory. |
| */ |
| void* allocate(size_t s) const { return operator new(s); } |
| |
| /** Deallocate raw memory. Deallocates the memory pointed to by @a p |
| * without doing any destruction. |
| * |
| * @param p Pointer to the memory to be deallocated. |
| * |
| * @pre @a p points to a block of memory that was allocated by a |
| * call to allocate(). |
| */ |
| void deallocate(void* p) const { operator delete(p); } |
| |
| /** Create the identity value. Constructs (without allocating) a @a View |
| * object representing the default value of the @a Value type. |
| * |
| * @param p A pointer to a block of raw memory large enough to hold a |
| * @a View object. |
| * |
| * @post The memory pointed to by @a p contains a @a View object that |
| * represents the default value of the @a View type. |
| * |
| * @deprecated This function constructs the @a View object with its default |
| * constructor, which will often, but not always, yield the |
| * appropriate identity value. Monoid classes should declare |
| * their identity function explicitly, rather than relying on |
| * this default definition. |
| */ |
| void identity(View* p) const { new ((void*) p) View(); } |
| |
| |
| /** @name Construct the monoid and the view with arbitrary arguments. |
| * |
| * A @ref reducer object contains monoid and view data members, which are |
| * declared as raw storage (byte arrays), so that they are not implicitly |
| * constructed when the reducer is constructed. Instead, a reducer |
| * constructor calls one of the monoid class’s static construct() |
| * functions with the addresses of the monoid and the view, and the |
| * construct() function uses placement `new` to construct them. |
| * |
| * This allows the monoid to determine the order in which the monoid and |
| * view are constructed, and to make one of them dependent on the other. |
| * |
| * Any arguments to the reducer constructor are just passed on as |
| * additional arguments to the construct() function (after the monoid |
| * and view addresses). |
| * |
| * Any monoid whose needs are satisfied by the suite of construct() |
| * functions below, such as @ref monoid_with_view, can just inherit them |
| * from monoid_base. Other monoids will need to provide their own versions |
| * to override the monoid_base functions. |
| */ |
| //@{ |
| |
| /** Default-construct the monoid, and pass zero to five const reference |
| * arguments to the view constructor. |
| */ |
| //@{ |
| |
| template <typename Monoid> |
| static void construct(Monoid* monoid, View* view) |
| { provisional( new ((void*)monoid) Monoid() ).confirm_if( |
| (monoid->identity(view), view) ); } |
| |
| template <typename Monoid, typename T1> |
| static void construct(Monoid* monoid, View* view, const T1& x1) |
| { provisional( new ((void*)monoid) Monoid() ).confirm_if( |
| new ((void*)view) View(x1) ); } |
| |
| template <typename Monoid, typename T1, typename T2> |
| static void construct(Monoid* monoid, View* view, |
| const T1& x1, const T2& x2) |
| { provisional( new ((void*)monoid) Monoid() ).confirm_if( |
| new ((void*)view) View(x1, x2) ); } |
| |
| 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) |
| { provisional( new ((void*)monoid) Monoid() ).confirm_if( |
| new ((void*)view) View(x1, x2, x3) ); } |
| |
| template <typename Monoid, typename T1, typename T2, typename T3, |
| typename T4> |
| static void construct(Monoid* monoid, View* view, |
| const T1& x1, const T2& x2, const T3& x3, |
| const T4& x4) |
| { provisional( new ((void*)monoid) Monoid() ).confirm_if( |
| new ((void*)view) View(x1, x2, x3, x4) ); } |
| |
| template <typename Monoid, typename T1, typename T2, typename T3, |
| typename T4, typename T5> |
| static void construct(Monoid* monoid, View* view, |
| const T1& x1, const T2& x2, const T3& x3, |
| const T4& x4, const T5& x5) |
| { provisional( new ((void*)monoid) Monoid() ).confirm_if( |
| new ((void*)view) View(x1, x2, x3, x4, x5) ); } |
| |
| //@} |
| |
| /** Default-construct the monoid, and pass one non-const reference argument |
| * to the view constructor. |
| */ |
| //@{ |
| template <typename Monoid, typename T1> |
| static void construct(Monoid* monoid, View* view, T1& x1) |
| { provisional( new ((void*)monoid) Monoid() ).confirm_if( |
| new ((void*)view) View(x1) ); } |
| //@} |
| |
| /** Copy-construct the monoid, and pass zero to four const reference |
| * arguments to the view constructor. |
| */ |
| //@{ |
| |
| template <typename Monoid> |
| static void construct(Monoid* monoid, View* view, const Monoid& m) |
| { provisional( new ((void*)monoid) Monoid(m) ).confirm_if( |
| new ((void*)view) View() ); } |
| |
| template <typename Monoid, typename T1> |
| static void construct(Monoid* monoid, View* view, const Monoid& m, |
| const T1& x1) |
| { provisional( new ((void*)monoid) Monoid(m) ).confirm_if( |
| new ((void*)view) View(x1) ); } |
| |
| template <typename Monoid, typename T1, typename T2> |
| static void construct(Monoid* monoid, View* view, const Monoid& m, |
| const T1& x1, const T2& x2) |
| { provisional( new ((void*)monoid) Monoid(m) ).confirm_if( |
| new ((void*)view) View(x1, x2) ); } |
| |
| template <typename Monoid, typename T1, typename T2, typename T3> |
| static void construct(Monoid* monoid, View* view, const Monoid& m, |
| const T1& x1, const T2& x2, const T3& x3) |
| { |
| provisional( new ((void*)monoid) Monoid(m) ).confirm_if( |
| new ((void*)view) View(x1, x2, x3) ); |
| } |
| |
| template <typename Monoid, typename T1, typename T2, typename T3, |
| typename T4> |
| static void construct(Monoid* monoid, View* view, const Monoid& m, |
| const T1& x1, const T2& x2, const T3& x3, |
| const T4& x4) |
| { |
| provisional( new ((void*)monoid) Monoid(m) ).confirm_if( |
| new ((void*)view) View(x1, x2, x3, x4) ); |
| } |
| |
| //@} |
| |
| //@} |
| }; |
| |
| |
| /** Monoid class that gets its value type and identity and reduce operations |
| * from its view. |
| * |
| * A simple implementation of the monoid-view-reducer architecture would |
| * distribute knowledge about the type and operations for the reduction |
| * between the monoid and the view — the identity and reduction operations are |
| * specified in the monoid, the reduction operations are implemented in the |
| * view, and the value type is specified in both the monoid and the view. |
| * This is inelegant. |
| * |
| * monoid_with_view is a subclass of @ref monoid_base that gets its value type |
| * and its identity and reduction operations from its view class. No |
| * customization of the monoid_with_view class itself is needed beyond |
| * instantiating it with an appropriate view class. (Customized subclasses of |
| * monoid_with_view may be needed for other reasons, such as to keep some |
| * state for the reducer.) All of the Cilk predefined reducers use |
| * monoid_with_view or one of its subclasses. |
| * |
| * The view class `View` of a monoid_with_view must provide the following public definitions: |
| * |
| * Definition | Meaning |
| * ---------------------------------|-------- |
| * `value_type` | a typedef of the value type for the reduction |
| * `View()` | a default constructor which constructs the identity value for the reduction |
| * `void reduce(const View* other)` | a member function which applies the reduction operation to the values of `this` view and the `other` view, leaving the result as the value of `this` view, and leaving the value of the `other` view undefined (but valid) |
| * |
| * @tparam View The view class for the monoid. |
| * @tparam Align If true, reducers instantiated on this monoid will be |
| * cache-aligned. By default, library reducers (unlike legacy |
| * library reducer _wrappers_) are aligned only as required by |
| * contents. |
| */ |
| template <class View, bool Align = false> |
| class monoid_with_view : public monoid_base<typename View::value_type, View> |
| { |
| public: |
| /** Should reducers created with this monoid be aligned? |
| */ |
| enum { align_reducer = Align }; |
| |
| /** Create the identity value. |
| * |
| * Implements the monoid `identity` operation by using the @a View class’s |
| * default constructor. |
| * |
| * @param p A pointer to a block of raw memory large enough to hold a |
| * @p View object. |
| */ |
| void identity(View* p) const { new ((void*)p) View(); } |
| |
| /** Reduce the values of two views. |
| * |
| * Implements the monoid `reduce` operation by calling the left view’s |
| * `%reduce()` function with the right view as an operand. |
| * |
| * @param left The left operand of the reduce operation. |
| * @param right The right operand of the reduce operation. |
| * @post The left view contains the result of the reduce |
| * operation, and the right view is undefined. |
| */ |
| void reduce(View* left, View* right) const { left->reduce(right); } |
| }; |
| |
| |
| /** Base class for simple views with (usually) scalar values. |
| * |
| * The scalar_view class is intended as a base class which provides about half |
| * of the required definitions for simple views. It defines the `value_type` |
| * required by a @ref monoid_with_view (but not the identity constructor and |
| * reduce operation, which are inherently specific to a particular kind of |
| * reduction). It also defines the value access functions which will be called |
| * by the corresponding @ref reducer functions. (It uses copy semantics for |
| * the view_move_in() and view_move_out() functions, which is appropriate |
| * for simple scalar types, but not necessarily for more complex types like |
| * STL containers. |
| * |
| * @tparam Type The type of value wrapped by the view. |
| */ |
| template <typename Type> |
| class scalar_view |
| { |
| protected: |
| Type m_value; ///< The wrapped accumulator variable. |
| |
| public: |
| /** Value type definition required by @ref monoid_with_view. |
| */ |
| typedef Type value_type; |
| |
| /** Default constructor. |
| */ |
| scalar_view() : m_value() {} |
| |
| /** Value constructor. |
| */ |
| scalar_view(const Type& v) : m_value(v) {} |
| |
| /** @name Value functions required by the reducer class. |
| * |
| * Note that the move in/out functions use simple assignment semantics. |
| */ |
| //@{ |
| |
| /** Set the value of the view. |
| */ |
| void view_move_in(Type& v) { m_value = v; } |
| |
| /** Get the value of the view. |
| */ |
| void view_move_out(Type& v) { v = m_value; } |
| |
| /** Set the value of the view. |
| */ |
| void view_set_value(const Type& v) { m_value = v; } |
| |
| /** Get the value of the view. |
| */ |
| Type const& view_get_value() const { return m_value; } |
| |
| /** Get a reference to the value contained in the view. For legacy |
| * reducer support only. |
| */ |
| Type & view_get_reference() { return m_value; } |
| |
| /** Get a reference to the value contained in the view. For legacy |
| * reducer support only. |
| */ |
| Type const& view_get_reference() const { return m_value; } |
| //@} |
| }; |
| |
| |
| /** Wrapper class for move-in construction. |
| * |
| * Some types allow their values to be _moved_ as an alternative to copying. |
| * Moving a value may be much faster than copying it, but may leave the value |
| * of the move’s source undefined. Consider the `swap` operation provided by |
| * many STL container classes: |
| * |
| * list<T> x, y; |
| * x = y; // Copy |
| * x.swap(y); // Move |
| * |
| * The assignment _copies_ the value of `y` into `x` in time linear in the |
| * size of `y`, leaving `y` unchanged. The `swap` _moves_ the value of `y` |
| * into `x` in constant time, but it also moves the value of `x` into `y`, |
| * potentially leaving `y` undefined. |
| * |
| * A move_in_wrapper simply wraps a pointer to an object. It is created by a |
| * call to cilk::move_in(). Passing a move_in_wrapper to a view constructor |
| * (actually, passing it to a reducer constructor, which passes it to the |
| * monoid `construct()` function, which passes it to the view constructor) |
| * allows, but does not require, the value pointed to by the wrapper to be |
| * moved into the view instead of copied. |
| * |
| * A view class exercises this option by defining a _move-in constructor_, |
| * i.e., a constructor with a move_in_wrapper parameter. The constructor calls |
| * the wrapper’s `value()` function to get a reference to its pointed-to |
| * value, and can then use that reference in a move operation. |
| * |
| * A move_in_wrapper also has an implicit conversion to its pointed-to value, |
| * so if a view class does not define a move-in constructor, its ordinary |
| * value constructor will be called with the wrapped value. For example, an |
| * @ref ReducersAdd "op_add" view does not have a move-in constructor, so |
| * |
| * int x; |
| * reducer< op_add<int> > xr(move_in(x)); |
| * |
| * will simply call the `op_add_view(const int &)` constructor. But an |
| * @ref ReducersList "op_list_append" view does have a move-in constructor, |
| * so |
| * |
| * list<int> x; |
| * reducer< op_list_append<int> > xr(move_in(x)); |
| * |
| * will call the `op_list_append_view(move_in_wrapper<int>)` constructor, |
| * which can `swap` the value of `x` into the view. |
| * |
| * @note Remember that passing the value of a variable to a reducer |
| * constructor using a move_in_wrapper leaves the variable undefined. |
| * You cannot assume that the constructor either will or will not copy |
| * or move the value. |
| * |
| * @tparam Type The type of the wrapped value. |
| * |
| * @see cilk::move_in() |
| */ |
| template <typename Type> |
| class move_in_wrapper |
| { |
| Type *m_pointer; |
| public: |
| |
| /** Constructor that captures the address of its argument. This is almost |
| * always called from the @ref move_in function. |
| */ |
| explicit move_in_wrapper(Type& ref) : m_pointer(&ref) { } |
| |
| /** Implicit conversion to the wrapped value. This allows a move_in_wrapper |
| * to be used where a value of the wrapped type is expected, in which case |
| * the wrapper is completely transparent. |
| */ |
| operator Type&() const { return *m_pointer; } |
| |
| /** Get a reference to the pointed-to value. This has the same effect as |
| * the implicit conversion, but makes the intent clearer in a move-in |
| * constructor. |
| */ |
| Type& value() const { return *m_pointer; } |
| }; |
| |
| /** Function to create a move_in_wrapper for a value. |
| * |
| * @tparam Type The type of the argument, which will be the `type` of the |
| * created wrapper. |
| * |
| * @see move_in_wrapper |
| */ |
| template <typename Type> |
| inline |
| move_in_wrapper<Type> move_in(Type& ref) |
| { return move_in_wrapper<Type>(ref); } |
| |
| |
| /** @copydoc move_in(Type&) |
| * |
| * @note Applying a function that is explicitly specified as modifying its |
| * argument to a const argument is obviously an irrational thing to |
| * do. This move_in() variant is just provided to allow calling a |
| * move-in constructor with a function return value, which the |
| * language treats as a const. Using it for any other purpose will |
| * probably end in tears. |
| */ |
| template <typename Type> |
| inline |
| move_in_wrapper<Type> move_in(const Type& ref) |
| { return move_in_wrapper<Type>(ref); } |
| |
| |
| /** Wrapper class to allow implicit downcasts to reducer subclasses. |
| * |
| * The Cilk library contains a collection of reducer wrapper classes which |
| * were created before the `cilk::reducer<Monoid>` style was developed. For |
| * example, `cilk::reducer_opadd<Type>` provided essentially the same |
| * functionality that is now provided by |
| * `cilk::reducer< cilk::op_add<Type> >`. These legacy reducer classes are |
| * deprecated, but still supported, and they have been reimplemented as |
| * subclasses of the corresponding `cilk::reducer` classes. For example: |
| * |
| * template <class T> |
| * reducer_opadd<T> : public reducer< op_add<T> > { ... }; |
| * |
| * This reimplementation allows transparent conversion between legacy and |
| * new reducers. That is, a `reducer<op_add>*` or `reducer<op_add>&` can be |
| * used anywhere that a `reducer_opadd*` or `reducer_opadd&` is expected, |
| * and vice versa. |
| * |
| * The conversion from the legacy reducer to the new reducer is just an |
| * up-cast, which is provided for free by C++. The conversion from the new |
| * reducer to the legacy reducer is a down-cast, though, which requires an |
| * explicit conversion member function in the `reducer` class. The challenge |
| * is to define a function in the reducer template class which will convert |
| * each cilk::reducer specialization to the corresponding legacy reducer, |
| * if there is one. |
| * |
| * The trick is in the legacy_reducer_downcast template class, which provides |
| * a mapping from `cilk::reducer` specializations to legacy reducer classes. |
| * `reducer<Monoid>` has a conversion function to convert itself to |
| * `legacy_reducer_downcast< reducer<Monoid> >::%type`. By default, |
| * `legacy_reducer_downcast<Reducer>::%type` is just a trivial subclass of |
| * `Reducer`, which is uninteresting, but a reducer with a legacy counterpart |
| * will have a specialization of `legacy_reducer_downcast` whose `type` is |
| * the corresponding legacy reducer. For example: |
| * |
| * template <typename Type> |
| * struct legacy_reducer_downcast< reducer< op_add<Type> > > |
| * { |
| * typedef reducer_opadd<Type> type; |
| * }; |
| * |
| * |
| * @tparam Reducer The new-style reducer class whose corresponding legacy reducer class |
| * is `type`, if there is such a legacy reducer class. |
| */ |
| template <typename Reducer> |
| struct legacy_reducer_downcast |
| { |
| /** The related legacy reducer class. |
| * |
| * By default, this is just a trivial subclass of Reducer, but it can be |
| * overridden in the specialization of legacy_reducer_downcast for |
| * a reducer that has a corresponding legacy reducers. |
| */ |
| struct type : Reducer { }; |
| }; |
| |
| |
| namespace internal { |
| /// @cond internal |
| |
| template <typename Value, typename View> |
| struct reducer_set_get |
| { |
| static View theView; // Declared but not defined |
| |
| // sizeof(notchar) is guaranteed larger than 1 |
| struct notchar { char x[2]; }; |
| |
| // check_for_ref returns char if 'get_value' returns by value and notchar |
| // if 'get_value' returns by reference. |
| static char check_for_ref(Value, ...); |
| static notchar check_for_ref(Value&, int); |
| |
| enum { GET_VALUE_BY_VALUE = |
| (1 == sizeof(check_for_ref(theView.view_get_value(), 0))) } ; |
| |
| typedef typename condition<GET_VALUE_BY_VALUE, |
| Value, const Value&>::type get_value_type; |
| |
| static void move_in(View& view, Value& v) { view.view_move_in(v); } |
| static void move_out(View& view, Value& v) { view.view_move_out(v); } |
| |
| static void set_value(View& view, const Value& v) |
| { view.view_set_value(v); } |
| |
| static get_value_type get_value(const View& view) |
| { return view.view_get_value(); } |
| }; |
| |
| template <typename Value> |
| struct reducer_set_get<Value, Value> |
| { |
| typedef const Value& get_value_type; |
| |
| static void move_in(Value& view, Value& v) { view = v; } |
| static void move_out(Value& view, Value& v) { v = view; } |
| |
| static void set_value(Value& view, const Value& v) { view = v; } |
| |
| static get_value_type get_value(const Value& view) { return view; } |
| }; |
| |
| /// @endcond |
| |
| |
| /** Base class defining the data layout that is common to all reducers. |
| */ |
| template <typename Monoid> |
| class reducer_base { |
| typedef typename Monoid::view_type view_type; |
| |
| // This makes the reducer a hyper-object. (Partially initialized in |
| // the derived reducer_content class.) |
| // |
| __cilkrts_hyperobject_base m_base; |
| |
| // The monoid is allocated here as raw bytes, and is constructed explicitly |
| // by a call to the monoid_type::construct() function in the constructor of |
| // the `reducer` subclass. |
| // |
| storage_for_object<Monoid> m_monoid; |
| |
| // Used for sanity checking at destruction. |
| // |
| void* m_initialThis; |
| |
| // The leftmost view comes next. It is defined in the derived |
| // reducer_content class. |
| |
| /** @name C-callable wrappers for the C++-coded monoid dispatch functions. |
| */ |
| //@{ |
| |
| static void reduce_wrapper(void* r, void* lhs, void* rhs); |
| static void identity_wrapper(void* r, void* view); |
| static void destroy_wrapper(void* r, void* view); |
| static void* allocate_wrapper(void* r, __STDNS size_t bytes); |
| static void deallocate_wrapper(void* r, void* view); |
| |
| //@} |
| |
| protected: |
| |
| /** Constructor. |
| * |
| * @param leftmost The address of the leftmost view in the reducer. |
| */ |
| reducer_base(char* leftmost) |
| { |
| static const cilk_c_monoid c_monoid_initializer = { |
| (cilk_c_reducer_reduce_fn_t) &reduce_wrapper, |
| (cilk_c_reducer_identity_fn_t) &identity_wrapper, |
| (cilk_c_reducer_destroy_fn_t) &destroy_wrapper, |
| (cilk_c_reducer_allocate_fn_t) &allocate_wrapper, |
| (cilk_c_reducer_deallocate_fn_t) &deallocate_wrapper |
| }; |
| |
| m_base.__c_monoid = c_monoid_initializer; |
| m_base.__flags = 0; |
| m_base.__view_offset = (char*)leftmost - (char*)this; |
| m_base.__view_size = sizeof(view_type); |
| m_initialThis = this; |
| |
| __cilkrts_hyper_create(&m_base); |
| } |
| |
| /** Destructor. |
| */ |
| __CILKRTS_STRAND_STALE(~reducer_base()) |
| { |
| // Make sure we haven't been memcopy'd or corrupted |
| __CILKRTS_ASSERT( |
| this == m_initialThis || |
| // Allow for a layout bug that may put the initialThis field one |
| // word later in 1.0 reducers than in 0.9 and 1.1 reducers. |
| this == *(&m_initialThis + 1) |
| ); |
| __cilkrts_hyper_destroy(&m_base); |
| } |
| |
| /** Monoid data member. |
| * |
| * @return A pointer to the reducer’s monoid data member. |
| */ |
| Monoid* monoid_ptr() { return &m_monoid.object(); } |
| |
| /** Leftmost view data member. |
| * |
| * @return A pointer to the reducer’s leftmost view data member. |
| * |
| * @note This function returns the address of the *leftmost* view, |
| * which is unique for the lifetime of the reducer. It is |
| * intended to be used in constructors and destructors. |
| * Use the reducer::view() function to access the per-strand |
| * view instance. |
| */ |
| view_type* leftmost_ptr() |
| { |
| char* view_addr = (char*)this + m_base.__view_offset; |
| return reinterpret_cast<view_type*>(view_addr); |
| } |
| |
| public: |
| |
| /** @name Access the current view. |
| * |
| * These functions return a reference to the instance of the reducer’s |
| * view that was created for the current strand of a parallel computation |
| * (and create it if it doesn’t already exist). Note the difference from |
| * the (private) leftmost_ptr() function, which returns a pointer to the |
| * _leftmost_ view, which is the same in all strands. |
| */ |
| //@{ |
| |
| /** Per-strand view instance. |
| * |
| * @return A reference to the per-strand view instance. |
| */ |
| view_type& view() |
| { |
| return *static_cast<view_type *>(__cilkrts_hyper_lookup(&m_base)); |
| } |
| |
| /** @copydoc view() |
| */ |
| const view_type& view() const |
| { |
| return const_cast<reducer_base*>(this)->view(); |
| } |
| |
| //@} |
| |
| /** Initial view pointer field. |
| * |
| * @internal |
| * |
| * @return a reference to the m_initialThis field. |
| * |
| * @note This function is provided for “white-box” testing of the |
| * reducer layout code. There is never any reason for user code |
| * to call it. |
| */ |
| const void* const & initial_this() const { return m_initialThis; } |
| }; |
| |
| template <typename Monoid> |
| void reducer_base<Monoid>::reduce_wrapper(void* r, void* lhs, void* rhs) |
| { |
| Monoid* monoid = static_cast<reducer_base*>(r)->monoid_ptr(); |
| monoid->reduce(static_cast<view_type*>(lhs), |
| static_cast<view_type*>(rhs)); |
| } |
| |
| template <typename Monoid> |
| void reducer_base<Monoid>::identity_wrapper(void* r, void* view) |
| { |
| Monoid* monoid = static_cast<reducer_base*>(r)->monoid_ptr(); |
| monoid->identity(static_cast<view_type*>(view)); |
| } |
| |
| template <typename Monoid> |
| void reducer_base<Monoid>::destroy_wrapper(void* r, void* view) |
| { |
| Monoid* monoid = static_cast<reducer_base*>(r)->monoid_ptr(); |
| monoid->destroy(static_cast<view_type*>(view)); |
| } |
| |
| template <typename Monoid> |
| void* reducer_base<Monoid>::allocate_wrapper(void* r, __STDNS size_t bytes) |
| { |
| Monoid* monoid = static_cast<reducer_base*>(r)->monoid_ptr(); |
| return monoid->allocate(bytes); |
| } |
| |
| template <typename Monoid> |
| void reducer_base<Monoid>::deallocate_wrapper(void* r, void* view) |
| { |
| Monoid* monoid = static_cast<reducer_base*>(r)->monoid_ptr(); |
| monoid->deallocate(static_cast<view_type*>(view)); |
| } |
| |
| |
| /** Base class defining the data members of a reducer. |
| * |
| * @tparam Aligned The `m_view` data member, and therefore the entire |
| * structure, are cache-line aligned if this parameter |
| * is `true'. |
| */ |
| template <typename Monoid, bool Aligned = Monoid::align_reducer> |
| class reducer_content; |
| |
| /** Base class defining the data members of an aligned reducer. |
| */ |
| template <typename Monoid> |
| class reducer_content<Monoid, true> : public reducer_base<Monoid> |
| { |
| typedef typename Monoid::view_type view_type; |
| |
| // The leftmost view is defined as raw bytes. It will be constructed |
| // by the monoid `construct` function. It is cache-aligned, which |
| // will push it into a new cache line. Furthermore, its alignment causes |
| // the reducer as a whole to be cache-aligned, which makes the reducer |
| // size a multiple of a cache line. Since there is nothing in the reducer |
| // after the view, all this means that the leftmost view gets one or more |
| // cache lines all to itself, which prevents false sharing. |
| // |
| __CILKRTS_CACHE_ALIGN |
| char m_leftmost[sizeof(view_type)]; |
| |
| /** Test if the reducer is cache-line-aligned. |
| * |
| * Used in assertions. |
| */ |
| bool reducer_is_cache_aligned() const |
| { return 0 == ((std::size_t) this & (__CILKRTS_CACHE_LINE__ - 1)); } |
| |
| protected: |
| |
| /** Constructor. |
| */ |
| reducer_content() : reducer_base<Monoid>((char*)&m_leftmost) |
| { |
| #ifndef CILK_IGNORE_REDUCER_ALIGNMENT |
| assert(reducer_is_cache_aligned() && |
| "Reducer should be cache aligned. Please see comments following this assertion for explanation and fixes."); |
| #endif |
| /* "REDUCER SHOULD BE CACHE ALIGNED" ASSERTION. |
| * |
| * This Reducer class instantiation specifies cache-line alignment of the |
| * leftmost view field (and, implicitly, of the reducer itself). You got |
| * this assertion because a reducer with this class was allocated at a |
| * non-cache-aligned address, probably because it was allocated on the |
| * heap with `new`. This can be a problem for two reasons: |
| * |
| * 1. If the leftmost view is not on a cache line by itself, there might |
| * be a slowdown resulting from accesses to the same cache line from |
| * different threads. |
| * |
| * 2. The compiler thinks that reducer is cache-line aligned, but it |
| * really isn't. If the reducer is contained in a structure, then the |
| * compiler will believe that the containing structure, and other |
| * fields contained in it, are also more aligned than they really |
| * are. In particular, if the structure contains a numeric array that |
| * is used in a vectorizable loop, then the compiler might generate |
| * invalid vector instructions, resulting in a runtime error. |
| * |
| * The compiler will always allocate reducer variables, and structure |
| * variables containing reducers, with their required alignment. |
| * Reducers, and structures containing a reducer, which are allocated |
| * on the heap with `new` will _not_ be properly aligned. |
| * |
| * There are three ways that you can fix this assertion failure. |
| * |
| * A. Rewrite your code to use the new-style `reducer< op_XXX<Type> >` |
| * instead of the legacy `reducer_XXX<type>`. The new-style reducers |
| * are not declared to be cache-aligned, and will work properly if |
| * they are not cache-aligned. |
| * |
| * B. If you must allocate an old-style reducer or a structure containing |
| * a reducer on the heap, figure out how to align it correctly. The |
| * suggested fix is to use `cilk::aligned_new()` and |
| * `cilk::aligned_delete()` instead of `new` and `delete`, as follows: |
| * |
| * Type* ptr = cilk::aligned_new<Type>(constructor-arguments); |
| * cilk::aligned_delete(ptr); |
| * |
| * C. Define the macro CILK_IGNORE_REDUCER_ALIGNMENT, which will suppress |
| * the assertion check. Do this only if you are comfortable that |
| * problem (2) above will not occur. |
| */ |
| } |
| }; |
| |
| /** Base class defining the data members of an unaligned reducer. |
| */ |
| template <typename Monoid> |
| class reducer_content<Monoid, false> : public reducer_base<Monoid> |
| { |
| typedef typename Monoid::view_type view_type; ///< The view type. |
| |
| // Reserve space for the leftmost view. The view will be allocated at an |
| // aligned offset in this space at runtime, to guarantee that the view |
| // will get one or more cache lines all to itself, to prevent false |
| // sharing. |
| // |
| // The number of bytes to reserve is determined as follows: |
| // * Start with the view size. |
| // * Round up to a multiple of the cache line size, to get the total size |
| // of the cache lines that will be dedicated to the view. |
| // * Add (cache line size - 1) filler bytes to guarantee that the reserved |
| // area will contain a cache-aligned block of the required cache lines, |
| // no matter where the reserved area starts. |
| // |
| char m_leftmost[ |
| // View size rounded up to multiple cache lines |
| ( (sizeof(view_type) + __CILKRTS_CACHE_LINE__ - 1) |
| & ~ (__CILKRTS_CACHE_LINE__ - 1) |
| ) |
| // plus filler to allow alignment. |
| + __CILKRTS_CACHE_LINE__ - 1 |
| ]; |
| |
| protected: |
| |
| /** Constructor. Find the first cache-aligned position in the reserved |
| * area, and pass it to the base constructor as the leftmost view |
| * address. |
| */ |
| reducer_content() : |
| reducer_base<Monoid>( |
| (char*)( ((std::size_t)&m_leftmost + __CILKRTS_CACHE_LINE__ - 1) |
| & ~ (__CILKRTS_CACHE_LINE__ - 1) ) ) |
| {} |
| }; |
| |
| |
| } // namespace internal |
| |
| |
| // The __cilkrts_hyperobject_ functions are defined differently depending on |
| // whether a file is compiled with or without the CILK_STUB option. Therefore, |
| // reducers compiled in the two modes should be link-time incompatible, so that |
| // object files compiled with stubbed reducers won't be linked into an |
| // unstubbed program, or vice versa. We achieve this by putting the reducer |
| // class definition into the cilk::stub namespace in a stubbed compilation. |
| |
| #ifdef CILK_STUB |
| namespace stub { |
| #endif |
| |
| /** Reducer class. |
| * |
| * A reducer is instantiated on a Monoid. The Monoid provides the value |
| * type, associative reduce function, and identity for the reducer. |
| * |
| * @tparam Monoid The monoid class that the reducer is instantiated on. It must model |
| * the @ref reducers_monoid_concept "monoid concept". |
| * |
| * @see @ref pagereducers |
| */ |
| template <class Monoid> |
| class reducer : public internal::reducer_content<Monoid> |
| { |
| typedef internal::reducer_content<Monoid> base; |
| using base::monoid_ptr; |
| using base::leftmost_ptr; |
| public: |
| typedef Monoid monoid_type; ///< The monoid type. |
| typedef typename Monoid::value_type value_type; ///< The value type. |
| typedef typename Monoid::view_type view_type; ///< The view type. |
| |
| private: |
| typedef internal::reducer_set_get<value_type, view_type> set_get; |
| |
| reducer(const reducer&); ///< Disallow copying. |
| reducer& operator=(const reducer&); ///< Disallow assignment. |
| |
| public: |
| |
| /** @name Constructors |
| * |
| * All reducer constructors call the static `construct()` function of the monoid class to |
| * construct the reducer's monoid and leftmost view. |
| * |
| * The reducer constructor arguments are simply passed through to the construct() function. |
| * Thus, the constructor parameters accepted by a particular reducer class are determined |
| * by its monoid class. |
| */ |
| //@{ |
| |
| /** 0 – 6 const reference parameters. |
| */ |
| //@{ |
| |
| reducer() |
| { |
| monoid_type::construct(monoid_ptr(), leftmost_ptr()); |
| } |
| |
| template <typename T1> |
| reducer(const T1& x1) |
| { |
| monoid_type::construct(monoid_ptr(), leftmost_ptr(), x1); |
| } |
| |
| template <typename T1, typename T2> |
| reducer(const T1& x1, const T2& x2) |
| { |
| monoid_type::construct(monoid_ptr(), leftmost_ptr(), x1, x2); |
| } |
| |
| template <typename T1, typename T2, typename T3> |
| reducer(const T1& x1, const T2& x2, const T3& x3) |
| { |
| monoid_type::construct(monoid_ptr(), leftmost_ptr(), x1, x2, x3); |
| } |
| |
| template <typename T1, typename T2, typename T3, typename T4> |
| reducer(const T1& x1, const T2& x2, const T3& x3, const T4& x4) |
| { |
| monoid_type::construct(monoid_ptr(), leftmost_ptr(), x1, x2, x3, x4); |
| } |
| |
| template <typename T1, typename T2, typename T3, typename T4, typename T5> |
| reducer(const T1& x1, const T2& x2, const T3& x3, const T4& x4, const T5& x5) |
| { |
| monoid_type::construct(monoid_ptr(), leftmost_ptr(), x1, x2, x3, x4, x5); |
| } |
| |
| template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6> |
| reducer(const T1& x1, const T2& x2, const T3& x3, const T4& x4, const T5& x5, const T6& x6) |
| { |
| monoid_type::construct(monoid_ptr(), leftmost_ptr(), x1, x2, x3, x4, x5, x6); |
| } |
| |
| //@} |
| |
| /** 1 non-const reference parameter. |
| */ |
| //@{ |
| |
| template <typename T1> |
| reducer(T1& x1) |
| { |
| monoid_type::construct(monoid_ptr(), leftmost_ptr(), x1); |
| } |
| |
| //@} |
| |
| /** Destructor. |
| */ |
| __CILKRTS_STRAND_STALE(~reducer()) |
| { |
| leftmost_ptr()->~view_type(); |
| monoid_ptr()->~monoid_type(); |
| } |
| |
| //@{ |
| /** Get the monoid. |
| * |
| * @return A reference to the monoid object belonging to this reducer. |
| */ |
| Monoid& monoid() { return *monoid_ptr(); } |
| |
| const Monoid& monoid() const |
| { return const_cast<reducer*>(this)->monoid(); } |
| //@} |
| |
| //@{ |
| /** Access the current view. |
| * |
| * Return a reference to the instance of the reducer’s view that was |
| * created for the current strand of a parallel computation (and create |
| * it if it doesn’t already exist). |
| */ |
| view_type& view() { return base::view(); } |
| const view_type& view() const { return base::view(); } |
| //@} |
| |
| |
| /** @name Dereference the reducer to get the view. |
| * |
| * “Dereferencing” a reducer yields the view for the current strand. The |
| * view, in turn, acts as a proxy for its contained value, exposing only |
| * those operations which are consistent with the reducer’s monoid. Thus, |
| * all modifications of the reducer’s accumulator variable are written as |
| * |
| * *reducer OP ... |
| * |
| * or |
| * |
| * reducer->func(...) |
| * |
| * (The permitted operations on a reducer’s accumulator are listed in the |
| * documentation for that particular kind of reducer.) |
| * |
| * @note `*r` is a synonym for `r.view()`. Recommended style is to use |
| * `*r` (or `r->`) in the common case where code is simply |
| * updating the accumulator variable wrapped in the view, and to |
| * use `r.view()` in the unusual case where it is desirable to |
| * call attention to the view itself. |
| */ |
| //@{ |
| |
| //@{ |
| /** Dereference operator. |
| * |
| * @return A reference to the per-strand view instance. |
| */ |
| view_type& operator*() { return view(); } |
| view_type const& operator*() const { return view(); } |
| //@} |
| |
| //@{ |
| /** Pointer operator. |
| * |
| * @return A pointer to the per-strand view instance. |
| */ |
| view_type* operator->() { return &view(); } |
| view_type const* operator->() const { return &view(); } |
| //@} |
| |
| //@{ |
| /** Deprecated view access. |
| * |
| * `r()` is a synonym for `*r` which was used with early versions of Cilk |
| * reducers. `*r` is now the preferred usage. |
| * |
| * @deprecated Use operator*() instead of operator()(). |
| * |
| * @return A reference to the per-strand view instance. |
| */ |
| view_type& operator()() { return view(); } |
| view_type const& operator()() const { return view(); } |
| //@} |
| |
| //@} |
| |
| /** @name Set and get the value. |
| * |
| * These functions are used to set an initial value for the reducer before |
| * starting the reduction, or to get the final value after the reduction |
| * is complete. |
| * |
| * @note These functions are completely different from the view |
| * operations that are made available via operator*() and |
| * operator->(), which are used to _modify_ the reducer’s value |
| * _during_ the reduction. |
| * |
| * @warning These functions _can_ be called at any time, and in |
| * general, they will refer to the value contained in the view |
| * for the current strand. However, using them other than to |
| * set the reduction’s initial value or get its final value |
| * will almost always result in undefined behavior. |
| */ |
| //@{ |
| |
| /** Move a value into the reducer. |
| * |
| * This function is used to set the initial value of the reducer’s |
| * accumulator variable by either copying or _moving_ the value of @a obj |
| * into it. Moving a value can often be performed in constant time, even |
| * for large container objects, but has the side effect of leaving the |
| * value of @a obj undefined. (See the description of the |
| * @ref move_in_wrapper class for a discussion of moving values.) |
| * |
| * @par Usage |
| * A move_in() call to initialize a reducer is often paired with a |
| * move_out() call to get its final value: |
| * |
| * reducer<Type> xr; |
| * xr.move_in(x); |
| * … do the reduction … |
| * xr.move_out(x); |
| * |
| * @par Assumptions |
| * - You cannot assume either that this will function will copy its |
| * value or that it will move it. |
| * - You must assume that the value of @a obj will be undefined |
| * after the call to move_in(). |
| * - You can assume that move_in() will be at least as efficient as |
| * set_value(), and you should therefore prefer move_in() unless |
| * you need the value of @a obj to be unchanged after the call. |
| * (But you should usually prefer the move-in constructor over a |
| * move_in() call — see the note below.) |
| * |
| * @note The behavior of a default constructor followed by move-in |
| * initialization: |
| * |
| * reducer<Type> xr; |
| * xr.move_in(x); |
| * |
| * @note is not necessarily the same as a move-in constructor: |
| * |
| * reducer<Type> xr(move_in(x)); |
| * |
| * @note In particular, when @a Type is a container type with a |
| * non-empty allocator, the move-in constructor will create the |
| * accumulator variable with the same allocator as the input |
| * argument @a x, while the default constructor will create the |
| * accumulator variable with a default allocator. The mismatch of |
| * allocators in the latter case means that the input argument |
| * @a x may have to be copied in linear time instead of being |
| * moved in constant time. |
| * |
| * @note Best practice is to prefer the move-in constructor over the |
| * move-in function unless the move-in function is required for |
| * some specific reason. |
| * |
| * @warning Calling this function other than to set the initial value |
| * for a reduction will almost always result in undefined |
| * behavior. |
| * |
| * @param obj The object containing the value that will be moved into the |
| * reducer. |
| * |
| * @post The reducer contains the value that was initially in @a obj. |
| * @post The value of @a obj is undefined. |
| * |
| * @see set_value() |
| */ |
| void move_in(value_type& obj) { set_get::move_in(view(), obj);} |
| |
| /** Move the value out of the reducer. |
| * |
| * This function is used to retrieve the final value of the reducer’s |
| * accumulator variable by either copying or _moving_ the value of @a obj |
| * into it. Moving a value can often be performed in constant time, even |
| * for large container objects, but has the side effect of leaving the |
| * value of the reducer’s accumulator variable undefined. (See the |
| * description of the @ref move_in_wrapper class for a discussion of |
| * moving values.) |
| * |
| * @par Usage |
| * A move_in() call to initialize a reducer is often paired with a |
| * move_out() call to get its final value: |
| * |
| * reducer<Type> xr; |
| * xr.move_in(x); |
| * … do the reduction … |
| * xr.move_out(x); |
| * |
| * @par Assumptions |
| * - You cannot assume either that this will function will copy its |
| * value or that it will move it. |
| * - You must assume that the value of the reducer’s accumulator |
| * variable will be undefined after the call to move_out(). |
| * - You can assume that move_out() will be at least as efficient as |
| * get_value(), and you should therefore prefer move_out() unless |
| * you need the accumulator variable to be preserved after the |
| * call. |
| * |
| * @warning Calling this function other than to retrieve the final |
| * value of a reduction will almost always result in undefined |
| * behavior. |
| * |
| * @param obj The object that the value of the reducer will be moved into. |
| * |
| * @post @a obj contains the value that was initially in the reducer. |
| * @post The value of the reducer is undefined. |
| * |
| * @see get_value() |
| */ |
| void move_out(value_type& obj) { set_get::move_out(view(), obj); } |
| |
| /** Set the value of the reducer. |
| * |
| * This function sets the initial value of the reducer’s accumulator |
| * variable to the value of @a obj. |
| * |
| * @note The behavior of a default constructor followed by |
| * initialization: |
| * |
| * reducer<Type> xr; |
| * xr.set_value(x); |
| * |
| * @note is not necessarily the same as a value constructor: |
| * |
| * reducer<Type> xr(x); |
| * |
| * @note In particular, when @a Type is a container type with a |
| * non-empty allocator, the value constructor will create the |
| * accumulator variable with the same allocator as the input |
| * argument @a x, while the default constructor will create the |
| * accumulator variable with a default allocator. |
| * |
| * @warning Calling this function other than to set the initial value |
| * for a reduction will almost always result in undefined |
| * behavior. |
| * |
| * @param obj The object containing the value that will be copied into |
| * the reducer. |
| * |
| * @post The reducer contains a copy of the value in @a obj. |
| * |
| * @see move_in() |
| */ |
| void set_value(const value_type& obj) { set_get::set_value(view(), obj); } |
| |
| /** Get the value of the reducer. |
| * |
| * This function gets the final value of the reducer’s accumulator |
| * variable. |
| * |
| * @warning Calling this function other than to retrieve the final |
| * value of a reduction will almost always result in undefined |
| * behavior. |
| * |
| * @return A reference to the value contained in the reducer. |
| * |
| * @see move_out() |
| */ |
| typename set_get::get_value_type get_value() const |
| { return set_get::get_value(view()); } |
| |
| //@} |
| |
| /** Implicit downcast to legacy reducer wrapper, if any. |
| * |
| * @see legacy_reducer_downcast |
| */ |
| operator typename legacy_reducer_downcast<reducer>::type& () |
| { |
| typedef typename legacy_reducer_downcast<reducer>::type downcast_type; |
| return *reinterpret_cast<downcast_type*>(this); |
| } |
| |
| |
| /** Implicit downcast to legacy reducer wrapper, if any. |
| * |
| * @see legacy_reducer_downcast |
| */ |
| operator const typename legacy_reducer_downcast<reducer>::type& () const |
| { |
| typedef typename legacy_reducer_downcast<reducer>::type downcast_type; |
| return *reinterpret_cast<const downcast_type*>(this); |
| } |
| }; |
| |
| #ifdef CILK_STUB |
| } // namespace stub |
| using stub::reducer; |
| #endif |
| |
| } // end namespace cilk |
| |
| #endif /* __cplusplus */ |
| |
| /** @page page_reducers_in_c Creating and Using Reducers in C |
| * |
| * @tableofcontents |
| * |
| * The Cilk runtime supports reducers written in C as well as in C++. The basic logic is the |
| * same, but the implementation details are very different. The C++ reducer implementation uses |
| * templates heavily to create very generic components. The C reducer implementation uses |
| * macros, which are a much blunter instrument. The most immediate consequence is that the |
| * monoid/view/reducer architecture is mostly implicit rather than explicit in C reducers. |
| * |
| * @section reducers_c_overview Overview of Using Reducers in C |
| * |
| * The basic usage pattern for C reducers is: |
| * |
| * 1. Create and initialize a reducer object. |
| * 2. Tell the Cilk runtime about the reducer. |
| * 3. Update the value contained in the reducer in a parallel computation. |
| * 4. Tell the Cilk runtime that you are done with the reducer. |
| * 5. Retrieve the value from the reducer. |
| * |
| * @subsection reducers_c_creation Creating and Initializing a C Reducer |
| * |
| * The basic pattern for creating and initializing a reducer object in C is |
| * |
| * CILK_C_DECLARE_REDUCER(value-type) reducer-name = |
| * CILK_C_INIT_REDUCER(value-type, |
| * reduce-function, |
| * identity-function, |
| * destroy-function, |
| * initial-value); |
| * |
| * This is simply an initialized definition of a variable named _reducer-name_. The |
| * @ref CILK_C_DECLARE_REDUCER macro expands to an anonymous `struct` declaration for a reducer |
| * object containing a view of type _value-type_, and the @ref CILK_C_INIT_REDUCER macro |
| * expands to a struct initializer. |
| * |
| * @subsection reducers_c_reduce_func Reduce Functions |
| * |
| * The reduce function for a reducer is called when a parallel execution strand terminates, to |
| * combine the values computed by the terminating strand and the strand to its left. It takes |
| * three arguments: |
| * |
| * - `void* reducer` — the address of the reducer. |
| * - `void* left` — the address of the value for the left strand. |
| * - `void* right` — the address of the value for the right (terminating) strand. |
| * |
| * It must apply the reducer’s reduction operation to the `left` and `right` values, leaving |
| * the result in the `left` value. The `right` value is undefined after the reduce function |
| * call. |
| * |
| * @subsection reducers_c_identity_func Identity Functions |
| * |
| * The identity function for a reducer is called when a parallel execution strand begins, to |
| * initialize its value to the reducer’s identity value. It takes two arguments: |
| * |
| * - `void* reducer` — the address of the reducer. |
| * - `void* v` — the address of a freshly allocated block of memory of size |
| * `sizeof(value-type)`. |
| * |
| * It must initialize the memory pointed to by `v` so that it contains the reducer’s identity |
| * value. |
| * |
| * @subsection reducers_c_destroy_func Destroy Functions |
| * |
| * The destroy function for a reducer is called when a parallel execution strand terminates, to |
| * do any necessary cleanup before its value is deallocated. It takes two arguments: |
| * |
| * - `void* reducer` — the address of the reducer. |
| * - `void* p` — the address of the value for the terminating strand. |
| * |
| * It must release any resources belonging to the value pointed to by `p`, to avoid a resource |
| * leak when the memory containing the value is deallocated. |
| * |
| * The runtime function `__cilkrts_hyperobject_noop_destroy` can be used for the destructor |
| * function if the reducer’s values do not need any cleanup. |
| * |
| * @subsection reducers_c_register Tell the Cilk Runtime About the Reducer |
| * |
| * Call the @ref CILK_C_REGISTER_REDUCER macro to register the reducer with the Cilk runtime: |
| * |
| * CILK_C_REGISTER_REDUCER(reducer-name); |
| * |
| * The runtime will manage reducer values for all registered reducers when parallel execution |
| * strands begin and end. |
| * |
| * @subsection reducers_c_update Update the Value Contained in the Reducer |
| * |
| * The @ref REDUCER_VIEW macro returns a reference to the reducer’s value for the current |
| * parallel strand: |
| * |
| * REDUCER_VIEW(reducer-name) = REDUCER_VIEW(reducer-name) OP x; |
| * |
| * C++ reducer views restrict access to the wrapped value so that it can only be modified in |
| * ways consistent with the reducer’s operation. No such protection is provided for C reducers. |
| * It is |
| * entirely the responsibility of the user to avoid modifying the value in any |
| * inappropriate way. |
| * |
| * @subsection c_reducers_unregister Tell the Cilk Runtime That You Are Done with the Reducer |
| * |
| * When the parallel computation is complete, call the @ref CILK_C_UNREGISTER_REDUCER macro to |
| * unregister the reducer with the Cilk runtime: |
| * |
| * CILK_C_UNREGISTER_REDUCER(reducer-name); |
| * |
| * The runtime will stop managing reducer values for the reducer. |
| * |
| * @subsection c_reducers_retrieve Retrieve the Value from the Reducer |
| * |
| * When the parallel computation is complete, use the @ref REDUCER_VIEW macro to retrieve the |
| * final value computed by the reducer. |
| * |
| * @subsection reducers_c_example_custom Example — Creating and Using a Custom C Reducer |
| * |
| * The `IntList` type represents a simple list of integers. |
| * |
| * struct _intListNode { |
| * int value; |
| * _intListNode* next; |
| * } IntListNode; |
| * typedef struct { IntListNode* head; IntListNode* tail; } IntList; |
| * |
| * // Initialize a list to be empty |
| * void IntList_init(IntList* list) { list->head = list->tail = 0; } |
| * |
| * // Append an integer to the list |
| * void IntList_append(IntList* list, int x) |
| * { |
| * IntListNode* node = (IntListNode*) malloc(sizeof(IntListNode)); |
| * if (list->tail) list->tail->next = node; else list->head = node; |
| * list->tail = node; |
| * } |
| * |
| * // Append the right list to the left list, and leave the right list empty |
| * void IntList_concat(IntList* left, IntList* right) |
| * { |
| * if (left->head) { |
| * left->tail->next = right->head; |
| * if (right->tail) left->tail = right->tail; |
| * } |
| * else { |
| * *left = *right; |
| * } |
| * IntList_init(*right); |
| * } |
| * |
| * This code creates a reducer that supports creating an `IntList` by appending values to it. |
| * |
| * void identity_IntList(void* reducer, void* list) |
| * { |
| * IntList_init((IntList*)list); |
| * } |
| * |
| * void reduce_IntList(void* reducer, void* left, void* right) |
| * { |
| * IntList_concat((IntList*)left, (IntList*)right); |
| * } |
| * |
| * CILK_C_DECLARE_REDUCER(IntList) my_list_int_reducer = |
| * CILK_C_INIT_REDUCER(IntList, |
| * reduce_int_list, |
| * identity_int_list, |
| * __cilkrts_hyperobject_noop_destroy); |
| * // Initial value omitted // |
| * ListInt_init(&REDUCER_VIEW(my_int_list_reducer)); |
| * |
| * CILK_C_REGISTER_REDUCER(my_int_list_reducer); |
| * cilk_for (int i = 0; i != n; ++i) { |
| * IntList_append(&REDUCER_VIEW(my_int_list_reducer), a[i]); |
| * } |
| * CILK_C_UNREGISTER_REDUCER(my_int_list_reducer); |
| * |
| * IntList result = REDUCER_VIEW(my_int_list_reducer); |
| * |
| * @section reducers_c_predefined Predefined C Reducers |
| * |
| * Some of the predefined reducer classes in the Cilk library come with a set of predefined |
| * macros to provide the same capabilities in C. In general, two macros are provided for each |
| * predefined reducer family: |
| * |
| * - `CILK_C_REDUCER_operation(reducer-name, type-name, initial-value)` — Declares a |
| * reducer object named _reducer-name_ with initial value _initial-value_ to perform |
| * a reduction using the _operation_ on values of the type specified by _type-name_. |
| * This is the equivalent of the general code described in @ref reducers_c_creation : |
| * |
| * CILK_C_DECLARE_REDUCER(type) reducer-name = |
| * CILK_C_INIT_REDUCER(type, ..., initial-value); |
| * |
| * where _type_ is the C type corresponding to _type_name_. See @ref reducers_c_type_names |
| * below for the _type-names_ that you can use. |
| * |
| * - `CILK_C_REDUCER_operation_TYPE(type-name)` — Expands to the `typedef` name for the type |
| * of the reducer object declared by |
| * `CILK_C_REDUCER_operation(reducer-name, type-name, initial-value)`. |
| * |
| * See @ref reducers_c_example_predefined. |
| * |
| * The predefined C reducers are: |
| * |
| * | Operation | Name | Documentation | |
| * |-------------------|---------------|-------------------------------| |
| * | addition | `OPADD` | @ref ReducersAdd | |
| * | bitwise and | `OPAND` | @ref ReducersAnd | |
| * | bitwise or | `OPOR` | @ref ReducersOr | |
| * | bitwise xor | `OPXOR` | @ref ReducersXor | |
| * | multiplication | `OPMUL` | @ref ReducersMul | |
| * | minimum | `MIN` | @ref ReducersMinMax | |
| * | minimum & index | `MIN_INDEX` | @ref ReducersMinMax | |
| * | maximum | `MIN` | @ref ReducersMinMax | |
| * | maximum & index | `MIN_INDEX` | @ref ReducersMinMax | |
| * |
| * @subsection reducers_c_type_names Numeric Type Names |
| * |
| * The type and function names created by the C reducer definition macros incorporate both the |
| * reducer kind (`opadd`, `opxor`, etc.) and the value type of the reducer (`int`, `double`, |
| * etc.). The value type is represented by a _numeric type name_ string. The types supported |
| * in C reducers, and their corresponding numeric type names, are given in the following table: |
| * |
| * | Type | Numeric Type Name | |
| * |-----------------------|-------------------------------| |
| * | `char` | `char` | |
| * | `unsigned char` | `uchar` | |
| * | `signed char` | `schar` | |
| * | `wchar_t` | `wchar_t` | |
| * | `short` | `short` | |
| * | `unsigned short` | `ushort` | |
| * | `int` | `int` | |
| * | `unsigned int` | `uint` | |
| * | `unsigned int` | `unsigned` (alternate name) | |
| * | `long` | `long` | |
| * | `unsigned long` | `ulong` | |
| * | `long long` | `longlong` | |
| * | `unsigned long long` | `ulonglong` | |
| * | `float` | `float` | |
| * | `double` | `double` | |
| * | `long double` | `longdouble` | |
| * |
| * @subsection reducers_c_example_predefined Example — Using a Predefined C Reducer |
| * |
| * To compute the sum of all the values in an array of `unsigned int`: |
| * |
| * CILK_C_REDUCER_OPADD(sum, uint, 0); |
| * CILK_C_REGISTER_REDUCER(sum); |
| * cilk_for(int i = 0; i != n; ++i) { |
| * REDUCER_VIEW(sum) += a[i]; |
| * } |
| * CILK_C_UNREGISTER_REDUCER(sum); |
| * printf("The sum is %u\n", REDUCER_VIEW(sum)); |
| */ |
| |
| |
| /** @name C language reducer macros |
| * |
| * These macros are used to declare and work with reducers in C code. |
| * |
| * @see @ref page_reducers_in_c |
| */ |
| //@{ |
| |
| /// @cond internal |
| |
| /** @name Compound identifier macros. |
| * |
| * These macros are used to construct an identifier by concatenating two or three identifiers. |
| */ |
| //@{ |
| |
| /** Expand to an identifier formed by concatenating two identifiers. |
| */ |
| #define __CILKRTS_MKIDENT(a,b) __CILKRTS_MKIDENT_IMP(a,b,) |
| |
| /** Expand to an identifier formed by concatenating three identifiers. |
| */ |
| #define __CILKRTS_MKIDENT3(a,b,c) __CILKRTS_MKIDENT_IMP(a,b,c) |
| |
| /** Helper macro to do the concatenation. |
| */ |
| #define __CILKRTS_MKIDENT_IMP(a,b,c) a ## b ## c |
| |
| //@} |
| |
| /** Compiler-specific keyword for the “type of” operator. |
| */ |
| #if defined(__GNUC__) && !defined(__INTEL_COMPILER) |
| # define _Typeof __typeof__ |
| #endif |
| |
| /** @name Predefined reducer function declaration macros. |
| * |
| * These macros are used to create the function headers for the identity, reduction, |
| * and destructor functions for a builtin reducer family. The macro can be followed by |
| * a semicolon to create a declaration, or by a brace-enclosed body to create a definition. |
| */ |
| //@{ |
| |
| /** Create an identity function header. |
| * |
| * @note The name of the function’s value pointer parameter will always be `v`. |
| * |
| * @param name The reducer family name. |
| * @param tn The type name. |
| */ |
| #define __CILKRTS_DECLARE_REDUCER_IDENTITY(name,tn) CILK_EXPORT \ |
| void __CILKRTS_MKIDENT3(name,_identity_,tn)(void* key, void* v) |
| |
| /** Create a reduction function header. |
| * |
| * @param name The reducer family name. |
| * @param tn The type name. |
| * @param l The name to use for the function’s left value pointer parameter. |
| * @param r The name to use for the function’s right value pointer parameter. |
| */ |
| #define __CILKRTS_DECLARE_REDUCER_REDUCE(name,tn,l,r) CILK_EXPORT \ |
| void __CILKRTS_MKIDENT3(name,_reduce_,tn)(void* key, void* l, void* r) |
| |
| /** Create a destructor function header. |
| * |
| * @param name The reducer family name. |
| * @param tn The type name. |
| * @param p The name to use for the function’s value pointer parameter. |
| */ |
| #define __CILKRTS_DECLARE_REDUCER_DESTROY(name,tn,p) CILK_EXPORT \ |
| void __CILKRTS_MKIDENT3(name,_destroy_,tn)(void* key, void* p) |
| |
| //@} |
| |
| /// @endcond |
| |
| |
| /*************************************************************************** |
| * Real implementation |
| ***************************************************************************/ |
| |
| /** Declaration of a C reducer structure type. |
| * |
| * This macro expands into an anonymous structure declaration for a C reducer structure |
| * which contains a @a Type value. For example: |
| * |
| * CILK_C_DECLARE_REDUCER(int) my_add_int_reducer = |
| * CILK_C_INIT_REDUCER(int, …); |
| * |
| * @param Type The type of the value contained in the reducer object. |
| * |
| * @see @ref reducers_c_creation |
| */ |
| #define CILK_C_DECLARE_REDUCER(Type) struct { \ |
| __cilkrts_hyperobject_base __cilkrts_hyperbase; \ |
| __CILKRTS_CACHE_ALIGN Type value; \ |
| } |
| |
| /** Initializer for a C reducer structure. |
| * |
| * This macro expands into a brace-enclosed structure initializer for a C reducer structure |
| * that was declared with `CILK_C_DECLARE_REDUCER(Type)`. For example: |
| * |
| * CILK_C_DECLARE_REDUCER(int) my_add_int_reducer = |
| * CILK_C_INIT_REDUCER(int, |
| * add_int_reduce, |
| * add_int_identity, |
| * __cilkrts_hyperobject_noop_destroy, |
| * 0); |
| * |
| * @param Type The type of the value contained in the reducer object. Must be the same as |
| * the @a Type argument of the CILK_C_DECLARE_REDUCER macro call that created |
| * the reducer. |
| * @param Reduce The address of the @ref reducers_c_reduce_func "reduce function" for the |
| * reducer. |
| * @param Identity The address of the @ref reducers_c_identity_func "identity function" for |
| * the reducer. |
| * @param Destroy The address of the @ref reducers_c_destroy_func "destroy function" for the |
| * reducer. |
| * @param ... The initial value for the reducer. (A single expression if @a Type is a |
| * scalar type; a list of values if @a Type is a struct or array type.) |
| * |
| * @see @ref reducers_c_creation |
| */ |
| |
| #define CILK_C_INIT_REDUCER(Type, Reduce, Identity, Destroy, ...) \ |
| { { { Reduce \ |
| , Identity \ |
| , Destroy \ |
| , __cilkrts_hyperobject_alloc \ |
| , __cilkrts_hyperobject_dealloc \ |
| } \ |
| , 0 \ |
| , __CILKRTS_CACHE_LINE__ \ |
| , sizeof(Type) \ |
| } \ |
| , __VA_ARGS__ \ |
| } |
| |
| /** Register a reducer with the Cilk runtime. |
| * |
| * The runtime will manage reducer values for all registered reducers when parallel execution |
| * strands begin and end. For example: |
| * |
| * CILK_C_REGISTER_REDUCER(my_add_int_reducer); |
| * cilk_for (int i = 0; i != n; ++i) { |
| * … |
| * } |
| * |
| * @param Expr The reducer to be registered. |
| * |
| * @see @ref page_reducers_in_c |
| */ |
| #define CILK_C_REGISTER_REDUCER(Expr) \ |
| __cilkrts_hyper_create(&(Expr).__cilkrts_hyperbase) |
| |
| /** Unregister a reducer with the Cilk runtime. |
| * |
| * The runtime will stop managing reducer values for a reducer after it is unregistered. For |
| * example: |
| * |
| * cilk_for (int i = 0; i != n; ++i) { |
| * … |
| * } |
| * CILK_C_UNREGISTER_REDUCER(my_add_int_reducer); |
| * |
| * @param Expr The reducer to be unregistered. |
| * |
| * @see @ref page_reducers_in_c |
| */ |
| #define CILK_C_UNREGISTER_REDUCER(Expr) \ |
| __cilkrts_hyper_destroy(&(Expr).__cilkrts_hyperbase) |
| |
| /** Get the current view for a reducer. |
| * |
| * The `REDUCER_VIEW(reducer-name)` returns a reference to the reducer’s value for the |
| * current parallel strand. This can be used to initialize thevalue of the reducer before it |
| * is used, to modify the value of the reducer on the current parallel strand, or to retrieve |
| * the final value of the reducer at the end of the parallel computation. |
| * |
| * REDUCER_VIEW(my_add_int_reducer) = REDUCER_VIEW(my_add_int_reducer) + x; |
| * |
| * @note C++ reducer views restrict access to the wrapped value so that it can only be |
| * modified in ways consistent with the reducer’s operation. No such protection is provided |
| * for C reducers. It is entirely the responsibility of the user to refrain from modifying the |
| * value in any inappropriate way. |
| * |
| * @param Expr The reducer whose value is to be returned. |
| * |
| * @see @ref page_reducers_in_c |
| */ |
| #define REDUCER_VIEW(Expr) (*(_Typeof((Expr).value)*) \ |
| __cilkrts_hyper_lookup(&(Expr).__cilkrts_hyperbase)) |
| |
| //@} C language reducer macros |
| |
| #endif // CILK_REDUCER_H_INCLUDED |