Turi Create  4.0
table_printer.hpp
1 /* Copyright © 2017 Apple Inc. All rights reserved.
2  *
3  * Use of this source code is governed by a BSD-3-clause license that can
4  * be found in the LICENSE.txt file or at https://opensource.org/licenses/BSD-3-Clause
5  */
6 #ifndef TURI_TABLE_PRINTER_H_
7 #define TURI_TABLE_PRINTER_H_
8 
10 #include <core/logging/assertions.hpp>
11 #include <timer/timer.hpp>
12 #include <core/parallel/pthread_tools.hpp>
13 #include <core/storage/sframe_data/sframe.hpp>
14 #include <core/parallel/atomic.hpp>
15 #include <sstream>
16 #include <vector>
17 
18 #include <core/util/code_optimization.hpp>
19 #include <core/logging/table_printer/table_element_printers.hpp>
20 
21 namespace turi {
22 
23 extern double MIN_SECONDS_BETWEEN_TICK_PRINTS;
24 
25 /** A format specifying class telling the table printer to print the
26  * progress time. See table_printer documentation for use.
27  */
28 struct progress_time {
29  explicit progress_time(const timer& tt) : progress_time(tt.current_time()) {}
30  explicit progress_time(double seconds) : elapsed_seconds(seconds) {}
31 
32  // For printing since the table start
33  progress_time() : elapsed_seconds(-1) {}
34 
35  double elapsed_seconds;
36 };
37 
38 /** A simple table printer for consistent information.
39  *
40  * The constructor takes a list of (column name, width) strings. If
41  * any column name is longer than the specified width, then the
42  * column is sized to just fit the header.
43  *
44  * The header is printed with print_header().
45  *
46  * Each row is shown with print_row(...), where ... contains the
47  * arguments of each row. If this doesn't
48  *
49  * numeric: double precision is printed so that it fits in the proper
50  * row width. It is recommended that columns of floats be at least
51  * of width 8. integers are printed without any decimal point.
52  *
53  *
54  * strings: strings are printed as is. If they are longer than the
55  * specified line, they are truncated with a "..." printed after
56  * them.
57  *
58  *
59  * progress_time instance: This tells the printer to print the
60  * elapsed time. If progress_time() is passed in without any
61  * arguments, then it prints the time elapsed since the class was
62  * constructed. If progress_time() is constructed with a timer
63  * object or the elapsed number of seconds, then that is printed.
64  * See the example below for more information.
65  *
66  * The footer is printed with print_footer().
67  *
68  * Example:
69  *
70  *
71  * progress_table_printer table( { {"Iteration", 0}, {"Time", 10}, {"RMSE", 8}, {"Top String", 16} } );
72  *
73  * table.print_header();
74  *
75  * table.print_row(0, progress_time(), 1e6, "Alphabetical.");
76  * table.print_row(1, progress_time(), 10, "Alphabet soup.");
77  * table.print_row(2, progress_time(0.1), 1, "Mine!!!!");
78  * table.print_row(4, progress_time(100), 0.1, "Now it's a really long string.");
79  * table.print_row(5, progress_time(1000), 0.01, "Yours!!!!");
80  * table.print_row(6, progress_time(1000.0001), 0.001, "");
81  * table.print_row(7, progress_time(5e5), 1e-6, "Turi");
82  *
83  * table.print_row("FINAL", progress_time(5e6), 1e-6, "Turi");
84  *
85  * table.print_footer();
86  *
87  * This prints out the table:
88  *
89  * +-----------+------------+----------+------------------+
90  * | Iteration | Time | RMSE | Top String |
91  * +-----------+------------+----------+------------------+
92  * | 0 | 15us | 1000000 | Alphabetical. |
93  * | 1 | 93us | 10 | Alphabet soup. |
94  * | 2 | 100ms | 1 | Mine!!!! |
95  * | 4 | 1m 40s | 0.1 | Now it's a rea...|
96  * | 5 | 16m 40s | 0.01 | Yours!!!! |
97  * | 6 | 16m 40s | 0.001 | |
98  * | 7 | 5d 18h 53m | 1e-06 | Turi |
99  * | FINAL | 57d 20h | 1e-06 | Turi |
100  * +-----------+------------+----------+------------------+
101  *
102  *
103  * --------------------------------------------------------------------------------
104  * TIMED PROGRESS PRINTING
105  * --------------------------------------------------------------------------------
106  *
107  *
108  * Using the print_progress_row(...) method instead of print_row(...),
109  * you can control how often progress messages are printed. It
110  * automatically adjusts the printing interval so that the rows are
111  * printed, on average, every 1-5 seconds. The first argument to
112  * print_progress_row is the tick variable, which must simply be a
113  * monotonically increasing integer.
114  *
115  *
116  * Example 1:
117  *
118  * Code:
119  *
120  * table_printer table( { {"Iteration", 0}, {"Elapsed Time", 10}, {"RMSE", 8} } );
121  *
122  * table.print_header();
123  *
124  * for(size_t i = 0; i < 2000; ++i) {
125  * table.print_progress_row(i, i, progress_time(), std::exp(-double(i) / 5000));
126  * usleep(8000); // Sleep for 8 milliseconds
127  * }
128  *
129  * table.print_row("FINAL", progress_time(), 1e-6);
130  *
131  * table.print_footer();
132  *
133  * Output:
134  *
135  * +-----------+--------------+----------+
136  * | Iteration | Elapsed Time | RMSE |
137  * +-----------+--------------+----------+
138  * | 0 | 30us | 1 |
139  * | 500 | 4.05s | 0.904837 |
140  * | 1000 | 8.10s | 0.818731 |
141  * | 1500 | 12.15s | 0.740818 |
142  * | FINAL | 16.21s | 1e-06 |
143  * +-----------+--------------+----------+
144  *
145  * Example 2:
146  *
147  * Code:
148  *
149  * random::seed(0);
150  *
151  * table_printer table( { {"samples_processed", 0}, {"Elapsed Time", 10}, {"A value", 8} } );
152  *
153  * table.print_header();
154  *
155  * size_t proc = 0;
156  *
157  * for(size_t i = 0; i < 50000; ++i) {
158  * table.print_progress_row(proc, proc, progress_time(), i);
159  * proc += random::fast_uniform<size_t>(0, 100);
160  * usleep(100); // sleep for 200 microseconds
161  * }
162  *
163  * table.print_row("FINAL", progress_time(), 1e-6);
164  *
165  * table.print_footer();
166  *
167  * Output:
168  *
169  * +-------------------+--------------+----------+
170  * | samples_processed | Elapsed Time | A value |
171  * +-------------------+--------------+----------+
172  * | 0 | 71us | 0 |
173  * | 500081 | 1.61s | 10009 |
174  * | 1000094 | 3.16s | 19906 |
175  * | 1500049 | 4.78s | 29980 |
176  * | 2000052 | 6.40s | 39882 |
177  * | 2500054 | 8.03s | 49971 |
178  * | FINAL | 8.04s | 1e-06 |
179  * +-------------------+--------------+----------+
180  *
181  *
182  * --------------------------------------------------------------------------------
183  * TRACKING
184  * --------------------------------------------------------------------------------
185  *
186  * Optionally, the table printer can track the calls to the progress
187  * row functions by storing each call as a row in an SFrame that can
188  * be retrieved at the end. This is useful for recording the training
189  * statistics of an algorithm for reporting at the end with
190  * get_tracked_table().
191  *
192  * Not every call to the progress functions are printed; the on-screen
193  * printing is designed for a pleasing visual report. Tracking,
194  * however, is determined by an interval specified in the constructor
195  * -- a row is recorded in the sframe every track_interval calls to
196  * one of the progress printing calls. It may be turned off by
197  * setting track_interval to be 0.
198  *
199  */
201 
202  public:
203  /** Constructor. Must be initialized elsewise using copy assignment ops. */
205 
206  /** Constructor. Sets up the columns.
207  *
208  * \param _format A vector of (column name, width) pairs. If the
209  * length of column name is larger than width, than width is set to
210  * the column name. See class header for examples.
211  *
212  * The track_interval determines how often a result is stored in the
213  * SFrame tracking the row progress. Every track_interval calls to
214  * get_tracked_table, the row is written to the sframe. If
215  * track_interval is 0, then tracking is turned off.
216  */
217  table_printer(const std::vector<std::pair<std::string, size_t> >& _format,
218  size_t track_interval = 1);
219 
220 
221  /** Need to clean up some things.
222  */
223  ~table_printer();
224 
225  /** Sets the output stream to something custom. Only a reference to
226  * this stream is stored, so the stream must be in existance for as
227  * long as this class is.
228  */
229  void set_output_stream(std::ostream& out_stream) {
230  alt_output_stream = &out_stream;
231  }
232 
233 
234  /** Prints the header.
235  *
236  * Example output:
237  *
238  * +-----------+------------+----------+------------------+
239  * | Iteration | Time | RMSE | Top String |
240  * +-----------+------------+----------+------------------+
241  */
242  void print_header() const;
243 
244  /** Prints a line break.
245  *
246  * Example output:
247  *
248  * +-----------+------------+----------+------------------+
249  */
250  void print_line_break() const;
251 
252  /** Prints the footer.
253  *
254  * Example output:
255  *
256  * +-----------+------------+----------+------------------+
257  */
258  void print_footer() const;
259 
260  /** Print a row.
261  *
262  * Example output:
263  *
264  * table.print_row(5, progress_time(1000), 0.01, "Yours!!!!");
265  *
266  * Gives
267  *
268  * | 5 | 16m 40s | 0.01 | Yours!!!! |
269  */
270  template <typename... Args>
271  void print_row(const Args&... columns) const {
272  ASSERT_EQ(size_t(sizeof...(Args)), format.size());
273 
274  std::ostringstream ss;
275  size_t column_index = 0;
276 
277  ss << '|';
278 
279  _add_values_in_row(ss, column_index, columns...);
280 
281  _p(ss);
282  }
283 
284  /** Same as print row but take a vector of one particular type. May
285  * be flexible type. **/
286  template <typename T>
287  void print_row(const std::vector<T>& row_string) const {
288  ASSERT_EQ(row_string.size(), format.size());
289 
290  std::ostringstream ss;
291 
292  ss << '|';
293 
294  for (size_t i = 0; i < row_string.size(); ++i) {
295  os_log_value(i, row_string[i]);
296  _get_table_printer(row_string[i]).print(ss, format[i].second);
297  }
298 
299  _p(ss);
300  }
301 
302  // If it's time to print the next row. This can avoid doing
303  // expensive things in an inner loop.
305  bool time_for_next_row() const {
306  double time_ms = lowres_tt.ms();
307  return (time_ms >= next_timed_print);
308  }
309 
310  /** Print a row associated with the progress of an algorithm, but
311  * print at most once a second.
312  */
313  template <typename... Args>
314  inline GL_HOT_INLINE void print_timed_progress_row(const Args&... columns) {
315 
316  double time_ms = lowres_tt.ms();
317 
318  if(time_ms >= next_timed_print) {
319  std::lock_guard<mutex> pl_guard(print_lock);
320 
321  if(time_ms < next_timed_print)
322  return;
323 
324  if(next_timed_print == -1) {
325  next_timed_print = time_ms + 1000.0 * MIN_SECONDS_BETWEEN_TICK_PRINTS;
326  } else {
327 
328  next_timed_print += 1000.0 * MIN_SECONDS_BETWEEN_TICK_PRINTS;
329 
330  // If that wasn't good enough
331  if(next_timed_print < time_ms)
332  next_timed_print = time_ms + 1000.0 * MIN_SECONDS_BETWEEN_TICK_PRINTS;
333  }
334 
335  _print_progress_row(columns...);
336 
337  // Turn off the tracking if people don't want it, i.e. by
338  // passing 0 as the track interval to the constructor.
339  if(track_interval != 0)
340  _track_progress(/* was_printed */ true, columns...);
341  }
342  }
343 
344  /** Print a row associated with the progress of an algorithm.
345  *
346  * The first argument is the tick, which can be something like
347  * samples processed or iterations. The time at which to update
348  * this is automatically determined based on the first 2 (or more,
349  * if calls are extremely frequent) calls.
350  *
351  * Example output:
352  *
353  * table.print_row(5, 5, progress_time(1000), 0.01, "Yours!!!!");
354  *
355  * Gives
356  *
357  * | 5 | 16m 40s | 0.01 | Yours!!!! |
358  */
359  template <typename... Args>
360  inline GL_HOT_INLINE void print_progress_row(size_t tick, const Args&... columns) {
361 
362  size_t ticks_so_far = ++num_ticks_so_far;
363  bool was_printed = false;
364 
365  if(register_tick(tick, ticks_so_far)) {
366  std::unique_lock<mutex> pl_guard(print_lock, std::defer_lock);
367  if(pl_guard.try_lock()) {
368  _print_progress_row(columns...);
369  was_printed = true;
370  }
371  }
372 
373  if((track_interval != 0) && ((ticks_so_far - 1) % track_interval) == 0) {
374  _track_progress(was_printed, columns...);
375  }
376  }
377 
378  /** Print a row associated with the progress of an algorithm.
379  *
380  * The first argument is the tick, which can be something like
381  * samples processed or iterations. The time at which to update
382  * this is automatically determined based on the first 2 (or more,
383  * if calls are extremely frequent) calls.
384  *
385  */
386  inline GL_HOT_INLINE void print_progress_row_strs(size_t tick, const std::vector<std::string>& cols) {
387  ASSERT_EQ(cols.size(), format.size());
388 
389  size_t ticks_so_far = ++num_ticks_so_far;
390  bool was_printed = false;
391 
392  if(register_tick(tick, ticks_so_far)) {
393  std::lock_guard<mutex> pl_guard(print_lock);
394  print_row(cols);
395  was_printed = true;
396  }
397 
398  if (track_interval != 0 && ((ticks_so_far - 1) % track_interval) == 0) {
399  std::lock_guard<mutex> guard(track_register_lock);
400 
401  if (!tracker_is_initialized) {
402  track_row_values_.resize(cols.size());
403  track_row_styles_.resize(cols.size());
404  }
405 
406  for (size_t i = 0; i < cols.size(); ++i) {
407  track_row_values_[i] = cols[i]; // Convert from string to flexible_type
408  track_row_styles_[i] = style_type::kDefault;
409  }
410 
411  track_progress_row(track_row_values_);
412  track_row_was_printed_ = was_printed;
413  }
414  }
415 
416 
417 
418  /** Returns the elapsed time since class creation. This is the
419  * value used if progress_time() is passed in to print_row.
420  */
421  double elapsed_time() const;
422 
423 
424  /** Returns the current tracked table. Any rows added after this is
425  * called will cause the table to be cleared and all rows to be
426  * added to another table.
427  *
428  */
429  sframe get_tracked_table();
430 
431 private:
432 
433  /**
434  * Methods to log specific value types
435  */
436  static void os_log_value(size_t column_index, unsigned long long value);
437  static void os_log_value(size_t column_index, unsigned long value);
438  static void os_log_value(size_t column_index, unsigned int value);
439  static void os_log_value(size_t column_index, long long value);
440  static void os_log_value(size_t column_index, long value);
441  static void os_log_value(size_t column_index, int value);
442  static void os_log_value(size_t column_index, double value);
443  static void os_log_value(size_t column_index, float value);
444  void os_log_value(size_t column_index, const progress_time& value) const;
445  static void os_log_value(size_t column_index, const char* value);
446  static void os_log_value(size_t column_index, bool value);
447  static void os_log_value(size_t column_index, const flexible_type& value);
448 
449 
450  /** Returns the table_printer::style_type for a given value
451  */
452  template <typename T>
453  table_internal::table_printer_element_base::style_type
454  _get_table_printer_style(const T& v) {
455  static_assert(table_internal::table_printer_element<T>::valid_type,
456  "Table printer not available for this type; please cast to approprate type.");
457 
458  return table_internal::table_printer_element<T>::style;
459  }
460 
461  /** By value, disambiguate the elements
462  */
463  template <typename T>
464  table_internal::table_printer_element<T> _get_table_printer(const T& v) const {
465  static_assert(table_internal::table_printer_element<T>::valid_type,
466  "Table printer not available for this type; please cast to approprate type.");
467 
468  return table_internal::table_printer_element<T>(v);
469  }
470 
471  /** Special case this one since it has to have access to the current
472  * local time, stored in this class.
473  */
474  table_internal::table_printer_element<progress_time> _get_table_printer(const progress_time& pt) const {
475 
476  double t = (pt.elapsed_seconds < 0) ? tt.current_time() : pt.elapsed_seconds;
477  return table_internal::table_printer_element<progress_time>(t);
478  }
479 
480  /** Recursively add values in a row.
481  */
482  template <typename T, typename... Args>
483  GL_HOT_INLINE_FLATTEN void _add_values_in_row(std::ostringstream& ss, size_t column_index,
484  const T& t, const Args&... columns) const {
485 
486  os_log_value(column_index, t);
487  _get_table_printer(t).print(ss, format[column_index].second);
488  _add_values_in_row(ss, column_index + 1, columns...);
489  }
490 
491  /** Recursively add values in a row -- final termination state.
492  */
493  template <typename T, typename... Args>
494  GL_HOT_INLINE_FLATTEN void _add_values_in_row(std::ostringstream& ss, size_t column_index, const T& t) const {
495  os_log_value(column_index, t);
496  _get_table_printer(t).print(ss, format[column_index].second);
497  }
498 
499  ////////////////////////////////////////////////////////////////////////////////
500  //
501 
502  template <typename... Args>
503  GL_HOT_NOINLINE void _print_progress_row(const Args&... columns) {
504 
505  print_row(columns...);
506  };
507 
508  private:
509 
510  /** Do the printing (in one place, since it's line origin is printed
511  * in debug mode.
512  */
513  void _p(std::ostringstream& ss) const {
514 
515  if(alt_output_stream == nullptr) {
516  logprogress_stream << ss.str() << std::endl;
517  } else {
518  (*alt_output_stream) << ss.str() << std::endl;
519  }
520  }
521 
522  private:
523  std::vector<std::pair<std::string, size_t> > format;
524 
525  timer tt;
526  rdtsc_time lowres_tt;
527 
528  ////////////////////////////////////////
529  // Controlling the output printing
530 
531  std::ostream* alt_output_stream = nullptr;
532 
533  //////////////////////////////////////////
534  // Controlling interval printing of things.
535 
536  atomic<double> time_of_first_tick;
537  atomic<size_t> value_of_first_tick;
538 
539  atomic<size_t> num_ticks_so_far;
540  atomic<size_t> next_tick_to_print;
541  size_t tick_interval = 0;
542 
543  mutex print_lock;
544  mutex tick_interval_lock;
545 
546 
547  double next_timed_print = -1;
548 
549 
550  /** The ticks_so_far thing is stored in the
551  *
552  */
553  inline bool register_tick(size_t tick, size_t ticks_so_far) {
554 
555  // RULES:
556  //
557  // 1. Always print the first 5 ticks seen.
558  //
559  // 2. On the fifth row printed, choose a schedule based on how
560  // long those took. After that, some intervals will be always
561  // printed, but then the next_tick_to_print option will
562  // determine which ones to print
563 
564  if(ticks_so_far == 1) {
565  value_of_first_tick = tick;
566  time_of_first_tick = tt.current_time();
567 
568  return true;
569 
570  } else if(ticks_so_far < 5) {
571  return true;
572 
573  } else if (ticks_so_far == 5) {
574 
575  // Make sure the ticks_so_far == 1 case has written this correctly.
576  while(time_of_first_tick == -1.0);
577 
578  tick_interval = set_up_time_printing_interval(tick);
579 
580  // Set this to the next multiple of tick_interval after tick.
581  size_t nttp = ( (tick + 1) + tick_interval);
582  size_t rounded_nttp = nttp - nttp % tick_interval;
583  if(rounded_nttp <= tick)
584  rounded_nttp += tick_interval;
585 
586  // This unlocks any other threads hung in the while loop in the >5 case.
587  next_tick_to_print = rounded_nttp;
588 
589  // Print this row.
590  return true;
591 
592  // Now the typical case
593  } else if(ticks_so_far > 5) {
594 
595  // Wait for the ticks_so_far == 5 thread to finish setting everything.
596  if(UNLIKELY(next_tick_to_print == 0)) {
597  while(next_tick_to_print == 0);
598  }
599 
600  size_t next_tick = next_tick_to_print;
601 
602  if(tick < next_tick) {
603  return always_print(tick);
604 
605  } else {
606 
607  DASSERT_GT(tick_interval, 0);
608 
609  std::unique_lock<mutex> til_guard(tick_interval_lock, std::defer_lock);
610 
611  if (til_guard.try_lock()) {
612  if(tick < next_tick_to_print) {
613  return always_print(tick);
614  } else {
615 
616  while(next_tick_to_print <= tick)
617  next_tick_to_print += tick_interval;
618 
619  return true;
620  }
621  } else {
622  return false;
623  }
624  }
625 
626  }
627 
628  return true;
629  }
630 
631  /** Sets up the time interval at which things are printed.
632  */
633  size_t set_up_time_printing_interval(size_t tick);
634 
635 
636  /** Returns true if the given tick should always be printed. This
637  * prevents the common case of models going too quickly to actually print results
638  *
639  */
640  inline GL_HOT_INLINE_FLATTEN bool always_print(size_t tick_index) {
641 
642  // Always print 1,2,3,4,5
643  if(tick_index <= 5)
644  return true;
645 
646  // Always print 10, 50, 100, 500, 1000, 5000, etc.
647 
648  while( (tick_index % 10) == 0)
649  tick_index /= 10;
650 
651  return (tick_index == 1 || tick_index == 5);
652  }
653 
654  ////////////////////////////////////////////////////////////////////////////////
655  // Stuff for registering the values
656 
657  using style_type = table_internal::table_printer_element_base::style_type;
658 
659  mutable mutex track_register_lock;
660  sframe track_sframe;
661  bool tracker_is_initialized = false;
662  bool track_row_was_printed_ = false;
663  sframe::iterator tracking_out_iter;
664  std::vector<flexible_type> track_row_values_;
665  std::vector<style_type> track_row_styles_;
666  size_t track_interval = 1;
667 
668 
669  /** Record a row in the tracking SFrame.
670  */
671  template <typename... Args>
672  inline GL_HOT_NOINLINE void _track_progress(
673  bool was_printed, const Args&... columns) {
674 
675  std::lock_guard<decltype(track_register_lock)> register_lock_gourd(track_register_lock);
676 
677  const size_t n = sizeof...(columns);
678 
679  DASSERT_EQ(n, format.size());
680 
681  if(!tracker_is_initialized) {
682  track_row_values_.resize(n);
683  track_row_styles_.resize(n);
684  }
685 
686  _register_values_in_row(0, columns...);
687  track_progress_row(track_row_values_);
688  track_row_was_printed_ = was_printed;
689  }
690 
691  void print_track_row_if_necessary() const;
692 
693  inline GL_HOT_NOINLINE void
694  track_progress_row(const std::vector<flexible_type>& track_row_buffer) {
695 
696  size_t n = track_row_buffer.size();
697  if(!tracker_is_initialized) {
698  track_sframe = sframe();
699 
700  // Get the names
701  std::vector<std::string> column_names(n);
702  std::vector<flex_type_enum> column_types(n);
703 
704  for(size_t i = 0; i < n; ++i) {
705  column_names[i] = format[i].first;
706  column_types[i] = track_row_buffer[i].get_type();
707  }
708 
709  track_sframe.open_for_write(column_names, column_types, "", 1);
710  tracking_out_iter = track_sframe.get_output_iterator(0);
711  tracker_is_initialized = true;
712  }
713 
714  *tracking_out_iter = track_row_buffer;
715  ++tracking_out_iter;
716  }
717 
718  /** Recursively add values in a row.
719  */
720  template <typename T, typename... Args>
721  GL_HOT_INLINE_FLATTEN void _register_values_in_row(
722  size_t column_index, const T& t, const Args&... columns) {
723  DASSERT_LT(column_index, track_row_values_.size());
724  DASSERT_LT(column_index, track_row_styles_.size());
725 
726  track_row_values_[column_index] = _get_table_printer(t).get_value();
727  track_row_styles_[column_index] = _get_table_printer_style(t);
728  _register_values_in_row(column_index + 1, columns...);
729  }
730 
731  /** Recursively add values in a row -- final termination state.
732  */
733  template <typename T, typename... Args>
734  GL_HOT_INLINE_FLATTEN void _register_values_in_row(
735  size_t column_index, const T& t) {
736  DASSERT_LT(column_index, track_row_values_.size());
737  DASSERT_LT(column_index, track_row_styles_.size());
738 
739  track_row_values_[column_index] = _get_table_printer(t).get_value();
740  track_row_styles_[column_index] = _get_table_printer_style(t);
741  }
742 
743 };
744 
745 }
746 
747 #endif /* TURI_TABLE_PRINTER_H_ */
iterator get_output_iterator(size_t segmentid)
GL_HOT_INLINE void print_timed_progress_row(const Args &... columns)
double current_time() const
Returns the elapsed time in seconds since turi::timer::start was last called.
Definition: timer.hpp:83
#define logprogress_stream
Definition: logger.hpp:325
GL_HOT_INLINE void print_progress_row_strs(size_t tick, const std::vector< std::string > &cols)
#define GL_HOT_INLINE
#define GL_HOT_INLINE_FLATTEN
void open_for_write(const std::vector< std::string > &column_names, const std::vector< flex_type_enum > &column_types, const std::string &frame_sidx_file="", size_t nsegments=SFRAME_DEFAULT_NUM_SEGMENTS, bool fail_on_column_names=true)
Definition: sframe.hpp:265
GL_HOT_INLINE void print_progress_row(size_t tick, const Args &... columns)
void print_row(const std::vector< T > &row_string) const
void print_row(const Args &... columns) const
A simple class that can be used for benchmarking/timing up to microsecond resolution.
Definition: timer.hpp:59
void set_output_stream(std::ostream &out_stream)