/* reducer.h -*- C++ -*- * * Copyright (C) 2009-2016, Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * ********************************************************************* * * PLEASE NOTE: This file is a downstream copy of a file mainitained in * a repository at cilkplus.org. Changes made to this file that are not * submitted through the contribution process detailed at * http://www.cilkplus.org/submit-cilk-contribution will be lost the next * time that a new version is released. Changes only submitted to the * GNU compiler collection or posted to the git repository at * https://bitbucket.org/intelcilkruntime/intel-cilk-runtime.git are * not tracked. * * We welcome your contributions to this open source project. Thank you * for your assistance in helping us improve Cilk Plus. */ /** @file reducer.h * * @brief Defines foundation classes for creating Intel(R) Cilk(TM) Plus 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 namespace cilk { /** 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 x1_provisional( new (x1) T1 ); * … more initialization … * x1_provisional.confirm(); * * or * * provisional_guard 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. * * **NOTE**: Do *not* be tempted to chain a `provisional_guard` * constructor with `confirm_if` as in this example: * * // BAD IDEA * provisional_guard( new (x1) T1 ).confirm_if( new (x2) T2 ); * * The code above is problematic because the evaluation of the T2 * constructor is unsequenced with respect to the call to the * `provisional_guard` constructor (and with respect the T1 constructor). * Thus, the compiler may choose to evaluate `new (x2) T2` before * constructing the guard and leak the T1 object if the `T2` constructor * throws. * * @tparam Type The type of the provisionally constructed object. */ template 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 Cond* confirm_if(Cond* cond) { if (cond) m_ptr = 0; return cond; } }; /** 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 class monoid_base { 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 Intel Cilk Plus library versions 1.0 and 1.01, the default was true. * In Intel Cilk Plus library versions prior to 1.0, reducers were always * aligned, and this data member did not exist.) */ align_reducer = false }; /** Destroys 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(); } /** Allocates 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); } /** Deallocates raw 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); } /** Creates 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 Constructs 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 are set). * * A 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-constructs the monoid, identity-constructs the view. * * @param monoid Address of uninitialized monoid object. * @param view Address of uninitialized initial view object. */ //@{ template static void construct(Monoid* monoid, View* view) { provisional_guard guard( new((void*) monoid) Monoid() ); monoid->identity(view); guard.confirm(); } //@} /** Default-constructs the monoid, and passes one to five const reference * arguments to the view constructor. */ //@{ template static void construct(Monoid* monoid, View* view, const T1& x1) { provisional_guard guard( new((void*) monoid) Monoid() ); guard.confirm_if( new((void*) view) View(x1) ); } template static void construct(Monoid* monoid, View* view, const T1& x1, const T2& x2) { provisional_guard guard( new((void*) monoid) Monoid() ); guard.confirm_if( new((void*) view) View(x1, x2) ); } template static void construct(Monoid* monoid, View* view, const T1& x1, const T2& x2, const T3& x3) { provisional_guard guard( new((void*) monoid) Monoid() ); guard.confirm_if( new((void*) view) View(x1, x2, x3) ); } template static void construct(Monoid* monoid, View* view, const T1& x1, const T2& x2, const T3& x3, const T4& x4) { provisional_guard guard( new((void*) monoid) Monoid() ); guard.confirm_if( new((void*) view) View(x1, x2, x3, x4) ); } template static void construct(Monoid* monoid, View* view, const T1& x1, const T2& x2, const T3& x3, const T4& x4, const T5& x5) { provisional_guard guard( new((void*) monoid) Monoid() ); guard.confirm_if( new((void*) view) View(x1, x2, x3, x4, x5) ); } //@} /** Default-constructs the monoid, and passes one non-const reference * argument to the view constructor. */ //@{ template static void construct(Monoid* monoid, View* view, T1& x1) { provisional_guard guard( new((void*) monoid) Monoid() ); guard.confirm_if( new((void*) view) View(x1) ); } //@} /** Copy-constructs the monoid, and identity-constructs the view * constructor. * * @param monoid Address of uninitialized monoid object. * @param view Address of uninitialized initial view object. * @param m Object to be copied into `*monoid` */ //@{ template static void construct(Monoid* monoid, View* view, const Monoid& m) { provisional_guard guard( new((void*) monoid) Monoid(m) ); monoid->identity(view); guard.confirm(); } //@} /** Copy-constructs the monoid, and passes one to four const reference * arguments to the view constructor. */ //@{ template static void construct(Monoid* monoid, View* view, const Monoid& m, const T1& x1) { provisional_guard guard( new((void*) monoid) Monoid(m) ); guard.confirm_if( new((void*) view) View(x1) ); } template static void construct(Monoid* monoid, View* view, const Monoid& m, const T1& x1, const T2& x2) { provisional_guard guard( new((void*) monoid) Monoid(m) ); guard.confirm_if( new((void*) view) View(x1, x2) ); } template static void construct(Monoid* monoid, View* view, const Monoid& m, const T1& x1, const T2& x2, const T3& x3) { provisional_guard guard( new((void*) monoid) Monoid(m) ); guard.confirm_if( new((void*) view) View(x1, x2, x3) ); } template static void construct(Monoid* monoid, View* view, const Monoid& m, const T1& x1, const T2& x2, const T3& x3, const T4& x4) { provisional_guard guard( new((void*) monoid) Monoid(m) ); guard.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 Intel Cilk Plus 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 monoid_with_view : public monoid_base { 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 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; } /** Type returned by view_get_value. */ typedef Type const& return_type_for_get_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 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 > 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 x; * reducer< op_list_append > xr(move_in(x)); * * will call the `op_list_append_view(move_in_wrapper)` 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 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 inline move_in_wrapper move_in(Type& ref) { return move_in_wrapper(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 inline move_in_wrapper move_in(const Type& ref) { return move_in_wrapper(ref); } /** Wrapper class to allow implicit downcasts to reducer subclasses. * * The Intel Cilk Plus library contains a collection of reducer wrapper classes which * were created before the `cilk::reducer` style was developed. For * example, `cilk::reducer_opadd` provided essentially the same * functionality that is now provided by * `cilk::reducer< cilk::op_add >`. 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 * reducer_opadd : public reducer< op_add > { ... }; * * This reimplementation allows transparent conversion between legacy and * new reducers. That is, a `reducer*` or `reducer&` 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` has a conversion function to convert itself to * `legacy_reducer_downcast< reducer >::%type`. By default, * `legacy_reducer_downcast::%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 * struct legacy_reducer_downcast< reducer< op_add > > * { * typedef reducer_opadd type; * }; * * * @tparam Reducer The new-style reducer class whose corresponding legacy * reducer class is `type`, if there is such a legacy reducer * class. */ template 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 struct reducer_set_get { // sizeof(notchar) != sizeof(char) struct notchar { char x[2]; }; // `does_view_define_return_type_for_get_value(View*)` returns `char` if // `View` defines `return_type_for_get_value`, and `notchar` if it doesn't. template struct using_type {}; template static char does_view_define_return_type_for_get_value( using_type*); template static notchar does_view_define_return_type_for_get_value(...); // `VIEW_DOES_DEFINE_RETURN_TYPE_FOR_GET_VALUE` is true if `View` defines // `return_type_for_get_value`. enum { VIEW_DOES_DEFINE_RETURN_TYPE_FOR_GET_VALUE = sizeof( does_view_define_return_type_for_get_value(0) ) == sizeof(char) } ; // `return_type_for_get_value` is `View::return_type_for_get_value` // if it is defined, and just `Value` otherwise. template struct return_type_for_view_get_value { typedef Value type; }; template struct return_type_for_view_get_value { typedef typename InnerView::return_type_for_get_value type; }; public: typedef typename return_type_for_view_get_value< View, VIEW_DOES_DEFINE_RETURN_TYPE_FOR_GET_VALUE >::type return_type_for_get_value; 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 return_type_for_get_value get_value(const View& view) { return view.view_get_value(); } }; template struct reducer_set_get { typedef const Value& return_type_for_get_value; 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 return_type_for_get_value get_value(const Value& view) { return view; } }; /// @endcond /** Base class defining the data layout that is common to all reducers. */ template 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 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_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(__cilkrts_hyper_lookup(&m_base)); } /** @copydoc view() */ const view_type& view() const { return const_cast(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 void reducer_base::reduce_wrapper(void* r, void* lhs, void* rhs) { Monoid* monoid = static_cast(r)->monoid_ptr(); monoid->reduce(static_cast(lhs), static_cast(rhs)); } template void reducer_base::identity_wrapper(void* r, void* view) { Monoid* monoid = static_cast(r)->monoid_ptr(); monoid->identity(static_cast(view)); } template void reducer_base::destroy_wrapper(void* r, void* view) { Monoid* monoid = static_cast(r)->monoid_ptr(); monoid->destroy(static_cast(view)); } template void* reducer_base::allocate_wrapper(void* r, __STDNS size_t bytes) { Monoid* monoid = static_cast(r)->monoid_ptr(); return monoid->allocate(bytes); } template void reducer_base::deallocate_wrapper(void* r, void* view) { Monoid* monoid = static_cast(r)->monoid_ptr(); monoid->deallocate(static_cast(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 class reducer_content; /** Base class defining the data members of an aligned reducer. */ template class reducer_content : public reducer_base { 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((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 >` * instead of the legacy `reducer_XXX`. 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(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 class reducer_content : public reducer_base { 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( (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 reducer : public internal::reducer_content { typedef internal::reducer_content 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 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 reducer(const T1& x1) { monoid_type::construct(monoid_ptr(), leftmost_ptr(), x1); } template reducer(const T1& x1, const T2& x2) { monoid_type::construct(monoid_ptr(), leftmost_ptr(), x1, x2); } template reducer(const T1& x1, const T2& x2, const T3& x3) { monoid_type::construct(monoid_ptr(), leftmost_ptr(), x1, x2, x3); } template 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 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 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 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(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 * Intel Cilk Plus 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 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 xr; * xr.move_in(x); * * @note is not necessarily the same as a move-in constructor: * * reducer 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 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 xr; * xr.set_value(x); * * @note is not necessarily the same as a value constructor: * * reducer 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::return_type_for_get_value 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::type& () { typedef typename legacy_reducer_downcast::type downcast_type; return *reinterpret_cast(this); } /** Implicit downcast to legacy reducer wrapper, if any. * * @see legacy_reducer_downcast */ operator const typename legacy_reducer_downcast::type& () const { typedef typename legacy_reducer_downcast::type downcast_type; return *reinterpret_cast(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 Intel Cilk Plus 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 Intel Cilk Plus runtime about the reducer. * 3. Update the value contained in the reducer in a parallel computation. * 4. Tell the Intel Cilk Plus 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 Intel Cilk Plus Runtime About the * Reducer * * Call the @ref CILK_C_REGISTER_REDUCER macro to register the reducer with * the Intel Cilk Plus 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 Intel Cilk Plus 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 * Intel Cilk Plus 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 Intel Cilk Plus 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 | `MAX` | @ref ReducersMinMax | * | maximum & index | `MAX_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 Intel Cilk Plus 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 Intel Cilk Plus 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 the * value 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