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 //
4 // Teuchos: Common Tools Package
5 // Copyright (2004) Sandia Corporation
6 //
7 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
8 // license for use of this work by or on behalf of the U.S. Government.
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are
12 // met:
13 //
14 // 1. Redistributions of source code must retain the above copyright
15 // notice, this list of conditions and the following disclaimer.
16 //
17 // 2. Redistributions in binary form must reproduce the above copyright
18 // notice, this list of conditions and the following disclaimer in the
19 // documentation and/or other materials provided with the distribution.
20 //
21 // 3. Neither the name of the Corporation nor the names of the
22 // contributors may be used to endorse or promote products derived from
23 // this software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
26 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
29 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 //
37 // ***********************************************************************
38 // @HEADER
39 
40 #ifndef TEUCHOS_STACKED_TIMER_HPP
41 #define TEUCHOS_STACKED_TIMER_HPP
42 
43 #include "Teuchos_ConfigDefs.hpp"
44 #include "Teuchos_Comm.hpp"
45 #include "Teuchos_DefaultComm.hpp"
46 #include "Teuchos_CommHelpers.hpp"
47 #include "Teuchos_RCP.hpp"
48 #include "Teuchos_Array.hpp"
50 #include <string>
51 #include <vector>
52 #include <cassert>
53 #include <chrono>
54 #include <climits>
55 #include <cstdlib> // for std::getenv and atoi
56 #include <ctime> // for timestamp support
57 #include <iostream>
58 
59 #if defined(HAVE_TEUCHOS_KOKKOS_PROFILING) && defined(HAVE_TEUCHOSCORE_KOKKOS)
60 namespace Kokkos {
61 namespace Profiling {
62 extern void pushRegion (const std::string&);
63 extern void popRegion ();
64 } // namespace Profiling
65 } // namespace Kokkos
66 #endif
67 
68 
69 namespace Teuchos {
70 
72 TEUCHOSCOMM_LIB_DLL_EXPORT void error_out(const std::string& msg, const bool fail_all = false);
73 
83 class BaseTimer {
84 
85 public:
86 
87  using Clock = std::chrono::high_resolution_clock;
88 
89  BaseTimer() : accumulation_(0.0), count_started_(0), count_updates_(0), running_(false) {}
90 
92  void start(){
93  if (running_)
94  error_out("Base_Timer:start Failed timer already running");
95  start_time_ = Clock::now();
96 
97  count_started_++;
98  running_ = true;
99  }
100 
102  void stop(){
103  if (!running_)
104  error_out("Base_Timer:stop Failed timer not running");
105  accumulation_ += std::chrono::duration_cast<std::chrono::duration<double>>(Clock::now() - start_time_).count();
106  running_ = false;
107  }
108 
110  unsigned long long incrementUpdates(unsigned long long count=1) {count_updates_ += count; return count_updates_;}
111 
113  double accumulatedTime() const {return accumulation_;}
114 
116  void setAccumulatedTime(double accum=0) {accumulation_=accum;}
117  double accumulatedTimePerUpdate() const {
127  if (count_updates_ > 0) {
128  return accumulation_/count_updates_;
129  } else {
130  return 0;
131  }
132  }
133 
134 
143  if (count_started_> 0) {
144  return accumulation_/count_started_;
145  } else {
146  return 0;
147  }
148  }
149 
156  double difference(const BaseTimer &from) const {
157  return accumulation_ - from.accumulation_;
158  }
159 
161  void reset() {
162  if (running_)
163  error_out("BaseTimer, cannot reset a running timer");
164  accumulation_ = 0.0;
165  count_started_ = count_updates_ = 0;
166  }
167 
169  bool running() const { return running_;}
170 
172  unsigned long numCalls() const { return count_started_; }
173 
175  unsigned long long numUpdates() const { return count_updates_; }
176 
178  void overrideNumCallsForUnitTesting(const unsigned long num_calls)
179  { count_started_ = num_calls; }
180 
182  void overrideNumUpdatesForUnitTesting(const unsigned long long num_updates)
183  { count_updates_ = num_updates; }
184 
185  struct TimeInfo {
186  TimeInfo():time(0.0), count(0), updates(0), running(false){}
187  TimeInfo(BaseTimer* t): time(t->accumulation_), count(t->count_started_), updates(t->count_updates_), running(t->running()) {}
188  double time;
189  unsigned long count;
190  unsigned long long updates;
191  bool running;
192  };
193 
194 protected:
195  double accumulation_; // total time
196  unsigned long count_started_; // Number of times this timer has been started
197  unsigned long long count_updates_; // Total count of items updated during this timer
198  Clock::time_point start_time_;
199  bool running_;
200 
201  friend struct TimeInfo;
202 };
203 
221 class TEUCHOSCOMM_LIB_DLL_EXPORT StackedTimer
222 {
223 protected:
224 
233  class LevelTimer : public BaseTimer {
234  protected:
235 
236  // TODO: implement operator=
237 
238  unsigned level_;
239  std::string name_;
240  LevelTimer *parent_;
241  std::vector<LevelTimer> sub_timers_;
242  public:
244  LevelTimer();
245 
253  LevelTimer(int level,
254  const char* name = "RootTimer",
255  LevelTimer *parent=nullptr,
256  bool start_timer=true) :
257  BaseTimer(),
258  level_(level),
259  name_(name),
260  parent_(parent)
261  {
262  if ( start_timer )
264 
265  }
266 
268  LevelTimer(const LevelTimer &src) :
269  BaseTimer(src), level_(src.level_), name_(src.name_),parent_(src.parent_), sub_timers_(src.sub_timers_)
270  {
271  for (unsigned i=0;i<sub_timers_.size();++i)
272  sub_timers_[i].parent_ = this;
273  }
274 
280  LevelTimer* start(const char* sub_name) {
281  for (unsigned i=0;i<sub_timers_.size();i++ )
282  if (sub_name == sub_timers_[i].name_ ) {
283  sub_timers_[i].BaseTimer::start();
284  return &sub_timers_[i];
285  }
286  sub_timers_.push_back(LevelTimer(level_+1,sub_name,this,true));
287  return &sub_timers_[sub_timers_.size()-1];
288  }
289 
297  LevelTimer* stop(const std::string &name = "RootTimer") {
298  if (name != name_)
299  error_out("Stopping timer "+name+" But top level running timer is "+name_);
300  BaseTimer::stop();
301  return parent_;
302  }
303 
304 
309  std::string get_full_name() const {
310  std::string parent_name("");
311  if ((parent_ != nullptr))
312  parent_name = parent_->get_full_name() + "@";
313 
314  std::string my_name(name_);
315 
316  std::string full_name = parent_name + my_name;
317  return full_name;
318  }
319 
325  int countTimers() {
326  int count=1;
327  for (unsigned i=0;i<sub_timers_.size(); ++i)
328  count += sub_timers_[i].countTimers();
329  return count;
330  }
331 
332  void addTimerNames(Array<std::string> &names, unsigned &pos) {
333  names[pos++] = get_full_name();
334  for (unsigned i=0;i<sub_timers_.size(); ++i)
335  sub_timers_[i].addTimerNames(names, pos);
336  }
337 
343  double accumulatedTime(const std::string &locate_name="") {
344 
345  if (locate_name == "")
347 
348  std::string first_name,second_name;
349 
350  size_t i = locate_name.find_first_of('@');
351  if ( i >= locate_name.size() ) {
352  first_name = locate_name;
353  second_name = "";
354  } else {
355  first_name.assign(locate_name,0,i);
356  second_name.assign(locate_name,i+1,locate_name.size()-i-1);
357  }
358  for (unsigned j=0;j<sub_timers_.size();++j)
359  if ( first_name == sub_timers_[j].name_)
360  return sub_timers_[j].accumulatedTime(second_name);
361  return 0;
362  }
363 
365  unsigned level() const
366  {return level_;}
367 
368  protected:
375  void splitString(const std::string &locate_name, std::string &first_name, std::string &second_name) {
376  size_t i = locate_name.find_first_of('@');
377  if ( i >= locate_name.size() ) {
378  first_name = locate_name;
379  second_name = "";
380  } else {
381  first_name.assign(locate_name,0,i);
382  second_name.assign(locate_name,i+1,locate_name.size()-i-1);
383  }
384  }
385 
386  public:
392  double accumulatedTimePerUpdate(const std::string &locate_name="") {
393 
394  if (locate_name == "")
396 
397  std::string first_name,second_name;
398  splitString(locate_name, first_name, second_name);
399 
400  for (unsigned j=0;j<sub_timers_.size();j++)
401  if ( first_name == sub_timers_[j].name_)
402  return sub_timers_[j].accumulatedTimePerUpdate(second_name);
403  return 0;
404  }
405 
411  double accumulatedTimePerTimerCall(const std::string &locate_name="") {
412 
413  if (locate_name == "")
415 
416  std::string first_name,second_name;
417  splitString(locate_name, first_name, second_name);
418 
419  for (unsigned j=0;j<sub_timers_.size();j++)
420  if ( first_name == sub_timers_[j].name_)
421  return sub_timers_[j].accumulatedTimePerTimerCall(second_name);
422  return 0;
423  }
424 
428  void pack();
429 
435  LevelTimer* unpack(unsigned from);
436 
441  void report(std::ostream &os);
442 
448  const BaseTimer* findBaseTimer(const std::string &name) const;
449 
456  BaseTimer::TimeInfo findTimer(const std::string &name,bool& found);
457 
458  }; // LevelTimer
459 
460 
461 public:
467  explicit StackedTimer(const char *name, const bool start_base_timer = true)
468  : timer_(0,name,nullptr,false),
469  global_mpi_aggregation_called_(false),
470  enable_verbose_(false),
471  verbose_timestamp_levels_(0), // 0 disables
472  verbose_ostream_(Teuchos::rcpFromRef(std::cout)),
473  enable_timers_(true)
474  {
475  top_ = &timer_;
476  if (start_base_timer)
477  this->startBaseTimer();
478 
479  auto check_verbose = std::getenv("TEUCHOS_ENABLE_VERBOSE_TIMERS");
480  if (check_verbose != nullptr)
481  enable_verbose_ = true;
482 
483  auto check_timestamp = std::getenv("TEUCHOS_ENABLE_VERBOSE_TIMESTAMP_LEVELS");
484  if (check_timestamp != nullptr) {
485  verbose_timestamp_levels_ = std::atoi(check_timestamp);
486  }
487  }
488 
492  void startBaseTimer() {
493  timer_.BaseTimer::start();
494 #if defined(HAVE_TEUCHOS_KOKKOS_PROFILING) && defined(HAVE_TEUCHOSCORE_KOKKOS)
495  ::Kokkos::Profiling::pushRegion(timer_.get_full_name());
496 #endif
497  }
498 
502  void stopBaseTimer() {
503  timer_.BaseTimer::stop();
504 #if defined(HAVE_TEUCHOS_KOKKOS_PROFILING) && defined(HAVE_TEUCHOSCORE_KOKKOS)
505  ::Kokkos::Profiling::popRegion();
506 #endif
507  }
508 
514  void start(const std::string name,
515  const bool push_kokkos_profiling_region = true) {
516  if (enable_timers_) {
517  if (top_ == nullptr)
518  top_ = timer_.start(name.c_str());
519  else
520  top_ = top_->start(name.c_str());
521 #if defined(HAVE_TEUCHOS_KOKKOS_PROFILING) && defined(HAVE_TEUCHOSCORE_KOKKOS)
522  if (push_kokkos_profiling_region) {
523  ::Kokkos::Profiling::pushRegion(name);
524  }
525 #endif
526  }
527  if (enable_verbose_) {
528  if (!verbose_timestamp_levels_) {
529  *verbose_ostream_ << "STARTING: " << name << std::endl;
530  }
531  // gcc 4.X is incomplete in c++11 standard - missing
532  // std::put_time. We'll disable this feature for gcc 4.
533 #if !defined(__GNUC__) || ( defined(__GNUC__) && (__GNUC__ > 4) )
534  else if (top_ != nullptr) {
535  if ( top_->level() <= verbose_timestamp_levels_) {
536  auto now = std::chrono::system_clock::now();
537  auto now_time = std::chrono::system_clock::to_time_t(now);
538  auto gmt = gmtime(&now_time);
539  auto timestamp = std::put_time(gmt, "%Y-%m-%d %H:%M:%S");
540  auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
541  *verbose_ostream_ << "STARTING: " << name << " LEVEL: " << top_->level() << " COUNT: " << timer_.numCalls() << " TIMESTAMP: " << timestamp << "." << ms.count() << std::endl;
542  }
543  }
544 #endif
545  }
546  }
547 
553  void stop(const std::string &name,
554  const bool pop_kokkos_profiling_region = true) {
555  if (enable_timers_) {
556  if (top_)
557  top_ = top_->stop(name);
558  else
559  timer_.BaseTimer::stop();
560 #if defined(HAVE_TEUCHOS_KOKKOS_PROFILING) && defined(HAVE_TEUCHOSCORE_KOKKOS)
561  if (pop_kokkos_profiling_region) {
562  ::Kokkos::Profiling::popRegion();
563  }
564 #endif
565  }
566  if (enable_verbose_) {
567  if (!verbose_timestamp_levels_) {
568  *verbose_ostream_ << "STOPPING: " << name << std::endl;
569  }
570  // gcc 4.X is incomplete in c++11 standard - missing
571  // std::put_time. We'll disable this feature for gcc 4.
572 #if !defined(__GNUC__) || ( defined(__GNUC__) && (__GNUC__ > 4) )
573  // The stop adjusts the top level, need to adjust by +1 for printing
574  else if (top_ != nullptr) {
575  if ( (top_->level()+1) <= verbose_timestamp_levels_) {
576  auto now = std::chrono::system_clock::now();
577  auto now_time = std::chrono::system_clock::to_time_t(now);
578  auto gmt = gmtime(&now_time);
579  auto timestamp = std::put_time(gmt, "%Y-%m-%d %H:%M:%S");
580  auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
581  *verbose_ostream_ << "STOPPING: " << name << " LEVEL: " << top_->level()+1 << " COUNT: " << timer_.numCalls() << " TIMESTAMP: " << timestamp << "." << ms.count() << std::endl;
582  }
583  }
584 #endif
585  }
586  }
587 
592  void incrementUpdates(const long long i = 1) {
593  top_->incrementUpdates(i);
594  }
595 
601  double accumulatedTime(const std::string &name="") {
602  if (top_) // Top is null for the head node when nothing is running
603  return top_->accumulatedTime(name);
604  else
605  return timer_.accumulatedTime(name);
606  }
607 
613  double accumulatedTimePerUpdate(const std::string &name="") {
614  if (top_) // Top is null for the head node when nothing is running
615  return top_->accumulatedTimePerUpdate(name);
616  else
617  return timer_.accumulatedTimePerUpdate(name);
618  }
624  double accumulatedTimePerTimerCall(const std::string &name="") {
625  if (top_) // Top is null for the head node when nothing is running
626  return top_->accumulatedTimePerTimerCall(name);
627  else
628  return timer_.accumulatedTimePerTimerCall(name);
629  }
630 
636  const BaseTimer* findBaseTimer(const std::string &name) const {
637  const BaseTimer* baseTimer = timer_.findBaseTimer(name);
638  TEUCHOS_TEST_FOR_EXCEPTION(baseTimer == nullptr, std::runtime_error,
639  "StackedTimer::findBaseTimer() failed to find a timer named \"" << name << "\"!\n");
640  return baseTimer;
641  }
642 
648  BaseTimer::TimeInfo findTimer(const std::string &name) {
649  bool foundTimer = false;
650  const auto timeInfo = timer_.findTimer(name,foundTimer);
651  TEUCHOS_TEST_FOR_EXCEPTION(!foundTimer, std::runtime_error,
652  "StackedTimer::findTimer() failed to find a timer named \"" << name << "\"!\n");
653  return timeInfo;
654  }
655 
656  void report(std::ostream &os) {
657  timer_.report(os);
658  }
659 
673  struct OutputOptions {
674  OutputOptions() : output_fraction(false), output_total_updates(false), output_histogram(false),
675  output_minmax(false), output_proc_minmax(false), num_histogram(10), max_levels(INT_MAX),
676  print_warnings(true), align_columns(false), print_names_before_values(true),
677  drop_time(-1.0) {}
678  bool output_fraction;
679  bool output_total_updates;
680  bool output_histogram;
681  bool output_minmax;
682  bool output_proc_minmax;
683  int num_histogram;
684  int max_levels;
685  bool print_warnings;
686  bool align_columns;
687  bool print_names_before_values;
688  double drop_time;
689  };
690 
697  void report(std::ostream &os, Teuchos::RCP<const Teuchos::Comm<int> > comm, OutputOptions options = OutputOptions());
698 
708  void reportXML(std::ostream &os, const std::string& datestamp, const std::string& timestamp, Teuchos::RCP<const Teuchos::Comm<int> > comm);
709 
741  std::string reportWatchrXML(const std::string& name, Teuchos::RCP<const Teuchos::Comm<int> > comm);
742 
744  void enableVerbose(const bool enable_verbose);
745 
747  void enableVerboseTimestamps(const unsigned levels);
748 
750  void setVerboseOstream(const Teuchos::RCP<std::ostream>& os);
751 
754  void disableTimers();
755 
758  void enableTimers();
759 
765  void aggregateMpiData(Teuchos::RCP<const Teuchos::Comm<int> > comm, OutputOptions options = OutputOptions());
766 
775  double getMpiAverageTime(const std::string& flat_timer_name);
776 
785  double getMpiAverageCount(const std::string& flat_timer_name);
786 
795  bool isTimer(const std::string& flat_timer_name);
796 
797 protected:
802 
803  // Global MPI aggregation arrays
804  Array<std::string> flat_names_;
805  Array<double> min_;
806  Array<double> max_;
807  Array<int> procmin_;
808  Array<int> procmax_;
809  Array<double> sum_;
810  Array<double> sum_sq_;
811  Array<Array<int>> hist_;
812  Array<unsigned long> count_;
813  Array<unsigned long long> updates_;
814  Array<int> active_;
815  bool global_mpi_aggregation_called_;
816 
819  std::string::size_type timer_names_;
820  std::string::size_type average_time_;
821  std::string::size_type fraction_;
822  std::string::size_type count_;
823  std::string::size_type total_updates_;
824  std::string::size_type min_;
825  std::string::size_type max_;
826  std::string::size_type procmin_;
827  std::string::size_type procmax_;
828  std::string::size_type stddev_;
829  std::string::size_type histogram_;
830  AlignmentWidths() :
831  timer_names_(0),
832  average_time_(0),
833  fraction_(0),
834  count_(0),
835  total_updates_(0),
836  min_(0),
837  max_(0),
838  procmax_(0),
839  stddev_(0),
840  histogram_(0){}
841  } alignments_;
842 
845 
848 
851 
854 
858  void flatten();
859 
864  void merge(Teuchos::RCP<const Teuchos::Comm<int> > comm);
865 
869  void collectRemoteData(Teuchos::RCP<const Teuchos::Comm<int> > comm, const OutputOptions &options );
870 
874  int getFlatNameIndex(const std::string& flat_timer_name);
875 
882  double computeColumnWidthsForAligment(std::string prefix, int print_level,
883  std::vector<bool> &printed, double parent_time,
884  const OutputOptions &options);
885 
889  double printLevel(std::string prefix, int level, std::ostream &os, std::vector<bool> &printed,
890  double parent_time, const OutputOptions &options);
891 
896  double printLevelXML(std::string prefix, int level, std::ostream &os, std::vector<bool> &printed, double parent_time, const std::string& rootName = "");
897 
898 }; //StackedTimer
899 
900 
901 } //namespace Teuchos
902 
903 #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.
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)