vSMC  v3.0.0
Scalable Monte Carlo
progress.hpp
Go to the documentation of this file.
1 //============================================================================
2 // vSMC/include/vsmc/utility/progress.hpp
3 //----------------------------------------------------------------------------
4 // vSMC: Scalable Monte Carlo
5 //----------------------------------------------------------------------------
6 // Copyright (c) 2013-2016, Yan Zhou
7 // All rights reserved.
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions are met:
11 //
12 // Redistributions of source code must retain the above copyright notice,
13 // this list of conditions and the following disclaimer.
14 //
15 // Redistributions in binary form must reproduce the above copyright notice,
16 // this list of conditions and the following disclaimer in the documentation
17 // and/or other materials provided with the distribution.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
20 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 // POSSIBILITY OF SUCH DAMAGE.
30 //============================================================================
31 
32 #ifndef VSMC_UTILITY_PROGRESS_HPP
33 #define VSMC_UTILITY_PROGRESS_HPP
34 
35 #include <vsmc/internal/common.hpp>
37 
38 namespace vsmc
39 {
40 
43 class Progress
44 {
45  public:
47  Progress(std::ostream &os = std::cout)
48  : thread_ptr_(nullptr)
49  , interval_ms_(0)
50  , iter_(0)
51  , total_(0)
52  , length_(0)
53  , show_iter_(true)
54  , print_first_(true)
55  , in_progress_(false)
56  , num_equal_(0)
57  , percent_(0)
58  , seconds_(0)
59  , last_iter_(0)
60  , cstr_bar_()
61  , cstr_percent_()
62  , cstr_time_()
63  , cstr_iter_()
64  , os_(os)
65  {
66  }
67 
68  Progress(const Progress &other) = delete;
69 
70  Progress &operator=(const Progress &other) = delete;
71 
72  ~Progress() { join(); }
73 
83  void start(std::size_t total, const std::string &msg = std::string(),
84  std::size_t length = 0, bool show_iter = true, double interval_s = 0.1)
85  {
86  total_ = total;
87  msg_ = msg;
88  length_ = length;
89  show_iter_ = show_iter;
90  interval_ms_ = std::max(1.0, interval_s * 1000);
91 
92  watch_.reset();
93  watch_.start();
94  fork();
95  }
96 
103  void stop(bool finished = true)
104  {
105  join();
106  if (finished && iter_ < total_)
107  iter_ = total_;
108  print_stop();
109  watch_.stop();
110  }
111 
117  void increment(std::size_t step = 1) { iter_ += step; }
118 
120  void message(const std::string &msg) { msg_ = msg; }
121 
122  private:
123  static constexpr std::size_t max_val_ =
124  std::numeric_limits<std::size_t>::max();
125 
126  StopWatch watch_;
127  std::thread *thread_ptr_;
128 
129  double interval_ms_;
130  std::atomic<std::size_t> iter_;
131  std::size_t total_;
132  std::size_t length_;
133  bool show_iter_;
134  bool print_first_;
135  bool in_progress_;
136 
137  std::size_t num_equal_;
138  std::size_t percent_;
139  std::size_t seconds_;
140  std::size_t last_iter_;
141 
142  std::string msg_;
143  char cstr_bar_[128];
144  char cstr_percent_[32];
145  char cstr_time_[32];
146  char cstr_iter_[64];
147 
148  std::ostream &os_;
149 
150  void fork()
151  {
152  join();
153  iter_ = 0;
154  print_first_ = true;
155  in_progress_ = true;
156  thread_ptr_ = new std::thread([this]() { print_start(); });
157  }
158 
159  void join()
160  {
161  in_progress_ = false;
162  if (thread_ptr_ != nullptr) {
163  if (thread_ptr_->joinable())
164  thread_ptr_->join();
165  delete thread_ptr_;
166  thread_ptr_ = nullptr;
167  }
168  }
169 
170  void print_start()
171  {
172  while (in_progress_) {
173  print_progress();
174  os_ << '\r' << std::flush;
175  std::this_thread::sleep_for(std::chrono::milliseconds(
176  static_cast<std::chrono::milliseconds::rep>(interval_ms_)));
177  }
178  }
179 
180  void print_stop()
181  {
182  print_progress();
183  os_ << '\n' << std::flush;
184  }
185 
186  void print_progress()
187  {
188  watch_.stop();
189  watch_.start();
190  const std::size_t seconds = static_cast<std::size_t>(watch_.seconds());
191 
192  std::size_t iter = iter_;
193  std::size_t display_iter = std::min(iter, total_);
194  std::size_t num_equal = (length_ == 0 || total_ == 0) ? 0 : length_ *
195  display_iter / total_;
196  std::size_t percent = total_ == 0 ? 100 : 100 * display_iter / total_;
197 
198  if (print_first_) {
199  print_first_ = false;
200  num_equal_ = max_val_;
201  percent_ = max_val_;
202  seconds_ = max_val_;
203  last_iter_ = max_val_;
204  }
205 
206  if (length_ != 0 && num_equal_ != num_equal) {
207  num_equal_ = num_equal;
208  std::size_t num_space = length_ - num_equal;
209  std::size_t num_dash = 0;
210  if (num_space > 0) {
211  num_dash = 1;
212  --num_space;
213  }
214 
215  char *cstr = cstr_bar_;
216  std::size_t offset = 0;
217  cstr[offset++] = '[';
218  for (std::size_t i = 0; i != num_equal; ++i)
219  cstr[offset++] = '=';
220  for (std::size_t i = 0; i != num_dash; ++i)
221  cstr[offset++] = '-';
222  for (std::size_t i = 0; i != num_space; ++i)
223  cstr[offset++] = ' ';
224  cstr[offset++] = ']';
225  cstr[offset++] = '\0';
226  }
227 
228  if (percent_ != percent) {
229  percent_ = percent;
230  const std::size_t num_space = 3 - uint_digit(percent);
231 
232  char *cstr = cstr_percent_;
233  std::size_t offset = 0;
234  cstr[offset++] = '[';
235  for (std::size_t i = 0; i != num_space; ++i)
236  cstr[offset++] = ' ';
237  uint_to_char(percent, cstr, offset);
238  cstr[offset++] = '%';
239  cstr[offset++] = ']';
240  cstr[offset++] = '\0';
241  }
242 
243  if (seconds_ != seconds) {
244  seconds_ = seconds;
245  const std::size_t display_second = seconds % 60;
246  const std::size_t display_minute = (seconds / 60) % 60;
247  const std::size_t display_hour = seconds / 3600;
248 
249  char *cstr = cstr_time_;
250  std::size_t offset = 0;
251  cstr[offset++] = '[';
252  if (display_hour > 0) {
253  uint_to_char(display_hour, cstr, offset);
254  cstr[offset++] = ':';
255  }
256  cstr[offset++] = static_cast<char>('0' + display_minute / 10);
257  cstr[offset++] = static_cast<char>('0' + display_minute % 10);
258  cstr[offset++] = ':';
259  cstr[offset++] = static_cast<char>('0' + display_second / 10);
260  cstr[offset++] = static_cast<char>('0' + display_second % 10);
261  cstr[offset++] = ']';
262  cstr[offset++] = '\0';
263  }
264 
265  if (show_iter_ && last_iter_ != iter) {
266  last_iter_ = iter;
267  const std::size_t dtotal = uint_digit(total_);
268  const std::size_t diter = uint_digit(iter);
269  const std::size_t num_space = dtotal > diter ? dtotal - diter : 0;
270 
271  char *cstr = cstr_iter_;
272  std::size_t offset = 0;
273  cstr[offset++] = '[';
274  for (std::size_t i = 0; i < num_space; ++i)
275  cstr[offset++] = ' ';
276  uint_to_char(iter, cstr, offset);
277  cstr[offset++] = '/';
278  uint_to_char(total_, cstr, offset);
279  cstr[offset++] = ']';
280  cstr[offset++] = '\0';
281  }
282 
283  os_ << ' ';
284  if (length_ != 0)
285  os_ << cstr_bar_;
286  os_ << cstr_percent_;
287  os_ << cstr_time_;
288  if (show_iter_)
289  os_ << cstr_iter_;
290  if (msg_.size() != 0)
291  os_ << '[' << msg_ << ']';
292  }
293 
294  template <typename UIntType>
295  static void uint_to_char(UIntType num, char *cstr, std::size_t &offset)
296  {
297  if (num == 0) {
298  cstr[offset++] = '0';
299  return;
300  }
301 
302  char utmp[32];
303  std::size_t unum = 0;
304  while (num) {
305  utmp[unum++] = static_cast<char>('0' + num % 10);
306  num /= 10;
307  }
308  for (std::size_t i = unum; i != 0; --i)
309  cstr[offset++] = utmp[i - 1];
310  }
311 
312  template <typename UIntType>
313  static std::size_t uint_digit(UIntType num)
314  {
315  if (num == 0)
316  return 1;
317 
318  std::size_t digit = 0;
319  while (num != 0) {
320  ++digit;
321  num /= 10;
322  }
323 
324  return digit;
325  }
326 }; // class Progress
327 
328 } // namespace vsmc
329 
330 #endif // VSMC_UTILITY_PROGRESS_HPP
Progress(std::ostream &os=std::cout)
Construct a Progress with an output stream.
Definition: progress.hpp:47
Definition: monitor.hpp:48
std::basic_ostream< CharT, Traits > & ostream(std::basic_ostream< CharT, Traits > &os, const std::array< T, N > &ary)
Definition: basic.hpp:215
void stop(bool finished=true)
Stop to print the progress.
Definition: progress.hpp:103
void increment(std::size_t step=1)
Increment the iteration count.
Definition: progress.hpp:117
void reset()
Stop and reset the elapsed time to zero.
Definition: stop_watch.hpp:172
void start(std::size_t total, const std::string &msg=std::string(), std::size_t length=0, bool show_iter=true, double interval_s=0.1)
Start to print the progress.
Definition: progress.hpp:83
bool stop()
Stop the watch, no effect if already stopped.
Definition: stop_watch.hpp:158
Progress & operator=(const Progress &other)=delete
bool start()
Start the watch, no effect if already started.
Definition: stop_watch.hpp:141
Display a progress bar while algorithm proceed.
Definition: progress.hpp:43
double seconds() const
Return the accumulated elapsed time in seconds.
Definition: stop_watch.hpp:208
void message(const std::string &msg)
Set a new message for display.
Definition: progress.hpp:120