LCOV - code coverage report
Current view: top level - include/mutable/util - Timer.hpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 73 74 98.6 %
Date: 2025-05-23 10:42:01 Functions: 33 35 94.3 %
Branches: 34 52 65.4 %

           Branch data     Line data    Source code
       1                 :            : #pragma once
       2                 :            : 
       3                 :            : #include <mutable/util/fn.hpp>
       4                 :            : #include <mutable/util/macro.hpp>
       5                 :            : #include <algorithm>
       6                 :            : #include <chrono>
       7                 :            : #include <ctime>
       8                 :            : #include <string>
       9                 :            : #include <utility>
      10                 :            : #include <vector>
      11                 :            : 
      12                 :            : 
      13                 :            : namespace m {
      14                 :            : 
      15                 :            : /** Collect timings of events. */
      16                 :            : struct Timer
      17                 :            : {
      18                 :            :     /** A proxy class to record a `Measurement` with a `Timer`. */
      19                 :            :     struct TimingProcess
      20                 :            :     {
      21                 :            :         friend struct Timer;
      22                 :            : 
      23                 :            :         private:
      24                 :            :         Timer &timer_; ///< the `Timer` instance
      25                 :            :         std::size_t id_; ///< the ID of the `Measurement`
      26                 :            : 
      27                 :       2682 :         TimingProcess(Timer &timer, std::size_t index) : timer_(timer), id_(index) { }
      28                 :            : 
      29                 :            :         public:
      30                 :       2682 :         ~TimingProcess() {
      31         [ +  - ]:       2682 :             auto &M = timer_.get(id_);
      32   [ +  -  +  - ]:       2682 :             if (not M.has_ended())
      33         [ +  - ]:       2682 :                 timer_.stop(id_);
      34                 :       2682 :         }
      35                 :            : 
      36                 :          0 :         void stop() { timer_.stop(id_); }
      37                 :            :     };
      38                 :            : 
      39                 :            :     using clock = std::chrono::high_resolution_clock;
      40                 :            :     using duration = clock::duration;
      41                 :            :     using time_point = clock::time_point;
      42                 :            : 
      43                 :            :     struct Measurement
      44                 :            :     {
      45                 :            :         std::string name; ///< the name of this `Measurement`
      46                 :            :         time_point begin, end; ///< the begin and end time points of this `Measurement`
      47                 :            : 
      48                 :         54 :         explicit Measurement(std::string name, time_point begin = time_point(), time_point end = time_point())
      49                 :         54 :             : name(std::move(name))
      50                 :         54 :             , begin(std::move(begin))
      51                 :         54 :             , end(std::move(end))
      52                 :         54 :         { }
      53                 :            : 
      54                 :            :         /** Clear this `Measurement`, rendering it unused. */
      55                 :          1 :         void clear() { begin = end = time_point(); }
      56                 :            : 
      57                 :            :         /** Start this `Measurement` by setting the start time point to *NOW*. */
      58                 :         53 :         void start() {
      59                 :         53 :             M_insist(is_unused());
      60                 :         53 :             begin = clock::now();
      61                 :         53 :         }
      62                 :            : 
      63                 :            :         /** Stop this `Measurement` by setting the end time point to *NOW*. */
      64                 :       2683 :         void stop() {
      65                 :       2683 :             M_insist(is_active());
      66                 :       2683 :             end = clock::now();
      67                 :       2683 :         }
      68                 :            : 
      69                 :            :         /** Returns `true` iff the `Measurement` is unused, i.e. has not started (and hence also not finished). */
      70                 :         55 :         bool is_unused() const {
      71         [ -  + ]:         55 :             M_insist(has_started() or not has_ended(),
      72                 :            :                    "if the measurement hasn't started it must not have ended");
      73                 :         55 :             return not has_started();
      74                 :            :         }
      75                 :            :         /** Returns `true` iff this `Measurement` has begun. */
      76                 :       5444 :         bool has_started() const { return begin != time_point(); }
      77                 :            :         /** Returns `true` iff this `Measurement` has ended. */
      78                 :      10751 :         bool has_ended() const { return end != time_point(); }
      79                 :            :         /** Returns `true` iff this `Measurement` is currently in process. */
      80         [ +  + ]:       5320 :         bool is_active() const { return has_started() and not has_ended(); }
      81                 :            :         /** Returns `true` iff this `Measurement` has completed. */
      82         [ +  + ]:         10 :         bool is_finished() const { return has_started() and has_ended(); }
      83                 :            : 
      84                 :            :         /** Returns the duration of a *finished* `Measurement`. */
      85                 :          6 :         duration duration() const {
      86                 :          6 :             M_insist(is_finished(), "can only compute duration of finished measurements");
      87                 :          6 :             return end - begin;
      88                 :            :         }
      89                 :            : 
      90                 :            :         friend std::ostream & operator<<(std::ostream &out, const Measurement &M);
      91                 :            :         void dump(std::ostream &out) const;
      92                 :            :         void dump() const;
      93                 :            :     };
      94                 :            : 
      95                 :            :     private:
      96                 :            :     std::vector<Measurement> measurements_;
      97                 :            : 
      98                 :            :     public:
      99                 :          1 :     auto begin() const { return measurements_.cbegin(); }
     100                 :          1 :     auto end()   const { return measurements_.cend(); }
     101                 :            :     auto cbegin() const { return measurements_.cbegin(); }
     102                 :            :     auto cend()   const { return measurements_.cend(); }
     103                 :            : 
     104                 :          1 :     const std::vector<Measurement> & measurements() const { return measurements_; }
     105                 :       2685 :     const Measurement & get(std::size_t i) const {
     106         [ +  + ]:       2685 :         if (i >= measurements_.size())
     107   [ +  -  +  -  :          2 :             throw m::out_of_range("index i out of bounds");
             -  +  +  - ]
     108                 :       2683 :         return measurements_[i];
     109                 :          2 :     }
     110                 :          3 :     const Measurement & get(const std::string &name) const {
     111                 :          6 :         auto it = std::find_if(measurements_.begin(), measurements_.end(),
     112                 :          6 :                                [&](auto &elem) { return elem.name == name; });
     113         [ +  + ]:          3 :         if (it == measurements_.end())
     114   [ +  -  +  -  :          2 :             throw m::out_of_range("a measurement with that name does not exist");
             -  +  +  - ]
     115                 :          1 :         return *it;
     116                 :          2 :     }
     117                 :            : 
     118                 :            :     /** Erase all `Measurement`s from this `Timer`. */
     119                 :          1 :     void clear() { measurements_.clear(); }
     120                 :            : 
     121                 :            :     private:
     122                 :            :     /** Start a new `Measurement` with the name `name`.  Returns the ID assigned to that `Measurement`. */
     123                 :       2683 :     std::size_t start(std::string name) {
     124                 :       5366 :         auto it = std::find_if(measurements_.begin(), measurements_.end(),
     125                 :       8449 :                                [&](auto &elem) { return elem.name == name; });
     126                 :            : 
     127         [ +  + ]:       2683 :         if (it != measurements_.end()) { // overwrite existing, finished measurement
     128         [ +  + ]:       2631 :             if (it->is_active())
     129   [ +  -  +  -  :          1 :                 throw m::invalid_argument("a measurement with that name is already in progress");
             -  +  +  - ]
     130                 :       2630 :             const auto idx = std::distance(measurements_.begin(), it);
     131                 :       2630 :             it->end = time_point();
     132                 :       2630 :             it->begin = clock::now();
     133                 :       2630 :             return idx;
     134                 :            :         } else { // create new measurement
     135                 :         52 :             auto id = measurements_.size();
     136                 :         52 :             auto &M = measurements_.emplace_back(name);
     137                 :         52 :             M.start();
     138                 :         52 :             return id;
     139                 :            :         }
     140                 :       2683 :     }
     141                 :            : 
     142                 :            :     /** Stops the `Measurement` with the given ID. */
     143                 :       2682 :     void stop(std::size_t id) {
     144                 :       2682 :         M_insist(id < measurements_.size(), "id out of bounds");
     145                 :       2682 :         auto &M = measurements_[id];
     146                 :       2682 :         M_insist(not M.has_ended(), "cannot stop that measurement because it has already been stopped");
     147                 :       2682 :         M.stop();
     148                 :       2682 :     }
     149                 :            : 
     150                 :            :     public:
     151                 :            :     /** Creates a new `TimingProcess` with the given `name`. */
     152   [ +  +  +  - ]:       2683 :     TimingProcess create_timing(std::string name) { return TimingProcess(*this, /* ID= */ start(name)); }
     153                 :            : 
     154                 :            :     M_LCOV_EXCL_START
     155                 :            :     /** Print all finished and in-process timings of `timer` to `out`. */
     156                 :            :     friend std::ostream & operator<<(std::ostream &out, const Timer &timer) {
     157                 :            :         out << "Timer measurements:\n";
     158                 :            :         for (auto &M : timer)
     159                 :            :             out << "  " << M << '\n';
     160                 :            : 
     161                 :            :         return out;
     162                 :            :     }
     163                 :            : 
     164                 :            :     void dump(std::ostream &out) const;
     165                 :            :     void dump() const;
     166                 :            :     M_LCOV_EXCL_STOP
     167                 :            : 
     168                 :            :     /** Computes the total duration of all `Measurement`s. */
     169                 :          1 :     duration total() const {
     170                 :          1 :         duration d(0);
     171         [ +  + ]:          3 :         for (auto &m : measurements_)
     172                 :          2 :             d += m.duration();
     173                 :          1 :         return d;
     174                 :            :     }
     175                 :            : };
     176                 :            : 
     177                 :            : #define M_TIME_EXPR(EXPR, DESCR, TIMER) \
     178                 :            :     [&]() { auto TP = (TIMER).create_timing((DESCR)); return (EXPR); }();
     179                 :            : #define M_TIME_BLOCK(DESCR, TIMER, BLOCK) {\
     180                 :            :     auto TP = (TIMER).create_timing((DESCR)); \
     181                 :            :     BLOCK \
     182                 :            : }
     183                 :            : #define M_TIME_THIS(DESCR, TIMER) \
     184                 :            :     auto M_PASTE(__timer_, __COUNTER__) = (TIMER).create_timing((DESCR));
     185                 :            : 
     186                 :            : }

Generated by: LCOV version 1.16