tzh
2024-08-22 c7d0944258c7d0943aa7b2211498fd612971ce27
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
/*  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