Line data Source code
1 : /** \file ompLoopWatcher.hpp
2 : * \brief Track iterations in an OMP parallelized looop.
3 : *
4 : * \author Jared R. Males (jaredmales@gmail.com)
5 : *
6 : * \ingroup utils_files
7 : *
8 : */
9 :
10 : //***********************************************************************//
11 : // Copyright 2015, 2016, 2017 Jared R. Males (jaredmales@gmail.com)
12 : //
13 : // This file is part of mxlib.
14 : //
15 : // mxlib is free software: you can redistribute it and/or modify
16 : // it under the terms of the GNU General Public License as published by
17 : // the Free Software Foundation, either version 3 of the License, or
18 : // (at your option) any later version.
19 : //
20 : // mxlib is distributed in the hope that it will be useful,
21 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
22 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 : // GNU General Public License for more details.
24 : //
25 : // You should have received a copy of the GNU General Public License
26 : // along with mxlib. If not, see <http://www.gnu.org/licenses/>.
27 : //***********************************************************************//
28 :
29 : #ifndef ompLoopWatcher_hpp
30 : #define ompLoopWatcher_hpp
31 :
32 : #include <iostream>
33 : #include "../sys/timeUtils.hpp"
34 :
35 : namespace mx
36 : {
37 : namespace ipc
38 : {
39 :
40 : /// A class to track the number of iterations in an OMP parallelized loop.
41 : /** Uses omp critical directives to deconflict updates by different loops. Example:
42 : * \code
43 : ompLoopWatcher<> watcher(1000, std::cout); //Uses defaults
44 :
45 : #pragma omp parallel for
46 : int( i=0; i<1000; ++i)
47 : {
48 : watcher.incrementAndOutputStatus();
49 : //Do loop work
50 : ...
51 : }
52 : \endcode
53 : * This will result in the following output
54 : \verbatim
55 : 1 / 1000 (0.1%)
56 : 2 / 1000 (0.2%)
57 : 3 / 1000 (0.3%)
58 : \endverbatim
59 : * and so on.
60 : *
61 : * \note This can reduce performance due to critical points it creates if used in fast loops (i.e. do not use inside
62 : the inner most loop!).
63 : *
64 : * The behavior of the output is controlled through template parameters. A different output-type can be specified,
65 : which needs to accept size_t, and optionally
66 : * float, and character input using the << operator.
67 : *
68 : * \tparam outputT a type which accepts size_t, float, and character input via the << operator (default is
69 : std::ostream).
70 : * \tparam _printPretty flag to control whether the output is nicely formatted, if false then just the numbers are sent
71 : to the output with no spaces or delimiters (default is true).
72 : * \tparam _printLoops flag to control whether the number of loops is sent to the output each time (default is true).
73 : * \tparam _printPercent flag to control whether the percentage complete is calculated (default is true).
74 : * \tparam _printNLine flag to control whether the newline '\\n' is sent ot output (default is false). If false, '\\r'
75 : is written at end of output.
76 : * \tparam _time flag to control whether time is tracked. Default is true.
77 : * \ingroup mtutils
78 : */
79 : template <class _outputT = std::ostream,
80 : bool _printPretty = true,
81 : bool _printLoops = true,
82 : bool _printPercent = true,
83 : bool _printNLine = false,
84 : bool _time = true>
85 : class ompLoopWatcher
86 : {
87 : public:
88 : typedef _outputT outputT;
89 :
90 : protected:
91 : size_t _nLoops; ///< The total number of loops
92 : size_t _counter; ///< The current counter
93 :
94 : double t0;
95 : double t1;
96 :
97 : outputT *_output; ///< Pointer to the instance of type outputT
98 :
99 : size_t m_outputInterval {10};
100 : size_t m_outputCount {0};
101 :
102 : /// Increment the counter
103 45 : void _increment()
104 : {
105 45 : ++_counter;
106 : if( _time )
107 : {
108 35 : t1 = sys::get_curr_time();
109 : }
110 45 : }
111 :
112 : /// Advance the counter by a number of steps
113 11 : void _advance( size_t diff_count )
114 : {
115 11 : _counter += diff_count;
116 : if( _time )
117 : {
118 11 : t1 = sys::get_curr_time();
119 : }
120 11 : }
121 :
122 : /// Perform the output
123 64 : void _outputStatus()
124 : {
125 64 : if(m_outputInterval > 1)
126 : {
127 64 : if(m_outputCount < (m_outputInterval-1))
128 : {
129 58 : ++m_outputCount;
130 58 : return;
131 : }
132 :
133 6 : m_outputCount = 0;
134 : }
135 :
136 : if( _printPretty )
137 : {
138 4 : ( *_output ) << _counter;
139 : if( _printLoops )
140 : {
141 4 : ( *_output ) << " / " << _nLoops;
142 : }
143 : if( _printPercent )
144 : {
145 4 : ( *_output ) << " (" << 100.0 * ( (float)_counter ) / _nLoops << "%)";
146 : }
147 : if( _time )
148 : {
149 4 : ( *_output ) << " " << ( t1 - t0 ) / _counter << " s/loop ";
150 4 : ( *_output ) << " ~" << ( _nLoops - _counter ) * ( t1 - t0 ) / _counter << " s left";
151 : }
152 :
153 : if( _printNLine )
154 : {
155 1 : ( *_output ) << '\n';
156 : }
157 : if( !_printNLine )
158 : {
159 3 : ( *_output ) << " \r";
160 : }
161 :
162 4 : ( *_output ) << std::flush;
163 : }
164 : else
165 : {
166 2 : ( *_output ) << _counter;
167 : if( _printLoops )
168 : {
169 1 : ( *_output ) << _nLoops;
170 : }
171 : if( _printPercent )
172 : {
173 1 : ( *_output ) << 100.0 * ( (float)_counter ) / _nLoops;
174 : }
175 : if( _time )
176 : {
177 1 : ( *_output ) << " " << t0 << " " << t1;
178 : }
179 : if( _printNLine )
180 : {
181 1 : ( *_output ) << '\n';
182 : }
183 : }
184 : }
185 :
186 : private:
187 : // Default C'tor is private since you always have to give the number of loops.
188 : ompLoopWatcher()
189 : {
190 : }
191 :
192 : public:
193 : /// Constructor
194 : /** Registers the output and sets the number of loops.
195 : *
196 : * \param nLoops is the total number of loops.
197 : * \param output is the instance of type outputT to which the output will be sent.
198 : */
199 5 : ompLoopWatcher( size_t nLoops, outputT &output )
200 5 : {
201 5 : _output = &output;
202 5 : _nLoops = nLoops;
203 5 : _counter = 0;
204 :
205 : if( _time )
206 4 : t0 = sys::get_curr_time();
207 5 : }
208 :
209 : /// Increment the counter.
210 : /** Call this once per loop. It contains an omp critical directive.
211 : */
212 1 : void increment()
213 : {
214 2 : #pragma omp critical
215 1 : _increment();
216 1 : }
217 :
218 : /// Advance the counter by a number of steps.
219 : /** Call this once per loop. It contains an omp critical directive.
220 : */
221 1 : void advance( size_t diff_count )
222 : {
223 2 : #pragma omp critical
224 1 : _advance( diff_count );
225 1 : }
226 :
227 : /// Output current status.
228 : /** Call this whenever you want a status update. It contains an omp critical directive.
229 : */
230 10 : void outputStatus()
231 : {
232 20 : #pragma omp critical
233 10 : _outputStatus();
234 10 : }
235 :
236 : /// Output a final report about elapsed time
237 4 : void outputFinalStatus()
238 : {
239 : if( _printPretty )
240 : {
241 : if( _time )
242 : {
243 2 : ( *_output ) << ( t1 - t0 ) << " s elapsed ";
244 2 : ( *_output ) << " (" << ( t1 - t0 ) / _counter << " s/loop) ";
245 : }
246 :
247 : if( _printNLine )
248 : {
249 1 : ( *_output ) << '\n';
250 : }
251 : if( !_printNLine )
252 : {
253 1 : ( *_output ) << " \r";
254 : }
255 :
256 2 : ( *_output ) << std::flush;
257 : }
258 : else
259 : {
260 : if( _time )
261 : {
262 1 : ( *_output ) << " " << t0 << " " << t1;
263 : }
264 : if( _printNLine )
265 : {
266 1 : ( *_output ) << '\n';
267 : }
268 : }
269 4 : }
270 :
271 : /// Increment and output status.
272 : /** Call this to increment and then give a status update. Has only one omp critical directive for the two steps.
273 : */
274 44 : void incrementAndOutputStatus()
275 : {
276 88 : #pragma omp critical
277 : {
278 44 : _increment();
279 44 : _outputStatus();
280 : }
281 44 : }
282 :
283 : /// Advance and output status.
284 : /** Call this to advance and then give a status update. Has only one omp critical directive for the two steps.
285 : */
286 10 : void advanceAndOutputStatus( size_t diff_count )
287 : {
288 20 : #pragma omp critical
289 : {
290 10 : _advance( diff_count );
291 10 : _outputStatus();
292 : }
293 10 : }
294 :
295 : /// Clear the output line of any text
296 : /** If printing pretty and not printing new lines, this writes a line of blank
297 : * spaces and carriage returns to clear out the last status update.
298 : */
299 2 : void clearOutput()
300 : {
301 : if( _printPretty )
302 : {
303 : if( !_printNLine )
304 : {
305 1 : ( *_output ) << " \r";
306 1 : ( *_output ) << std::flush;
307 : }
308 : }
309 2 : }
310 : };
311 :
312 : } // namespace ipc
313 :
314 : } // namespace mx
315 :
316 : #endif // ompLoopWatcher_hpp
|