Teuchos Package Browser (Single Doxygen Collection)  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Teuchos_StackedTimer.hpp
Go to the documentation of this file.
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 "Teuchos_Behavior.hpp"
21 #ifdef HAVE_TEUCHOSCORE_KOKKOS
22 #include "Kokkos_Core.hpp"
23 #endif
24 #include <string>
25 #include <vector>
26 #include <cassert>
27 #include <chrono>
28 #include <climits>
29 #include <cstdlib> // for std::getenv and atoi
30 #include <ctime> // for timestamp support
31 #include <iostream>
32 
33 #if defined(HAVE_TEUCHOS_KOKKOS_PROFILING) && defined(HAVE_TEUCHOSCORE_KOKKOS)
34 namespace Kokkos {
35 namespace Tools {
36 extern void pushRegion (const std::string&);
37 extern void popRegion ();
38 } // namespace Profiling
39 } // namespace Kokkos
40 #endif
41 
42 
43 namespace Teuchos {
44 
46 TEUCHOSCOMM_LIB_DLL_EXPORT void error_out(const std::string& msg, const bool fail_all = false);
47 
57 class BaseTimer {
58 
59 public:
60 
61  using Clock = std::chrono::high_resolution_clock;
62 
64 
66  void start(){
67  if (running_)
68  error_out("Base_Timer:start Failed timer already running");
69  start_time_ = Clock::now();
70 
72  running_ = true;
73  }
74 
76  void stop(){
77  if (!running_)
78  error_out("Base_Timer:stop Failed timer not running");
79  auto elapsed = std::chrono::duration_cast<std::chrono::duration<double>>(Clock::now() - start_time_).count();
80  accumulation_ += elapsed;
81  accumulationSquared_ += elapsed*elapsed;
82  running_ = false;
83  }
84 
86  unsigned long long incrementUpdates(unsigned long long count=1) {count_updates_ += count; return count_updates_;}
87 
89  double accumulatedTime() const {return accumulation_;}
90 
92  void setAccumulatedTime(double accum = 0) { accumulation_ = accum; }
93 
95  void setAccumulatedTimeSquared(double accumSq=0) {accumulationSquared_=accumSq;}
96  double accumulatedTimePerUpdate() const {
106  if (count_updates_ > 0) {
108  } else {
109  return 0;
110  }
111  }
112 
113 
122  if (count_started_> 0) {
124  } else {
125  return 0;
126  }
127  }
128 
136  double timePerCallStdDev() const {
137  if (count_started_ > 0) {
138  double mean = accumulatedTimePerTimerCall();
139  return sqrt(std::max<double>(accumulationSquared_ / count_started_ - mean*mean, 0.0));
140  } else {
141  return 0;
142  }
143  }
144 
151  double difference(const BaseTimer &from) const {
152  return accumulation_ - from.accumulation_;
153  }
154 
156  void reset() {
157  if (running_)
158  error_out("BaseTimer, cannot reset a running timer");
159  accumulation_ = 0.0;
161  }
162 
164  bool running() const { return running_;}
165 
167  unsigned long numCalls() const { return count_started_; }
168 
170  unsigned long long numUpdates() const { return count_updates_; }
171 
173  void overrideNumCallsForUnitTesting(const unsigned long num_calls)
174  { count_started_ = num_calls; }
175 
177  void overrideNumUpdatesForUnitTesting(const unsigned long long num_updates)
178  { count_updates_ = num_updates; }
179 
180  struct TimeInfo {
181  TimeInfo():time(0.0), stdDev(0.0), count(0), updates(0), running(false){}
183  double time;
184  double stdDev;
185  unsigned long count;
186  unsigned long long updates;
187  bool running;
188  };
189 
190 protected:
191  double accumulation_; // total time
192  double accumulationSquared_; // Sum of squares of elapsed times
193  unsigned long count_started_; // Number of times this timer has been started
194  unsigned long long count_updates_; // Total count of items updated during this timer
195  Clock::time_point start_time_;
196  bool running_;
197 
198  friend struct TimeInfo;
199 };
200 
219 {
220 protected:
221 
230  class LevelTimer : public BaseTimer {
231  protected:
232 
233  // TODO: implement operator=
234 
235  unsigned level_;
236  std::string name_;
238  std::vector<LevelTimer> sub_timers_;
239  public:
241  LevelTimer();
242 
250  LevelTimer(int level,
251  const char* name = "RootTimer",
252  LevelTimer *parent=nullptr,
253  bool start_timer=true) :
254  BaseTimer(),
255  level_(level),
256  name_(name),
257  parent_(parent)
258  {
259  if ( start_timer )
261 
262  }
263 
265  LevelTimer(const LevelTimer &src) :
266  BaseTimer(src), level_(src.level_), name_(src.name_),parent_(src.parent_), sub_timers_(src.sub_timers_)
267  {
268  for (unsigned i=0;i<sub_timers_.size();++i)
269  sub_timers_[i].parent_ = this;
270  }
271 
277  LevelTimer* start(const char* sub_name) {
278  for (unsigned i=0;i<sub_timers_.size();i++ )
279  if (sub_name == sub_timers_[i].name_ ) {
280  sub_timers_[i].BaseTimer::start();
281  return &sub_timers_[i];
282  }
283  sub_timers_.push_back(LevelTimer(level_+1,sub_name,this,true));
284  return &sub_timers_[sub_timers_.size()-1];
285  }
286 
294  LevelTimer* stop(const std::string &name = "RootTimer") {
295  if (name != name_)
296  error_out("Stopping timer "+name+" But top level running timer is "+name_);
297  BaseTimer::stop();
298  return parent_;
299  }
300 
301 
306  std::string get_full_name() const {
307  std::string parent_name("");
308  if ((parent_ != nullptr))
309  parent_name = parent_->get_full_name() + "@";
310 
311  std::string my_name(name_);
312 
313  std::string full_name = parent_name + my_name;
314  return full_name;
315  }
316 
322  int countTimers() {
323  int count=1;
324  for (unsigned i=0;i<sub_timers_.size(); ++i)
325  count += sub_timers_[i].countTimers();
326  return count;
327  }
328 
329  void addTimerNames(Array<std::string> &names, unsigned &pos) {
330  names[pos++] = get_full_name();
331  for (unsigned i=0;i<sub_timers_.size(); ++i)
332  sub_timers_[i].addTimerNames(names, pos);
333  }
334 
340  double accumulatedTime(const std::string &locate_name="") {
341 
342  if (locate_name == "")
344 
345  std::string first_name,second_name;
346 
347  size_t i = locate_name.find_first_of('@');
348  if ( i >= locate_name.size() ) {
349  first_name = locate_name;
350  second_name = "";
351  } else {
352  first_name.assign(locate_name,0,i);
353  second_name.assign(locate_name,i+1,locate_name.size()-i-1);
354  }
355  for (unsigned j=0;j<sub_timers_.size();++j)
356  if ( first_name == sub_timers_[j].name_)
357  return sub_timers_[j].accumulatedTime(second_name);
358  return 0;
359  }
360 
362  unsigned level() const
363  {return level_;}
364 
365  protected:
372  void splitString(const std::string &locate_name, std::string &first_name, std::string &second_name) {
373  size_t i = locate_name.find_first_of('@');
374  if ( i >= locate_name.size() ) {
375  first_name = locate_name;
376  second_name = "";
377  } else {
378  first_name.assign(locate_name,0,i);
379  second_name.assign(locate_name,i+1,locate_name.size()-i-1);
380  }
381  }
382 
383  public:
389  double accumulatedTimePerUpdate(const std::string &locate_name="") {
390 
391  if (locate_name == "")
393 
394  std::string first_name,second_name;
395  splitString(locate_name, first_name, second_name);
396 
397  for (unsigned j=0;j<sub_timers_.size();j++)
398  if ( first_name == sub_timers_[j].name_)
399  return sub_timers_[j].accumulatedTimePerUpdate(second_name);
400  return 0;
401  }
402 
408  double accumulatedTimePerTimerCall(const std::string &locate_name="") {
409 
410  if (locate_name == "")
412 
413  std::string first_name,second_name;
414  splitString(locate_name, first_name, second_name);
415 
416  for (unsigned j=0;j<sub_timers_.size();j++)
417  if ( first_name == sub_timers_[j].name_)
418  return sub_timers_[j].accumulatedTimePerTimerCall(second_name);
419  return 0;
420  }
421 
425  void pack();
426 
432  LevelTimer* unpack(unsigned from);
433 
438  void report(std::ostream &os);
439 
445  const BaseTimer* findBaseTimer(const std::string &name) const;
446 
453  BaseTimer::TimeInfo findTimer(const std::string &name,bool& found);
454 
455  }; // LevelTimer
456 
457 
458 public:
464  explicit StackedTimer(const char *name, const bool start_base_timer = true)
465  : timer_(0,name,nullptr,false),
466  global_mpi_aggregation_called_(false),
467  enable_verbose_(false),
468  verbose_timestamp_levels_(0), // 0 disables
469  verbose_ostream_(Teuchos::rcpFromRef(std::cout)),
470  enable_timers_(true)
471  {
472  top_ = &timer_;
473  if (start_base_timer)
474  this->startBaseTimer();
475 
476  auto check_verbose = std::getenv("TEUCHOS_ENABLE_VERBOSE_TIMERS");
477  if (check_verbose != nullptr)
478  enable_verbose_ = true;
479 
480  auto check_timestamp = std::getenv("TEUCHOS_ENABLE_VERBOSE_TIMESTAMP_LEVELS");
481  if (check_timestamp != nullptr) {
482  verbose_timestamp_levels_ = std::atoi(check_timestamp);
483  }
484  }
485 
486  std::string name() {
487  return timer_.get_full_name();
488  }
489 
493  void startBaseTimer() {
494  timer_.BaseTimer::start();
495  }
496 
500  void stopBaseTimer() {
501  timer_.BaseTimer::stop();
502  }
503 
509  void start(const std::string name,
510  const bool push_kokkos_profiling_region = true) {
511  if (enable_timers_) {
512  if (top_ == nullptr) {
513  top_ = timer_.start(name.c_str());
514  } else {
515  top_ = top_->start(name.c_str());
516 #ifdef HAVE_TEUCHOSCORE_KOKKOS
517  if (Behavior::fenceTimers()) {
518  Kokkos::fence("timer_fence_begin_"+name);
519  }
520 #endif
521 #if defined(HAVE_TEUCHOS_KOKKOS_PROFILING) && defined(HAVE_TEUCHOSCORE_KOKKOS)
522  if (push_kokkos_profiling_region) {
523  ::Kokkos::Tools::pushRegion(name);
524  }
525 #endif
526  }
527  }
528  if (enable_verbose_) {
529  if (!verbose_timestamp_levels_) {
530  *verbose_ostream_ << "STARTING: " << name << std::endl;
531  }
532  // gcc 4.X is incomplete in c++11 standard - missing
533  // std::put_time. We'll disable this feature for gcc 4.
534 #if !defined(__GNUC__) || ( defined(__GNUC__) && (__GNUC__ > 4) )
535  else if (top_ != nullptr) {
536  if ( top_->level() <= verbose_timestamp_levels_) {
537  auto now = std::chrono::system_clock::now();
538  auto now_time = std::chrono::system_clock::to_time_t(now);
539  auto gmt = gmtime(&now_time);
540  auto timestamp = std::put_time(gmt, "%Y-%m-%d %H:%M:%S");
541  auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
542  *verbose_ostream_ << "STARTING: " << name << " LEVEL: " << top_->level() << " COUNT: " << timer_.numCalls() << " TIMESTAMP: " << timestamp << "." << ms.count() << std::endl;
543  }
544  }
545 #endif
546  }
547  }
548 
554  void stop(const std::string &name,
555  const bool pop_kokkos_profiling_region = true) {
556  if (enable_timers_) {
557  if (top_) {
558  top_ = top_->stop(name);
559 #ifdef HAVE_TEUCHOSCORE_KOKKOS
560  if (Behavior::fenceTimers()) {
561  Kokkos::fence("timer_fence_end_"+name);
562  }
563 #endif
564 #if defined(HAVE_TEUCHOS_KOKKOS_PROFILING) && defined(HAVE_TEUCHOSCORE_KOKKOS)
565  if (pop_kokkos_profiling_region) {
566  ::Kokkos::Tools::popRegion();
567  }
568 #endif
569  } else {
570  timer_.BaseTimer::stop();
571  }
572  }
573  if (enable_verbose_) {
574  if (!verbose_timestamp_levels_) {
575  *verbose_ostream_ << "STOPPING: " << name << std::endl;
576  }
577  // gcc 4.X is incomplete in c++11 standard - missing
578  // std::put_time. We'll disable this feature for gcc 4.
579 #if !defined(__GNUC__) || ( defined(__GNUC__) && (__GNUC__ > 4) )
580  // The stop adjusts the top level, need to adjust by +1 for printing
581  else if (top_ != nullptr) {
582  if ( (top_->level()+1) <= verbose_timestamp_levels_) {
583  auto now = std::chrono::system_clock::now();
584  auto now_time = std::chrono::system_clock::to_time_t(now);
585  auto gmt = gmtime(&now_time);
586  auto timestamp = std::put_time(gmt, "%Y-%m-%d %H:%M:%S");
587  auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
588  *verbose_ostream_ << "STOPPING: " << name << " LEVEL: " << top_->level()+1 << " COUNT: " << timer_.numCalls() << " TIMESTAMP: " << timestamp << "." << ms.count() << std::endl;
589  }
590  }
591 #endif
592  }
593  }
594 
599  void incrementUpdates(const long long i = 1) {
600  top_->incrementUpdates(i);
601  }
602 
608  double accumulatedTime(const std::string &name="") {
609  if (top_) // Top is null for the head node when nothing is running
610  return top_->accumulatedTime(name);
611  else
612  return timer_.accumulatedTime(name);
613  }
614 
620  double accumulatedTimePerUpdate(const std::string &name="") {
621  if (top_) // Top is null for the head node when nothing is running
622  return top_->accumulatedTimePerUpdate(name);
623  else
624  return timer_.accumulatedTimePerUpdate(name);
625  }
631  double accumulatedTimePerTimerCall(const std::string &name="") {
632  if (top_) // Top is null for the head node when nothing is running
633  return top_->accumulatedTimePerTimerCall(name);
634  else
635  return timer_.accumulatedTimePerTimerCall(name);
636  }
637 
643  const BaseTimer* findBaseTimer(const std::string &name) const {
644  const BaseTimer* baseTimer = timer_.findBaseTimer(name);
645  TEUCHOS_TEST_FOR_EXCEPTION(baseTimer == nullptr, std::runtime_error,
646  "StackedTimer::findBaseTimer() failed to find a timer named \"" << name << "\"!\n");
647  return baseTimer;
648  }
649 
655  BaseTimer::TimeInfo findTimer(const std::string &name) {
656  bool foundTimer = false;
657  const auto timeInfo = timer_.findTimer(name,foundTimer);
658  TEUCHOS_TEST_FOR_EXCEPTION(!foundTimer, std::runtime_error,
659  "StackedTimer::findTimer() failed to find a timer named \"" << name << "\"!\n");
660  return timeInfo;
661  }
662 
663  void report(std::ostream &os) {
664  timer_.report(os);
665  }
666 
681  struct OutputOptions {
682  OutputOptions() : output_fraction(false), output_total_updates(false), output_histogram(false),
683  output_minmax(false), output_proc_minmax(false), num_histogram(10), max_levels(INT_MAX),
684  print_warnings(true), align_columns(false), print_names_before_values(true),
685  drop_time(-1.0), output_per_proc_stddev(false) {}
696  double drop_time;
698  };
699 
706  void report(std::ostream &os, Teuchos::RCP<const Teuchos::Comm<int> > comm, OutputOptions options = OutputOptions());
707 
717  void reportXML(std::ostream &os, const std::string& datestamp, const std::string& timestamp, Teuchos::RCP<const Teuchos::Comm<int> > comm);
718 
750  std::string reportWatchrXML(const std::string& name, Teuchos::RCP<const Teuchos::Comm<int> > comm);
751 
753  void enableVerbose(const bool enable_verbose);
754 
756  void enableVerboseTimestamps(const unsigned levels);
757 
759  void setVerboseOstream(const Teuchos::RCP<std::ostream>& os);
760 
763  void disableTimers();
764 
767  void enableTimers();
768 
774  void aggregateMpiData(Teuchos::RCP<const Teuchos::Comm<int> > comm, OutputOptions options = OutputOptions());
775 
784  double getMpiAverageTime(const std::string& flat_timer_name);
785 
794  double getMpiAverageCount(const std::string& flat_timer_name);
795 
804  bool isTimer(const std::string& flat_timer_name);
805 
806 protected:
811 
812  // Global MPI aggregation arrays
827 
830  std::string::size_type timer_names_;
831  std::string::size_type average_time_;
832  std::string::size_type fraction_;
833  std::string::size_type count_;
834  std::string::size_type total_updates_;
835  std::string::size_type min_;
836  std::string::size_type max_;
837  std::string::size_type procmin_;
838  std::string::size_type procmax_;
839  std::string::size_type stddev_;
840  std::string::size_type histogram_;
842  timer_names_(0),
843  average_time_(0),
844  fraction_(0),
845  count_(0),
846  total_updates_(0),
847  min_(0),
848  max_(0),
849  procmax_(0),
850  stddev_(0),
851  histogram_(0){}
852  } alignments_;
853 
856 
859 
862 
865 
869  void flatten();
870 
875  void merge(Teuchos::RCP<const Teuchos::Comm<int> > comm);
876 
880  void collectRemoteData(Teuchos::RCP<const Teuchos::Comm<int> > comm, const OutputOptions &options );
881 
885  int getFlatNameIndex(const std::string& flat_timer_name);
886 
893  double computeColumnWidthsForAligment(std::string prefix, int print_level,
894  std::vector<bool> &printed, double parent_time,
895  const OutputOptions &options);
896 
900  double printLevel(std::string prefix, int level, std::ostream &os, std::vector<bool> &printed,
901  double parent_time, const OutputOptions &options);
902 
907  double printLevelXML(std::string prefix, int level, std::ostream &os, std::vector<bool> &printed, double parent_time, const std::string& rootName = "");
908 
909 }; //StackedTimer
910 
911 
912 } //namespace Teuchos
913 
914 #endif /* TEUCHOS_STACKED_TIMER_HPP */
void setAccumulatedTime(double accum=0)
Setter for accumulated time.
double accumulatedTimePerTimerCall(const std::string &name="")
Clock::time_point start_time_
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
Array< double > per_proc_stddev_max_
void addTimerNames(Array< std::string > &names, unsigned &pos)
#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)
Array< double > per_proc_stddev_min_
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.
Array< Array< int > > hist_
std::chrono::high_resolution_clock Clock
LevelTimer(const LevelTimer &src)
Copy constructor.
static bool fenceTimers()
Array< unsigned long long > updates_
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.
Array< std::string > flat_names_
void stop()
Stop a current running timer and accumulate time difference.
unsigned level() const
Returns the level of the timer in the stack.
void report(std::ostream &os)
#define TEUCHOSCOMM_LIB_DLL_EXPORT
unsigned long long count_updates_
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.
Array< unsigned long > count_
Reference-counted pointer class and non-member templated function implementations.
LevelTimer * start(const char *sub_name)