Teuchos - Trilinos Tools Package  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Teuchos_StackedTimer.hpp
1 // @HEADER
2 // *****************************************************************************
3 // Teuchos: Common Tools Package
4 //
5 // Copyright 2004 NTESS and the Teuchos contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
10 #ifndef TEUCHOS_STACKED_TIMER_HPP
11 #define TEUCHOS_STACKED_TIMER_HPP
12 
13 #include "Teuchos_ConfigDefs.hpp"
14 #include "Teuchos_Comm.hpp"
15 #include "Teuchos_DefaultComm.hpp"
16 #include "Teuchos_CommHelpers.hpp"
17 #include "Teuchos_RCP.hpp"
18 #include "Teuchos_Array.hpp"
20 #include <string>
21 #include <vector>
22 #include <cassert>
23 #include <chrono>
24 #include <climits>
25 #include <cstdlib> // for std::getenv and atoi
26 #include <ctime> // for timestamp support
27 #include <iostream>
28 
29 #if defined(HAVE_TEUCHOS_KOKKOS_PROFILING) && defined(HAVE_TEUCHOSCORE_KOKKOS)
30 namespace Kokkos {
31 namespace Tools {
32 extern void pushRegion (const std::string&);
33 extern void popRegion ();
34 } // namespace Profiling
35 } // namespace Kokkos
36 #endif
37 
38 
39 namespace Teuchos {
40 
42 TEUCHOSCOMM_LIB_DLL_EXPORT void error_out(const std::string& msg, const bool fail_all = false);
43 
53 class BaseTimer {
54 
55 public:
56 
57  using Clock = std::chrono::high_resolution_clock;
58 
59  BaseTimer() : accumulation_(0.0), count_started_(0), count_updates_(0), running_(false) {}
60 
62  void start(){
63  if (running_)
64  error_out("Base_Timer:start Failed timer already running");
65  start_time_ = Clock::now();
66 
67  count_started_++;
68  running_ = true;
69  }
70 
72  void stop(){
73  if (!running_)
74  error_out("Base_Timer:stop Failed timer not running");
75  auto elapsed = std::chrono::duration_cast<std::chrono::duration<double>>(Clock::now() - start_time_).count();
76  accumulation_ += elapsed;
77  accumulationSquared_ += elapsed*elapsed;
78  running_ = false;
79  }
80 
82  unsigned long long incrementUpdates(unsigned long long count=1) {count_updates_ += count; return count_updates_;}
83 
85  double accumulatedTime() const {return accumulation_;}
86 
88  void setAccumulatedTime(double accum = 0) { accumulation_ = accum; }
89 
91  void setAccumulatedTimeSquared(double accumSq=0) {accumulationSquared_=accumSq;}
92  double accumulatedTimePerUpdate() const {
102  if (count_updates_ > 0) {
103  return accumulation_/count_updates_;
104  } else {
105  return 0;
106  }
107  }
108 
109 
118  if (count_started_> 0) {
119  return accumulation_/count_started_;
120  } else {
121  return 0;
122  }
123  }
124 
132  double timePerCallStdDev() const {
133  if (count_started_ > 0) {
134  double mean = accumulatedTimePerTimerCall();
135  return sqrt(std::max<double>(accumulationSquared_ / count_started_ - mean*mean, 0.0));
136  } else {
137  return 0;
138  }
139  }
140 
147  double difference(const BaseTimer &from) const {
148  return accumulation_ - from.accumulation_;
149  }
150 
152  void reset() {
153  if (running_)
154  error_out("BaseTimer, cannot reset a running timer");
155  accumulation_ = 0.0;
156  count_started_ = count_updates_ = 0;
157  }
158 
160  bool running() const { return running_;}
161 
163  unsigned long numCalls() const { return count_started_; }
164 
166  unsigned long long numUpdates() const { return count_updates_; }
167 
169  void overrideNumCallsForUnitTesting(const unsigned long num_calls)
170  { count_started_ = num_calls; }
171 
173  void overrideNumUpdatesForUnitTesting(const unsigned long long num_updates)
174  { count_updates_ = num_updates; }
175 
176  struct TimeInfo {
177  TimeInfo():time(0.0), stdDev(0.0), count(0), updates(0), running(false){}
178  TimeInfo(BaseTimer* t): time(t->accumulation_), stdDev(t->timePerCallStdDev()), count(t->count_started_), updates(t->count_updates_), running(t->running()) {}
179  double time;
180  double stdDev;
181  unsigned long count;
182  unsigned long long updates;
183  bool running;
184  };
185 
186 protected:
187  double accumulation_; // total time
188  double accumulationSquared_; // Sum of squares of elapsed times
189  unsigned long count_started_; // Number of times this timer has been started
190  unsigned long long count_updates_; // Total count of items updated during this timer
191  Clock::time_point start_time_;
192  bool running_;
193 
194  friend struct TimeInfo;
195 };
196 
214 class TEUCHOSCOMM_LIB_DLL_EXPORT StackedTimer
215 {
216 protected:
217 
226  class LevelTimer : public BaseTimer {
227  protected:
228 
229  // TODO: implement operator=
230 
231  unsigned level_;
232  std::string name_;
233  LevelTimer *parent_;
234  std::vector<LevelTimer> sub_timers_;
235  public:
237  LevelTimer();
238 
246  LevelTimer(int level,
247  const char* name = "RootTimer",
248  LevelTimer *parent=nullptr,
249  bool start_timer=true) :
250  BaseTimer(),
251  level_(level),
252  name_(name),
253  parent_(parent)
254  {
255  if ( start_timer )
257 
258  }
259 
261  LevelTimer(const LevelTimer &src) :
262  BaseTimer(src), level_(src.level_), name_(src.name_),parent_(src.parent_), sub_timers_(src.sub_timers_)
263  {
264  for (unsigned i=0;i<sub_timers_.size();++i)
265  sub_timers_[i].parent_ = this;
266  }
267 
273  LevelTimer* start(const char* sub_name) {
274  for (unsigned i=0;i<sub_timers_.size();i++ )
275  if (sub_name == sub_timers_[i].name_ ) {
276  sub_timers_[i].BaseTimer::start();
277  return &sub_timers_[i];
278  }
279  sub_timers_.push_back(LevelTimer(level_+1,sub_name,this,true));
280  return &sub_timers_[sub_timers_.size()-1];
281  }
282 
290  LevelTimer* stop(const std::string &name = "RootTimer") {
291  if (name != name_)
292  error_out("Stopping timer "+name+" But top level running timer is "+name_);
293  BaseTimer::stop();
294  return parent_;
295  }
296 
297 
302  std::string get_full_name() const {
303  std::string parent_name("");
304  if ((parent_ != nullptr))
305  parent_name = parent_->get_full_name() + "@";
306 
307  std::string my_name(name_);
308 
309  std::string full_name = parent_name + my_name;
310  return full_name;
311  }
312 
318  int countTimers() {
319  int count=1;
320  for (unsigned i=0;i<sub_timers_.size(); ++i)
321  count += sub_timers_[i].countTimers();
322  return count;
323  }
324 
325  void addTimerNames(Array<std::string> &names, unsigned &pos) {
326  names[pos++] = get_full_name();
327  for (unsigned i=0;i<sub_timers_.size(); ++i)
328  sub_timers_[i].addTimerNames(names, pos);
329  }
330 
336  double accumulatedTime(const std::string &locate_name="") {
337 
338  if (locate_name == "")
340 
341  std::string first_name,second_name;
342 
343  size_t i = locate_name.find_first_of('@');
344  if ( i >= locate_name.size() ) {
345  first_name = locate_name;
346  second_name = "";
347  } else {
348  first_name.assign(locate_name,0,i);
349  second_name.assign(locate_name,i+1,locate_name.size()-i-1);
350  }
351  for (unsigned j=0;j<sub_timers_.size();++j)
352  if ( first_name == sub_timers_[j].name_)
353  return sub_timers_[j].accumulatedTime(second_name);
354  return 0;
355  }
356 
358  unsigned level() const
359  {return level_;}
360 
361  protected:
368  void splitString(const std::string &locate_name, std::string &first_name, std::string &second_name) {
369  size_t i = locate_name.find_first_of('@');
370  if ( i >= locate_name.size() ) {
371  first_name = locate_name;
372  second_name = "";
373  } else {
374  first_name.assign(locate_name,0,i);
375  second_name.assign(locate_name,i+1,locate_name.size()-i-1);
376  }
377  }
378 
379  public:
385  double accumulatedTimePerUpdate(const std::string &locate_name="") {
386 
387  if (locate_name == "")
389 
390  std::string first_name,second_name;
391  splitString(locate_name, first_name, second_name);
392 
393  for (unsigned j=0;j<sub_timers_.size();j++)
394  if ( first_name == sub_timers_[j].name_)
395  return sub_timers_[j].accumulatedTimePerUpdate(second_name);
396  return 0;
397  }
398 
404  double accumulatedTimePerTimerCall(const std::string &locate_name="") {
405 
406  if (locate_name == "")
408 
409  std::string first_name,second_name;
410  splitString(locate_name, first_name, second_name);
411 
412  for (unsigned j=0;j<sub_timers_.size();j++)
413  if ( first_name == sub_timers_[j].name_)
414  return sub_timers_[j].accumulatedTimePerTimerCall(second_name);
415  return 0;
416  }
417 
421  void pack();
422 
428  LevelTimer* unpack(unsigned from);
429 
434  void report(std::ostream &os);
435 
441  const BaseTimer* findBaseTimer(const std::string &name) const;
442 
449  BaseTimer::TimeInfo findTimer(const std::string &name,bool& found);
450 
451  }; // LevelTimer
452 
453 
454 public:
460  explicit StackedTimer(const char *name, const bool start_base_timer = true)
461  : timer_(0,name,nullptr,false),
462  global_mpi_aggregation_called_(false),
463  enable_verbose_(false),
464  verbose_timestamp_levels_(0), // 0 disables
465  verbose_ostream_(Teuchos::rcpFromRef(std::cout)),
466  enable_timers_(true)
467  {
468  top_ = &timer_;
469  if (start_base_timer)
470  this->startBaseTimer();
471 
472  auto check_verbose = std::getenv("TEUCHOS_ENABLE_VERBOSE_TIMERS");
473  if (check_verbose != nullptr)
474  enable_verbose_ = true;
475 
476  auto check_timestamp = std::getenv("TEUCHOS_ENABLE_VERBOSE_TIMESTAMP_LEVELS");
477  if (check_timestamp != nullptr) {
478  verbose_timestamp_levels_ = std::atoi(check_timestamp);
479  }
480  }
481 
485  void startBaseTimer() {
486  timer_.BaseTimer::start();
487 #if defined(HAVE_TEUCHOS_KOKKOS_PROFILING) && defined(HAVE_TEUCHOSCORE_KOKKOS)
488  ::Kokkos::Tools::pushRegion(timer_.get_full_name());
489 #endif
490  }
491 
495  void stopBaseTimer() {
496  timer_.BaseTimer::stop();
497 #if defined(HAVE_TEUCHOS_KOKKOS_PROFILING) && defined(HAVE_TEUCHOSCORE_KOKKOS)
498  ::Kokkos::Tools::popRegion();
499 #endif
500  }
501 
507  void start(const std::string name,
508  const bool push_kokkos_profiling_region = true) {
509  if (enable_timers_) {
510  if (top_ == nullptr)
511  top_ = timer_.start(name.c_str());
512  else
513  top_ = top_->start(name.c_str());
514 #if defined(HAVE_TEUCHOS_KOKKOS_PROFILING) && defined(HAVE_TEUCHOSCORE_KOKKOS)
515  if (push_kokkos_profiling_region) {
516  ::Kokkos::Tools::pushRegion(name);
517  }
518 #endif
519  }
520  if (enable_verbose_) {
521  if (!verbose_timestamp_levels_) {
522  *verbose_ostream_ << "STARTING: " << name << std::endl;
523  }
524  // gcc 4.X is incomplete in c++11 standard - missing
525  // std::put_time. We'll disable this feature for gcc 4.
526 #if !defined(__GNUC__) || ( defined(__GNUC__) && (__GNUC__ > 4) )
527  else if (top_ != nullptr) {
528  if ( top_->level() <= verbose_timestamp_levels_) {
529  auto now = std::chrono::system_clock::now();
530  auto now_time = std::chrono::system_clock::to_time_t(now);
531  auto gmt = gmtime(&now_time);
532  auto timestamp = std::put_time(gmt, "%Y-%m-%d %H:%M:%S");
533  auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
534  *verbose_ostream_ << "STARTING: " << name << " LEVEL: " << top_->level() << " COUNT: " << timer_.numCalls() << " TIMESTAMP: " << timestamp << "." << ms.count() << std::endl;
535  }
536  }
537 #endif
538  }
539  }
540 
546  void stop(const std::string &name,
547  const bool pop_kokkos_profiling_region = true) {
548  if (enable_timers_) {
549  if (top_)
550  top_ = top_->stop(name);
551  else
552  timer_.BaseTimer::stop();
553 #if defined(HAVE_TEUCHOS_KOKKOS_PROFILING) && defined(HAVE_TEUCHOSCORE_KOKKOS)
554  if (pop_kokkos_profiling_region) {
555  ::Kokkos::Tools::popRegion();
556  }
557 #endif
558  }
559  if (enable_verbose_) {
560  if (!verbose_timestamp_levels_) {
561  *verbose_ostream_ << "STOPPING: " << name << std::endl;
562  }
563  // gcc 4.X is incomplete in c++11 standard - missing
564  // std::put_time. We'll disable this feature for gcc 4.
565 #if !defined(__GNUC__) || ( defined(__GNUC__) && (__GNUC__ > 4) )
566  // The stop adjusts the top level, need to adjust by +1 for printing
567  else if (top_ != nullptr) {
568  if ( (top_->level()+1) <= verbose_timestamp_levels_) {
569  auto now = std::chrono::system_clock::now();
570  auto now_time = std::chrono::system_clock::to_time_t(now);
571  auto gmt = gmtime(&now_time);
572  auto timestamp = std::put_time(gmt, "%Y-%m-%d %H:%M:%S");
573  auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
574  *verbose_ostream_ << "STOPPING: " << name << " LEVEL: " << top_->level()+1 << " COUNT: " << timer_.numCalls() << " TIMESTAMP: " << timestamp << "." << ms.count() << std::endl;
575  }
576  }
577 #endif
578  }
579  }
580 
585  void incrementUpdates(const long long i = 1) {
586  top_->incrementUpdates(i);
587  }
588 
594  double accumulatedTime(const std::string &name="") {
595  if (top_) // Top is null for the head node when nothing is running
596  return top_->accumulatedTime(name);
597  else
598  return timer_.accumulatedTime(name);
599  }
600 
606  double accumulatedTimePerUpdate(const std::string &name="") {
607  if (top_) // Top is null for the head node when nothing is running
608  return top_->accumulatedTimePerUpdate(name);
609  else
610  return timer_.accumulatedTimePerUpdate(name);
611  }
617  double accumulatedTimePerTimerCall(const std::string &name="") {
618  if (top_) // Top is null for the head node when nothing is running
619  return top_->accumulatedTimePerTimerCall(name);
620  else
621  return timer_.accumulatedTimePerTimerCall(name);
622  }
623 
629  const BaseTimer* findBaseTimer(const std::string &name) const {
630  const BaseTimer* baseTimer = timer_.findBaseTimer(name);
631  TEUCHOS_TEST_FOR_EXCEPTION(baseTimer == nullptr, std::runtime_error,
632  "StackedTimer::findBaseTimer() failed to find a timer named \"" << name << "\"!\n");
633  return baseTimer;
634  }
635 
641  BaseTimer::TimeInfo findTimer(const std::string &name) {
642  bool foundTimer = false;
643  const auto timeInfo = timer_.findTimer(name,foundTimer);
644  TEUCHOS_TEST_FOR_EXCEPTION(!foundTimer, std::runtime_error,
645  "StackedTimer::findTimer() failed to find a timer named \"" << name << "\"!\n");
646  return timeInfo;
647  }
648 
649  void report(std::ostream &os) {
650  timer_.report(os);
651  }
652 
667  struct OutputOptions {
668  OutputOptions() : output_fraction(false), output_total_updates(false), output_histogram(false),
669  output_minmax(false), output_proc_minmax(false), num_histogram(10), max_levels(INT_MAX),
670  print_warnings(true), align_columns(false), print_names_before_values(true),
671  drop_time(-1.0), output_per_proc_stddev(false) {}
672  bool output_fraction;
673  bool output_total_updates;
674  bool output_histogram;
675  bool output_minmax;
676  bool output_proc_minmax;
677  int num_histogram;
678  int max_levels;
679  bool print_warnings;
680  bool align_columns;
681  bool print_names_before_values;
682  double drop_time;
683  bool output_per_proc_stddev;
684  };
685 
692  void report(std::ostream &os, Teuchos::RCP<const Teuchos::Comm<int> > comm, OutputOptions options = OutputOptions());
693 
703  void reportXML(std::ostream &os, const std::string& datestamp, const std::string& timestamp, Teuchos::RCP<const Teuchos::Comm<int> > comm);
704 
736  std::string reportWatchrXML(const std::string& name, Teuchos::RCP<const Teuchos::Comm<int> > comm);
737 
739  void enableVerbose(const bool enable_verbose);
740 
742  void enableVerboseTimestamps(const unsigned levels);
743 
745  void setVerboseOstream(const Teuchos::RCP<std::ostream>& os);
746 
749  void disableTimers();
750 
753  void enableTimers();
754 
760  void aggregateMpiData(Teuchos::RCP<const Teuchos::Comm<int> > comm, OutputOptions options = OutputOptions());
761 
770  double getMpiAverageTime(const std::string& flat_timer_name);
771 
780  double getMpiAverageCount(const std::string& flat_timer_name);
781 
790  bool isTimer(const std::string& flat_timer_name);
791 
792 protected:
797 
798  // Global MPI aggregation arrays
799  Array<std::string> flat_names_;
800  Array<double> min_;
801  Array<double> max_;
802  Array<int> procmin_;
803  Array<int> procmax_;
804  Array<double> sum_;
805  Array<double> sum_sq_;
806  Array<Array<int>> hist_;
807  Array<double> per_proc_stddev_min_;
808  Array<double> per_proc_stddev_max_;
809  Array<unsigned long> count_;
810  Array<unsigned long long> updates_;
811  Array<int> active_;
812  bool global_mpi_aggregation_called_;
813 
816  std::string::size_type timer_names_;
817  std::string::size_type average_time_;
818  std::string::size_type fraction_;
819  std::string::size_type count_;
820  std::string::size_type total_updates_;
821  std::string::size_type min_;
822  std::string::size_type max_;
823  std::string::size_type procmin_;
824  std::string::size_type procmax_;
825  std::string::size_type stddev_;
826  std::string::size_type histogram_;
827  AlignmentWidths() :
828  timer_names_(0),
829  average_time_(0),
830  fraction_(0),
831  count_(0),
832  total_updates_(0),
833  min_(0),
834  max_(0),
835  procmax_(0),
836  stddev_(0),
837  histogram_(0){}
838  } alignments_;
839 
842 
845 
848 
851 
855  void flatten();
856 
861  void merge(Teuchos::RCP<const Teuchos::Comm<int> > comm);
862 
866  void collectRemoteData(Teuchos::RCP<const Teuchos::Comm<int> > comm, const OutputOptions &options );
867 
871  int getFlatNameIndex(const std::string& flat_timer_name);
872 
879  double computeColumnWidthsForAligment(std::string prefix, int print_level,
880  std::vector<bool> &printed, double parent_time,
881  const OutputOptions &options);
882 
886  double printLevel(std::string prefix, int level, std::ostream &os, std::vector<bool> &printed,
887  double parent_time, const OutputOptions &options);
888 
893  double printLevelXML(std::string prefix, int level, std::ostream &os, std::vector<bool> &printed, double parent_time, const std::string& rootName = "");
894 
895 }; //StackedTimer
896 
897 
898 } //namespace Teuchos
899 
900 #endif /* TEUCHOS_STACKED_TIMER_HPP */
void setAccumulatedTime(double accum=0)
Setter for accumulated time.
double accumulatedTimePerTimerCall(const std::string &name="")
double difference(const BaseTimer &from) const
Return the difference between two timers in seconds,.
StackedTimer(const char *name, const bool start_base_timer=true)
void overrideNumUpdatesForUnitTesting(const unsigned long long num_updates)
Sets the number of counts for this timer. This is only used for unit testing.
double accumulatedTime() const
Get the total accumulated time since last reset or construction when the timer is running...
void splitString(const std::string &locate_name, std::string &first_name, std::string &second_name)
split a string into two parts split by a &#39;@&#39; if no &#39;@&#39; first gets the full string ...
void stop(const std::string &name, const bool pop_kokkos_profiling_region=true)
LevelTimer(int level, const char *name="RootTimer", LevelTimer *parent=nullptr, bool start_timer=true)
unsigned long long incrementUpdates(unsigned long long count=1)
Increment the total number of items updated between a start stop.
bool running() const
Returns true if the timer is currently accumulating time.
double accumulatedTimePerTimerCall() const
return the average time per timer start/stop
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Macro for throwing an exception with breakpointing to ease debugging.
double accumulatedTimePerUpdate() const
return the average time per item updated
Teuchos header file which uses auto-configuration information to include necessary C++ headers...
bool enable_verbose_
If set to true, prints to the debug ostream. At construction, default value is set from environment v...
void incrementUpdates(const long long i=1)
void start()
Start a currently stopped timer.
LevelTimer * top_
Current level running.
bool enable_timers_
Used to disable timers for asynchronous work.
double accumulatedTimePerUpdate(const std::string &name="")
unsigned verbose_timestamp_levels_
If set to a value greater than 0, verbose mode will print that many levels of timers with timestamps...
unsigned long numCalls() const
Returns the number of calls to start().
double accumulatedTime(const std::string &name="")
void start(const std::string name, const bool push_kokkos_profiling_region=true)
double accumulatedTime(const std::string &locate_name="")
void overrideNumCallsForUnitTesting(const unsigned long num_calls)
Sets the number of calls to start() for this timer. This is only used for unit testing.
void error_out(const std::string &msg, const bool)
Error reporting function for stacked timer.
The basic timer used internally, uses std::chrono::high_resolution_clock.
Stores the column widths for output alignment.
LevelTimer(const LevelTimer &src)
Copy constructor.
Timer info at a given level and all the children.
const BaseTimer * findBaseTimer(const std::string &name) const
double accumulatedTimePerUpdate(const std::string &locate_name="")
Teuchos::RCP< std::ostream > verbose_ostream_
For debugging, this is the ostream used for printing.
LevelTimer timer_
Base timer.
double accumulatedTimePerTimerCall(const std::string &locate_name="")
Templated array class derived from the STL std::vector.
double timePerCallStdDev() const
return the std dev in time per timer start/stop
void setAccumulatedTimeSquared(double accumSq=0)
Setter for squared accumulated time.
LevelTimer * stop(const std::string &name="RootTimer")
void reset()
Reset all the timer stats, throws if it is already running.
void stop()
Stop a current running timer and accumulate time difference.
unsigned level() const
Returns the level of the timer in the stack.
Smart reference counting pointer class for automatic garbage collection.
BaseTimer::TimeInfo findTimer(const std::string &name)
This class allows one to push and pop timers on and off a stack.
Common capabilities for collecting and reporting performance data collectively across MPI processes...
unsigned long long numUpdates() const
Returns the number of updates added to this timer.
Reference-counted pointer class and non-member templated function implementations.
LevelTimer * start(const char *sub_name)