LCOV - code coverage report
Current view: top level - tools - Stopwatch.h (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 49 54 90.7 %
Date: 2019-08-13 10:15:31 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2012-2019 The plumed team
       3             :    (see the PEOPLE file at the root of the distribution for a list of names)
       4             : 
       5             :    See http://www.plumed.org for more information.
       6             : 
       7             :    This file is part of plumed, version 2.
       8             : 
       9             :    plumed is free software: you can redistribute it and/or modify
      10             :    it under the terms of the GNU Lesser General Public License as published by
      11             :    the Free Software Foundation, either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    plumed is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU Lesser General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU Lesser General Public License
      20             :    along with plumed.  If not, see <http://www.gnu.org/licenses/>.
      21             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      22             : #ifndef __PLUMED_tools_Stopwatch_h
      23             : #define __PLUMED_tools_Stopwatch_h
      24             : 
      25             : #include "Exception.h"
      26             : #include <string>
      27             : #include <unordered_map>
      28             : #include <iosfwd>
      29             : #include <chrono>
      30             : 
      31             : namespace PLMD {
      32             : 
      33             : /**
      34             : \ingroup TOOLBOX
      35             : Class implementing stopwatch to time execution.
      36             : 
      37             : Each instance of this class is a container which
      38             : can keep track of several named stopwatches at
      39             : the same time. Access to the stopwatches
      40             : is obtained using start(), stop(), pause() methods,
      41             : giving as a parameter the name of the specific stopwatch.
      42             : Also an empty string can be used (un-named stopwatch).
      43             : Finally, all the times can be logged using << operator
      44             : 
      45             : \verbatim
      46             : #include "Stopwatch.h"
      47             : 
      48             : int main(){
      49             :   Stopwatch sw;
      50             :   sw.start();
      51             : 
      52             :   sw.start("initialization");
      53             : // do initialization ...
      54             :   sw.stop("initialization");
      55             : 
      56             :   for(int i=0;i<100;i++){
      57             :     sw.start("loop");
      58             : // do calculation
      59             :     sw.stop("loop");
      60             :   }
      61             : 
      62             :   sw.stop();
      63             :   return 0;
      64             : }
      65             : 
      66             : \endverbatim
      67             : 
      68             : Using pause a stopwatch can be put on hold until
      69             : the next start:
      70             : 
      71             : \verbatim
      72             : #include "Stopwatch.h"
      73             : 
      74             : int main(){
      75             :   Stopwatch sw;
      76             :   sw.start();
      77             : 
      78             :   sw.start("initialization");
      79             : // do initialization ...
      80             :   sw.stop("initialization");
      81             : 
      82             :   for(int i=0;i<100;i++){
      83             :     sw.start("loop");
      84             : // do calculation
      85             :     sw.pause("loop");
      86             : // here goes something that we do not want to include
      87             :     sw.start("loop");
      88             : // do calculation
      89             :     sw.stop("loop");
      90             :   }
      91             : 
      92             :   sw.stop();
      93             :   return 0;
      94             : }
      95             : 
      96             : \endverbatim
      97             : 
      98             : Notice that as of PLUMED 2.5 it is possible to use a slightly modified
      99             : interface that allow for exception safety. In pratice,
     100             : one can replace a pair of calls to Stopwatch::start() and Stopwatch::stop()
     101             : with a single call to Stopwatch::startStop(). This call will return an object
     102             : that, when goes out of scope, will stop the timer.
     103             : 
     104             : \notice The exception safety interace is highly recommended since it allows
     105             : to make sure that stopwatches are started and stopped consistently.
     106             : 
     107             : For instance the following
     108             : code
     109             : \verbatim
     110             :   {
     111             :     sw.start("A");
     112             :   // any code
     113             :     sw.stop("A");
     114             :   }
     115             : \endverbatim
     116             : can be replaced with
     117             : \verbatim
     118             :   {
     119             :     auto sww=sw.startStop("A");
     120             :   // any code
     121             : 
     122             :   // stopwatch is stopped when sww goes out of scope
     123             :   }
     124             : \endverbatim
     125             : Similarly, Stopwatch::startPause() can be used to replace a pair of
     126             : Stopwatch::start() and Stopwatch::pause().
     127             : 
     128             : The older syntax (explicitly calling `Stopwatch::start()` and `Stopwatch::pause()`) is still
     129             : allowed for backward compatibility.
     130             : 
     131             : Notice that the returned object is of type `Stopwatch::Handler`.
     132             : You might be willing to explicitly declare a `Stopwatch::Handler` (instead of using `auto`)
     133             : when you want to conditionally start the stopwatch. For instance:
     134             : \verbatim
     135             :   {
     136             :     Stopwatch::Handler handler;
     137             :     if(you_want_to_time_this) handler=sw.startStop();
     138             :     ... do something ...
     139             :   }
     140             :   // in case it was started, the stopwatch will stop here, at the end of the block
     141             :   // in case it was not started, nothing will happen
     142             : \endverbatim
     143             : 
     144             : A `Stopwatch::Handler` can not be copied but it can be moved (it behaves like a unique_ptr).
     145             : Moving it explicitly allows one to transfer it to another `Stopwatch::Handler` with a different scope.
     146             : For instance, in case you want to conditionally stop the stopwatch you might use something like this:
     147             : \verbatim
     148             :   {
     149             :     Stopwatch::Handler handler;
     150             :     if(you_want_to_time_this) handler=sw.startStop();
     151             :     ... do something ...
     152             :     if(you_want_to_stop_here) auto h2=std::move(handler);
     153             :     // the previous instruction moves handler to h2 that is then destroyed, stopping the watch
     154             :     // notice that if the stop was not started it will not stop.
     155             :     ... do something else ...
     156             :   }
     157             :   // in case it is running, the stopwatch will stop here, at the end of the block
     158             : \endverbatim
     159             : 
     160             : Finally, notice that in order to write the timers on an output file when the
     161             : Stopwatch is destroyed, one can store a reference to a PLMD::Log by passing it
     162             : to the Stopwatch constructor.
     163             : This will make sure timers are written also in case of a premature end.
     164             : */
     165             : 
     166             : class Log;
     167             : 
     168             : class Stopwatch {
     169             : /// Simple function returning an empty string.
     170             : /// Used to simplify Stopwatch interface.
     171      520191 :   static const std::string & emptyString() {
     172      522137 :     static std::string s;
     173      520191 :     return s;
     174             :   }
     175             : 
     176             : public:
     177             : /// Forward declaration
     178             :   class Watch;
     179             : /// Auxiliary class for handling exception-safe start/pause and start/stop.
     180             :   class Handler {
     181             :     Watch* watch=nullptr;
     182             :     /// stop (true) or pause (false).
     183             :     /// might be changed to an enum if clearer.
     184             :     bool stop=false;
     185             :     /// Private constructor.
     186             :     /// This is kept private to avoid misuse. Handler objects should
     187             :     /// only be created using startPause() or startStop().
     188             :     /// stop is required to know if the destructor should stop or pause the watch.
     189             :     Handler(Watch* watch,bool stop);
     190             :     /// Allows usage of private constructor
     191             :     friend class Stopwatch;
     192             :   public:
     193             :     /// Default constructor
     194             :     Handler() = default;
     195             :     /// Default copy constructor is deleted (not copyable)
     196             :     Handler(const Handler & handler) = delete;
     197             :     /// Default copy assignment is deleted (not copyable)
     198             :     Handler & operator=(const Handler & handler) = delete;
     199             :     /// Move constructor.
     200             :     Handler(Handler && handler) noexcept;
     201             :     /// Move assignemnt.
     202             :     Handler & operator=(Handler && handler) noexcept;
     203             :     /// Destructor either stops or pauses the watch
     204             :     ~Handler();
     205             :   };
     206             : 
     207             : /// Class to store a single stopwatch.
     208             : /// Class Stopwatch contains a collection of them
     209        7687 :   class Watch {
     210             : /// Instant in time when Watch was started last time
     211             :     std::chrono::time_point<std::chrono::high_resolution_clock> lastStart;
     212             : /// Total accumulated time, in nanoseconds
     213             :     long long int total = 0;
     214             : /// Accumulated time for this lap, in nanoseconds
     215             :     long long int lap = 0;
     216             : /// Slowest lap so far, in nanoseconds
     217             :     long long int max = 0;
     218             : /// Fastest lap so far, in nanoseconds
     219             :     long long int min = 0;
     220             : /// Total number of cycles
     221             :     unsigned cycles = 0;
     222             : /// count how many times Watch was started (+1) or stopped/paused (-1).
     223             :     unsigned running = 0;
     224             :     enum class State {started, stopped, paused};
     225             : /// keep track of state
     226             :     State state = State::stopped;
     227             : /// Allows access to internal data
     228             :     friend class Stopwatch;
     229             :   public:
     230             : /// start the watch
     231             :     Watch & start();
     232             : /// stop the watch
     233             :     Watch & stop();
     234             : /// pause the watch
     235             :     Watch & pause();
     236             : /// returns a start-stop handler
     237             :     Handler startStop();
     238             : /// returns a start-pause handler
     239             :     Handler startPause();
     240             :   };
     241             : 
     242             : private:
     243             : 
     244             : /// Pointer to a log file.
     245             : /// If set, the stopwatch is logged in its destructor.
     246             :   Log*mylog=nullptr;
     247             : 
     248             : /// List of watches.
     249             : /// Each watch is labeled with a string.
     250             :   std::unordered_map<std::string,Watch> watches;
     251             : 
     252             : /// Log over stream os.
     253             :   std::ostream& log(std::ostream& os)const;
     254             : 
     255             : public:
     256             : // Constructor.
     257        1124 :   explicit Stopwatch() = default;
     258             : // Constructor.
     259             : // When destructing, stopwatch is logged.
     260             : // Make sure that log survives stopwatch. Typically, it should be declared earlier, in order
     261             : // to be destroyed later.
     262        5466 :   explicit Stopwatch(Log&log): mylog(&log) {}
     263             : // Destructor.
     264             :   ~Stopwatch();
     265             : /// Start timer named "name"
     266        2202 :   Stopwatch& start(const std::string&name=emptyString());
     267             : /// Stop timer named "name"
     268             :   Stopwatch& stop(const std::string&name=emptyString());
     269             : /// Pause timer named "name"
     270             :   Stopwatch& pause(const std::string&name=emptyString());
     271             : /// Dump all timers on an ostream
     272             :   friend std::ostream& operator<<(std::ostream&,const Stopwatch&);
     273             : /// Start with exception safety, then stop.
     274             : /// Starts the Stopwatch and returns an object that, when goes out of scope,
     275             : /// stops the watch. This allows Stopwatch to be started and stopped in
     276             : /// an exception safe manner.
     277      517989 :   Handler startStop(const std::string&name=emptyString());
     278             : /// Start with exception safety, then pause.
     279             : /// Starts the Stopwatch and returns an object that, when goes out of scope,
     280             : /// pauses the watch. This allows Stopwatch to be started and paused in
     281             : /// an exception safe manner.
     282             :   Handler startPause(const std::string&name=emptyString());
     283             : };
     284             : 
     285             : inline
     286             : Stopwatch::Handler::Handler(Watch* watch,bool stop) :
     287             :   watch(watch),
     288     1026755 :   stop(stop)
     289             : {
     290             :   watch->start();
     291             : }
     292             : 
     293             : inline
     294     2020810 : Stopwatch::Handler::~Handler() {
     295     2020810 :   if(watch) {
     296     1026755 :     if(stop) watch->stop();
     297      517989 :     else watch->pause();
     298             :   }
     299     2020810 : }
     300             : 
     301             : inline
     302        1108 : Stopwatch& Stopwatch::start(const std::string & name) {
     303             :   watches[name].start();
     304        1108 :   return *this;
     305             : }
     306             : 
     307             : inline
     308             : Stopwatch& Stopwatch::stop(const std::string & name) {
     309         554 :   watches[name].stop();
     310             :   return *this;
     311             : }
     312             : 
     313             : inline
     314             : Stopwatch& Stopwatch::pause(const std::string & name) {
     315         551 :   watches[name].pause();
     316             :   return *this;
     317             : }
     318             : 
     319             : inline
     320      508766 : Stopwatch::Handler Stopwatch::startStop(const std::string&name) {
     321      508766 :   return watches[name].startStop();
     322             : }
     323             : 
     324             : inline
     325      517989 : Stopwatch::Handler Stopwatch::startPause(const std::string&name) {
     326      517989 :   return watches[name].startPause();
     327             : }
     328             : 
     329             : inline
     330             : Stopwatch::Handler::Handler(Handler && handler) noexcept :
     331             :   watch(handler.watch),
     332             :   stop(handler.stop)
     333             : {
     334             :   handler.watch=nullptr;
     335             : }
     336             : 
     337             : inline
     338        3493 : Stopwatch::Handler & Stopwatch::Handler::operator=(Handler && handler) noexcept {
     339        3493 :   if(this!=&handler) {
     340        3493 :     if(watch) {
     341           0 :       if(stop) {
     342             :         try {
     343           0 :           watch->stop();
     344           0 :         } catch(...) {
     345             : // this is to avoid problems with cppcheck, given than this method is declared as
     346             : // noexcept and stop might throw in case of an internal bug
     347           0 :           std::terminate();
     348             :         }
     349             :       }
     350           0 :       else watch->pause();
     351             :     }
     352             : // cppcheck complains about this:
     353             : // cppcheck-suppress uselessAssignmentPtrArg
     354        3493 :     watch=handler.watch;
     355        3493 :     stop=handler.stop;
     356        3493 :     handler.watch=nullptr;
     357             :   }
     358        3493 :   return *this;
     359             : }
     360             : 
     361             : inline
     362             : Stopwatch::Watch & Stopwatch::Watch::start() {
     363     1028634 :   state=State::started;
     364     1028634 :   running++;
     365     1028634 :   lastStart=std::chrono::high_resolution_clock::now();
     366             :   return *this;
     367             : }
     368             : 
     369             : inline
     370      510091 : Stopwatch::Watch & Stopwatch::Watch::stop() {
     371      510091 :   pause();
     372      510091 :   state=State::stopped;
     373      510091 :   cycles++;
     374      510091 :   total+=lap;
     375      510091 :   if(lap>max)max=lap;
     376      510091 :   if(min>lap || cycles==1)min=lap;
     377      510091 :   lap=0;
     378      510091 :   return *this;
     379             : }
     380             : 
     381             : inline
     382     1028546 : Stopwatch::Watch & Stopwatch::Watch::pause() {
     383     1028546 :   state=State::paused;
     384             : // In case of an internal bug (non matching start stop within the startStop or startPause interface)
     385             : // this assertion could fail in a destructor.
     386             : // If this happens, the program should crash immediately
     387     1028546 :   plumed_assert(running>0) << "Non matching start/pause or start/stop commands in a Stopwatch";
     388     1028546 :   running--;
     389             : // notice: with exception safety the following might be converted to a plain error.
     390             : // I leave it like this for now:
     391     1028546 :   if(running!=0) return *this;
     392     2056990 :   auto t=std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now()-lastStart);
     393     1028495 :   lap+=t.count();
     394     1028495 :   return *this;
     395             : }
     396             : 
     397             : inline
     398             : Stopwatch::Handler Stopwatch::Watch::startStop() {
     399             :   return Handler( this,true );
     400             : }
     401             : 
     402             : inline
     403             : Stopwatch::Handler Stopwatch::Watch::startPause() {
     404             :   return Handler( this,false );
     405             : }
     406             : 
     407             : 
     408             : }
     409             : 
     410             : 
     411             : #endif

Generated by: LCOV version 1.14