vSMC
vSMC: 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() { join(); }
69 
79  void start(std::size_t total, const std::string &msg = std::string(),
80  std::size_t length = 0, bool show_iter = true, double interval_s = 0.1)
81  {
82  total_ = total;
83  msg_ = msg;
84  length_ = length;
85  show_iter_ = show_iter;
86  interval_ms_ = std::max(1.0, interval_s * 1000);
87 
88  watch_.reset();
89  watch_.start();
90  fork();
91  }
92 
99  void stop(bool finished = true)
100  {
101  join();
102  if (finished && iter_ < total_)
103  iter_ = total_;
104  print_stop_(this);
105  watch_.stop();
106  }
107 
113  void increment(std::size_t step = 1) { iter_ += step; }
114 
116  void message(const std::string &msg) { msg_ = msg; }
117 
118  private:
119  static constexpr std::size_t max_val_ =
120  std::numeric_limits<std::size_t>::max();
121 
122  StopWatch watch_;
123  std::thread *thread_ptr_;
124 
125  double interval_ms_;
126  std::atomic<std::size_t> iter_;
127  std::size_t total_;
128  std::size_t length_;
129  bool show_iter_;
130  bool print_first_;
131  bool in_progress_;
132 
133  std::size_t num_equal_;
134  std::size_t percent_;
135  std::size_t seconds_;
136  std::size_t last_iter_;
137 
138  std::string msg_;
139  char cstr_bar_[128];
140  char cstr_percent_[32];
141  char cstr_time_[32];
142  char cstr_iter_[64];
143 
144  std::ostream &os_;
145 
146  void fork()
147  {
148  join();
149  iter_ = 0;
150  print_first_ = true;
151  in_progress_ = true;
152  thread_ptr_ = new std::thread(print_start_, this);
153  }
154 
155  void join()
156  {
157  in_progress_ = false;
158  if (thread_ptr_ != nullptr) {
159  if (thread_ptr_->joinable())
160  thread_ptr_->join();
161  delete thread_ptr_;
162  thread_ptr_ = nullptr;
163  }
164  }
165 
166  static void print_start_(void *context)
167  {
168  Progress *ptr = static_cast<Progress *>(context);
169  while (ptr->in_progress_) {
170  print_progress(context);
171  ptr->os_ << '\r' << std::flush;
172  std::this_thread::sleep_for(std::chrono::milliseconds(
173  static_cast<std::chrono::milliseconds::rep>(
174  ptr->interval_ms_)));
175  }
176  }
177 
178  static void print_stop_(void *context)
179  {
180  Progress *ptr = static_cast<Progress *>(context);
181  print_progress(context);
182  ptr->os_ << '\n' << std::flush;
183  }
184 
185  static void print_progress(void *context)
186  {
187  Progress *ptr = static_cast<Progress *>(context);
188 
189  ptr->watch_.stop();
190  ptr->watch_.start();
191  const std::size_t seconds =
192  static_cast<std::size_t>(ptr->watch_.seconds());
193 
194  std::size_t iter = ptr->iter_;
195  std::size_t display_iter = std::min(iter, ptr->total_);
196  std::size_t num_equal = (ptr->length_ == 0 || ptr->total_ == 0) ?
197  0 :
198  ptr->length_ * display_iter / ptr->total_;
199  std::size_t percent =
200  ptr->total_ == 0 ? 100 : 100 * display_iter / ptr->total_;
201 
202  if (ptr->print_first_) {
203  ptr->print_first_ = false;
204  ptr->num_equal_ = max_val_;
205  ptr->percent_ = max_val_;
206  ptr->seconds_ = max_val_;
207  ptr->last_iter_ = max_val_;
208  }
209 
210  if (ptr->length_ != 0 && ptr->num_equal_ != num_equal) {
211  ptr->num_equal_ = num_equal;
212  std::size_t num_space = ptr->length_ - num_equal;
213  std::size_t num_dash = 0;
214  if (num_space > 0) {
215  num_dash = 1;
216  --num_space;
217  }
218 
219  char *cstr = ptr->cstr_bar_;
220  std::size_t offset = 0;
221  cstr[offset++] = '[';
222  for (std::size_t i = 0; i != num_equal; ++i)
223  cstr[offset++] = '=';
224  for (std::size_t i = 0; i != num_dash; ++i)
225  cstr[offset++] = '-';
226  for (std::size_t i = 0; i != num_space; ++i)
227  cstr[offset++] = ' ';
228  cstr[offset++] = ']';
229  cstr[offset++] = '\0';
230  }
231 
232  if (ptr->percent_ != percent) {
233  ptr->percent_ = percent;
234  const std::size_t num_space = 3 - uint_digit(percent);
235 
236  char *cstr = ptr->cstr_percent_;
237  std::size_t offset = 0;
238  cstr[offset++] = '[';
239  for (std::size_t i = 0; i != num_space; ++i)
240  cstr[offset++] = ' ';
241  uint_to_char(percent, cstr, offset);
242  cstr[offset++] = '%';
243  cstr[offset++] = ']';
244  cstr[offset++] = '\0';
245  }
246 
247  if (ptr->seconds_ != seconds) {
248  ptr->seconds_ = seconds;
249  const std::size_t display_second = seconds % 60;
250  const std::size_t display_minute = (seconds / 60) % 60;
251  const std::size_t display_hour = seconds / 3600;
252 
253  char *cstr = ptr->cstr_time_;
254  std::size_t offset = 0;
255  cstr[offset++] = '[';
256  if (display_hour > 0) {
257  uint_to_char(display_hour, cstr, offset);
258  cstr[offset++] = ':';
259  }
260  cstr[offset++] = '0' + static_cast<char>(display_minute / 10);
261  cstr[offset++] = '0' + static_cast<char>(display_minute % 10);
262  cstr[offset++] = ':';
263  cstr[offset++] = '0' + static_cast<char>(display_second / 10);
264  cstr[offset++] = '0' + static_cast<char>(display_second % 10);
265  cstr[offset++] = ']';
266  cstr[offset++] = '\0';
267  }
268 
269  if (ptr->show_iter_ && ptr->last_iter_ != iter) {
270  ptr->last_iter_ = iter;
271  const std::size_t dtotal = uint_digit(ptr->total_);
272  const std::size_t diter = uint_digit(iter);
273  const std::size_t num_space = dtotal > diter ? dtotal - diter : 0;
274 
275  char *cstr = ptr->cstr_iter_;
276  std::size_t offset = 0;
277  cstr[offset++] = '[';
278  for (std::size_t i = 0; i < num_space; ++i)
279  cstr[offset++] = ' ';
280  uint_to_char(iter, cstr, offset);
281  cstr[offset++] = '/';
282  uint_to_char(ptr->total_, cstr, offset);
283  cstr[offset++] = ']';
284  cstr[offset++] = '\0';
285  }
286 
287  ptr->os_ << ' ';
288  if (ptr->length_ != 0)
289  ptr->os_ << ptr->cstr_bar_;
290  ptr->os_ << ptr->cstr_percent_;
291  ptr->os_ << ptr->cstr_time_;
292  if (ptr->show_iter_)
293  ptr->os_ << ptr->cstr_iter_;
294  if (ptr->msg_.size() != 0)
295  ptr->os_ << '[' << ptr->msg_ << ']';
296  }
297 
298  template <typename UIntType>
299  static void uint_to_char(UIntType num, char *cstr, std::size_t &offset)
300  {
301  if (num == 0) {
302  cstr[offset++] = '0';
303  return;
304  }
305 
306  char utmp[32];
307  std::size_t unum = 0;
308  while (num) {
309  utmp[unum++] = '0' + static_cast<char>(num % 10);
310  num /= 10;
311  }
312  for (std::size_t i = unum; i != 0; --i)
313  cstr[offset++] = utmp[i - 1];
314  }
315 
316  template <typename UIntType>
317  static std::size_t uint_digit(UIntType num)
318  {
319  if (num == 0)
320  return 1;
321 
322  std::size_t digit = 0;
323  while (num != 0) {
324  ++digit;
325  num /= 10;
326  }
327 
328  return digit;
329  }
330 }; // class Progress
331 
332 } // namespace vsmc
333 
334 #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:49
void stop(bool finished=true)
Stop to print the progress.
Definition: progress.hpp:99
void increment(std::size_t step=1)
Increment the iteration count.
Definition: progress.hpp:113
void reset()
Stop and reset the elapsed time to zero.
Definition: stop_watch.hpp:126
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:79
bool stop()
Stop the watch, no effect if already stopped.
Definition: stop_watch.hpp:113
bool start()
Start the watch, no effect if already started.
Definition: stop_watch.hpp:97
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:158
void message(const std::string &msg)
Set a new message for display.
Definition: progress.hpp:116