gcc/libcilkrts/include/cilk/reducer_ostream.h

518 lines
20 KiB
C++

/* reducer_ostream.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_ostream.h
*
* @brief Defines a class for writing to an ostream in parallel.
*
* @ingroup ReducersOstream
*
* @see @ref ReducersOstream
*/
#ifndef REDUCER_OSTREAM_H_INCLUDED
#define REDUCER_OSTREAM_H_INCLUDED
#include <cilk/reducer.h>
#include <ostream>
#include <sstream>
/** @defgroup ReducersOstream Ostream Reducers
*
* Ostream reducers allow multiple strands to write to an ostream in parallel.
*
* @ingroup Reducers
*
* You should be familiar with @ref pagereducers "Intel(R) Cilk(TM) Plus reducers",
* described in file reducers.md, and particularly with @ref reducers_using,
* before trying to use the information in this file.
*
* @section redostream_usage Usage Example
*
* One of the most common debugging techniques is adding `print` statements
* to the code being debugged. When the code is parallelized, the results can
* be less than satisfactory, as output from multiple strands is mingled in an
* unpredictable way. Like other reducers, an ostream reducer requires minimal
* recoding to guarantee that the output from parallelized computation will be
* ordered the same as though the computation were executed serially.
*
* cilk::reducer<cilk::op_ostream> r(std::cerr);
* cilk_for (int i = 0; i != data.size(); ++i) {
* *r << "Iteration " << i << ":\n";
* ... some computation ...
* *r << " Step 1:" << some information;
* ... some more computation ...
* *r << " Step 2:" << some more information;
* ... still more computation ...
* *r << " Step 3:" << still more information;
* }
*
* Output on standard error:
*
* Iteration 1:
* Step 1: ...
* Step 2: ...
* Step 3: ...
* Iteration 2:
* Step 1: ...
* Step 2: ...
* Step 3: ...
* Iteration 3:
* Step 1: ...
* Step 2: ...
* Step 3: ...
* ...
*
* @section redostream_overview Overview
*
* An "ostream reducer" is not really a reducer. It uses the reducer
* technology to coordinate operations on parallel strands to achieve
* the same behavior in a parallel computation that would be seen in a
* serial computation, but it does not have a monoid. It has a "monoid
* class," because that is part of the implementation framework, but it
* does not represent a mathematical monoid: there is no value type, no
* associative operation, and no identity value. The reducer is used for
* its side effect rather than to construct a value.
*
* You might think of an ostream reducer as a relative of a
* @ref ReducersString "string reducer" which uses stream output
* syntax (`stream << value`) instead of string append syntax
* (`string += value`), and which writes its result string to an
* ostream instead of making it available as the reducer value.
*
* Another difference is that "real" reducers protect their contained
* value quite strongly from improper access by the user. Ostream reducers,
* on the other hand, pretty much have to expose the ostream, since normal
* use of an ostream involves accessing its internal state. Furthermore,
* the ostream reducer just coordinates output to an existing ostream -
* there is nothing to keep the user from writing directly to the attached
* stream, with unpredictable results.
*
* @section redostream_operations Operations
*
* In the operation descriptions below, the type name `Ostream` refers to the
* reducer's ostream type, `std::basic_ostream<Char, Traits>`.
*
* @subsection redostream_constructors Constructors
*
* The only constructor is
*
* reducer(const Ostream& os)
*
* This creates a reducer that is associated with the existing ostream `os`.
* Anything "written to" the reducer will (eventually) be written to `os`.
*
* @subsection redostream_get_set Set and Get
*
* Just as a stream does not have a "value," neither does an ostream
* reducer. Therefore, none of the usual `set_value`, `get_value`,
* `move_in`, or `move_out` functions are available for ostream reducers.
*
* @subsection redostream_initial Initial Values
*
* Ostream reducers do not have default constructors.
*
* @subsection redostream_view_ops View Operations
*
* An ostream reducer view is actually a kind of `std::ostream`. Therefore,
* any operation that can be used on an ostream can be used on an ostream
* reducer view. For example:
*
* reducer<op_ostream> r(cout);
* *r << setw(5) << (x=1) << endl;
*
*
* @section redostream_performance Performance Considerations
*
* Ostream reducers work by creating a string stream for each non-leftmost
* view. When two strands are merged, the contents of the string buffer of the
* right view are written to the left view. Since all non-leftmost strands are
* eventually merged, all output is eventually written to the associated
* ostream.
*
* This implementation has two consequences.
*
* First, all output written to an ostream reducer on a stolen strand is kept
* in memory (in a string buffer) until the strand is merged with the leftmost
* strand. This means that some portion of the output written to an ostream
* reducer during a parallel computation - half of the total output, on
* average - will temporarily be held in memory during the computation.
* Obviously, ostream reducers will work better for small and moderate amounts
* of output.
*
* Second, buffered ostream reducer content must be copied at every merge.
* The total amount of copying is potentially proportional to the total amount
* of output multiplied by the number of strands stolen during the computation.
*
* In short, writing to an ostream in a parallel computation with an ostream
* reducer will always be less efficient than writing the same output directly
* to the ostream in a serial computation. The value of the ostream
* reducer is not in the writing of the ostream itself, but in removing the
* race and serialization obstacles that the ostream output would cause in an
* otherwise parallelizable computation.
*
*
* @section redostream_state Stream State
*
* The reducer implementation can correctly order the output that is written
* to an ostream. However, an ostream has additional state that controls its
* behavior, such as its formatting attributes, error state, extensible arrays, * and registered callbacks. If these are modified during the computation, the * reducer implementation cannot guarantee that they will be the same in a
* parallel computation as in a serial computation. In particular:
*
* - In the serial execution, the ostream state in the continuation of a
* spawn will be the same as the state at the end of the spawned function.
* In the parallel execution, if the continuation is stolen, its view will
* contain a newly created ostream with the default initial state.
* - In the serial execution, the ostream state following a sync is the same
* as the state before the sync. In the parallel execution, if the
* continuation is stolen, then the state following the sync will be the
* same as the state at the end of some spawned function.
*
* In short, you must not make any assumptions about the stream state of an
* ostream reducer:
*
* - Following a `cilk_spawn`.
* - Following a `cilk_sync`.
* - At the start of an iteration of a `cilk_for` loop.
* - Following the completion of a `cilk_for` loop.
*
* @section redostream_types Type and Operator Requirements
*
* `std::basic_ostream<Char, Traits>` must be a valid type.
*/
namespace cilk {
/** @ingroup ReducersOstream */
//@{
/** The ostream reducer view class.
*
* This is the view class for reducers created with
* `cilk::reducer< cilk::op_basic_ostream<Char, Traits> >`. It holds the
* actual ostream for a parallel strand, and allows only stream output
* operations to be performed on it.
*
* @note The reducer "dereference" operation (`reducer::operator *()`)
* yields a reference to the view. Thus, for example, the view
* class's `<<` operation would be used in an expression like
* `*r << "x = " << x`, where `r` is an ostream reducer.
*
* @tparam Char The ostream element type (not the ostream type).
* @tparam Traits The character traits type.
*
* @see ReducersOstream
* @see op_basic_ostream
*/
template<typename Char, typename Traits>
class op_basic_ostream_view : public std::basic_ostream<Char, Traits>
{
typedef std::basic_ostream<Char, Traits> base;
typedef std::basic_ostream<Char, Traits> ostream_type;
// A non-leftmost view is associated with a private string buffer. (The
// leftmost view is associated with the buffer of the reducer's associated
// ostream, so its private buffer is unused.)
//
std::basic_stringbuf<Char, Traits> m_buffer;
public:
/** Value type. Required by @ref monoid_with_view.
*/
typedef ostream_type value_type;
/** Reduce operation. Required by @ref monoid_with_view.
*/
void reduce(op_basic_ostream_view* other)
{
// Writing an empty buffer results in failure. Testing `sgetc()` is the
// easiest way of checking for an empty buffer.
if (other->m_buffer.sgetc() != Traits::eof()) {
*this << (&other->m_buffer);
}
}
/** Non-leftmost (identity) view constructor. The view is associated with
* its internal buffer. Required by @ref monoid_base.
*/
op_basic_ostream_view() : base(&m_buffer) {}
/** Leftmost view constructor. The view is associated with an existing
* ostream.
*/
op_basic_ostream_view(const ostream_type& os) : base(0)
{
base::rdbuf(os.rdbuf()); // Copy stream buffer
base::flags(os.flags()); // Copy formatting flags
base::setstate(os.rdstate()); // Copy error state
}
/** Sets/gets.
*
* These are all no-ops.
*/
//@{
void view_set_value(const value_type&)
{ assert("set_value() is not allowed on ostream reducers" && 0); }
const value_type& view_get_value() const
{ assert("get_value() is not allowed on ostream reducers" && 0);
return *this; }
typedef value_type const& return_type_for_get_value;
void view_move_in(const value_type&)
{ assert("move_in() is not allowed on ostream reducers" && 0); }
void view_move_out(const value_type&)
{ assert("move_out() is not allowed on ostream reducers" && 0); }
//@}
};
/** Ostream monoid class. Instantiate the cilk::reducer template class with an
* op_basic_ostream monoid to create an ostream reducer class:
*
* cilk::reducer< cilk::op_basic_string<char> > r;
*
* @tparam Char The stream element type (not the stream type).
* @tparam Traits The character traits type.
*
* @see ReducersOstream
* @see op_basic_ostream_view
* @see reducer_ostream
* @see op_ostream
* @see op_wostream
*/
template<typename Char,
typename Traits = std::char_traits<Char>,
bool Align = false>
class op_basic_ostream :
public monoid_with_view< op_basic_ostream_view<Char, Traits>, Align >
{
typedef monoid_with_view< op_basic_ostream_view<Char, Traits>, Align >
base;
typedef std::basic_ostream<Char, Traits> ostream_type;
typedef provisional_guard<typename base::view_type> view_guard;
public:
/** View type of the monoid.
*/
typedef typename base::view_type view_type;
/** @name Construct function.
*
* The only supported ostream reducer constructor takes a reference to
* an existing ostream.
*
* @param os The ostream destination for receive all data written to the
* reducer.
*/
static void construct(
op_basic_ostream* monoid,
view_type* view,
const ostream_type& os)
{
view_guard vg( new((void*) view) view_type(os) );
vg.confirm_if( new((void*) monoid) op_basic_ostream );
}
};
/**
* Convenience typedef for narrow ostreams.
*/
typedef op_basic_ostream<char> op_ostream;
/**
* Convenience typedef for wide ostreams.
*/
typedef op_basic_ostream<wchar_t> op_wostream;
/// @cond internal
class reducer_ostream;
/** Metafunction specialization for reducer conversion.
*
* This specialization of the @ref legacy_reducer_downcast template class
* defined in reducer.h causes the `reducer<op_basic_ostream<char> >` class
* to have an `operator reducer_ostream& ()` conversion operator that
* statically downcasts the `reducer<op_basic_ostream<char> >` to
* `reducer_ostream`. (The reverse conversion, from `reducer_ostream` to
* `reducer<op_basic_ostream<char> >`, is just an upcast, which is provided
* for free by the language.)
*/
template<bool Align>
struct legacy_reducer_downcast<
reducer<op_basic_ostream<char, std::char_traits<char>, Align> > >
{
typedef reducer_ostream type;
};
/// @endcond
/** Deprecated ostream reducer class.
*
* reducer_ostream is the same as @ref cilk::reducer<@ref op_ostream>, except
* that reducer_ostream is a proxy for the contained view, so that ostream
* operations can be applied directly to the reducer. For example, a number is
* written to a `reducer<op_ostream>` with `*r << x`, but a number can be
* written to a `reducer_ostream` with `r << x`.
*
* @deprecated Users are strongly encouraged to use `reducer<monoid>`
* reducers rather than the old wrappers like reducer_ostream. The
* `reducer<monoid>` reducers show the reducer/monoid/view
* architecture more clearly, are more consistent in their
* implementation, and present a simpler model for new
* user-implemented reducers.
*
* @note Implicit conversions are provided between `%reducer_ostream`
* and `reducer<%op_ostream>`. This allows incremental code
* conversion: old code that used `%reducer_ostream` can pass a
* `%reducer_ostream` to a converted function that now expects a
* pointer or reference to a `reducer<%op_ostream>`, and vice versa.
*
* @tparam Char The stream element type (not the stream type).
* @tparam Traits The character traits type.
*
* @see op_ostream
* @see reducer
* @see ReducersOstream
*/
class reducer_ostream :
public reducer<op_basic_ostream<char, std::char_traits<char>, true> >
{
typedef reducer<op_basic_ostream<char, std::char_traits<char>, true> > base;
using base::view;
public:
/// The view type for the reducer.
typedef base::view_type View;
/// The monoid type for the reducer.
typedef base::monoid_type Monoid;
/** Constructs an initial `reducer_ostream` from a `std::ostream`. The
* specified stream is used as the eventual destination for all text
* streamed to this hyperobject.
*/
explicit reducer_ostream(const std::ostream &os) : base(os) {}
/** Returns a modifiable reference to the underlying 'ostream' object.
*/
std::ostream& get_reference() { return view(); }
/** Writes to the ostream.
*/
template<typename T>
std::ostream& operator<< (const T &v)
{
return view() << v;
}
/**
* Calls a manipulator.
*
* @param _Pfn Pointer to the manipulator function.
*/
reducer_ostream& operator<< (std::ostream &(*_Pfn)(std::ostream &))
{
(*_Pfn)(view());
return *this;
}
/** @name Dereference
* @details Dereferencing a wrapper is a no-op. It simply returns the
* wrapper. Combined with the rule that the wrapper forwards view
* operations to its contained view, this means that view operations can
* be written the same way on reducers and wrappers, which is convenient
* for incrementally converting old code using wrappers to use reducers
* instead. That is:
*
* reducer<op_ostream> r;
* *r << "a"; // *r returns the view
* // operator<<() is a view member function
*
* reducer_ostream w;
* *w << "a"; // *w returns the wrapper
* // operator<<() is a wrapper member function
* // that calls the corresponding view function
*/
//@{
reducer_ostream& operator*() { return *this; }
reducer_ostream const& operator*() const { return *this; }
reducer_ostream* operator->() { return this; }
reducer_ostream const* operator->() const { return this; }
//@}
/** @name Upcast
* @details In Intel Cilk Plus library 0.9, reducers were always cache-aligned.
* In library 1.0, reducer cache alignment is optional. By default,
* reducers are unaligned (i.e., just naturally aligned), but legacy
* wrappers inherit from cache-aligned reducers for binary compatibility.
*
* This means that a wrapper will automatically be upcast to its aligned
* reducer base class. The following conversion operators provide
* pseudo-upcasts to the corresponding unaligned reducer class.
*/
//@{
operator reducer<op_ostream>& ()
{
return *reinterpret_cast< reducer<op_ostream>* >(this);
}
operator const reducer<op_ostream>& () const
{
return *reinterpret_cast< const reducer<op_ostream>* >(this);
}
//@}
};
} // namespace cilk
#endif // REDUCER_OSTREAM_H_INCLUDED