alaestor

[FGL Utility] Stopwatch.h

May 22nd, 2021 (edited)
149
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 4.32 KB | None | 0 0
  1. // This commit: c8109c08c4c3e8d458de48b38b22be0cf2082f8b
  2. // Discord Honshitsu#9218
  3.  
  4. #pragma once
  5. #ifndef STOPWATCH_H_INCLUDED
  6. #define STOPWATCH_H_INCLUDED
  7.  
  8. #include <chrono>
  9. #include <vector>
  10. #include <stdexcept>
  11. #include <string_view>
  12. #include <sstream>
  13. #include <ostream>
  14.  
  15. /*
  16.     This utility conforms to the Council of Ricks standard.
  17.     As well as the usual Future Gadget Laboratory standard.
  18.  
  19.     This utility is not TARDIS or DeLorean safe;
  20.     start must be called before stop.
  21.  
  22.     Writen in worldline Divergence 1.048596 by Alaestor.
  23.     Discord Honshitsu#9218
  24. */
  25.  
  26. namespace stopwatch {
  27. class Stopwatch
  28. {
  29.     typedef std::chrono::steady_clock clock;
  30.  
  31. protected:
  32.     const std::string m_name;
  33.     const std::chrono::time_point<clock> m_unset{};
  34.     std::vector<std::chrono::time_point<clock>> m_laps;
  35.     std::chrono::time_point<clock>  m_start, m_stop;
  36.    
  37.     [[nodiscard]]
  38.     static std::string formatted(std::chrono::nanoseconds elapsed)
  39.     {
  40.         std::ostringstream os;
  41.         using // using namespace std::chrono doesn't include nanoseconds?
  42.             std::chrono::duration_cast,
  43.             std::chrono::nanoseconds,
  44.             std::chrono::microseconds,
  45.             std::chrono::milliseconds,
  46.             std::chrono::seconds,
  47.             std::chrono::minutes,
  48.             std::chrono::hours;
  49.        
  50.         auto remaining{ elapsed };
  51.        
  52.         auto process{
  53.             [&remaining, &os]<typename T>(T time, std::string_view abbrev)
  54.             {
  55.                 remaining -= time;
  56.                 if (const auto& t{ time.count() }; t > 0)
  57.                     os << t << abbrev << " ";
  58.             }
  59.         };
  60.        
  61.         process(duration_cast<hours>(remaining), "h");
  62.         process(duration_cast<minutes>(remaining), "m");
  63.         process(duration_cast<seconds>(remaining), "s");
  64.         process(duration_cast<milliseconds>(remaining), "ms");
  65.         process(duration_cast<microseconds>(remaining), "us");
  66.         process(duration_cast<nanoseconds>(remaining), "ns");
  67.  
  68.         if (remaining.count() > 0)
  69.             os << "\n... wtf? Something probably broke.\n";
  70.        
  71.         return os.str();
  72.     }
  73.    
  74. public:
  75.     void start()
  76.     { m_start = clock::now(); }
  77.  
  78.     void stop()
  79.     { m_stop = clock::now(); }
  80.  
  81.     void reset()
  82.     { m_start = m_stop = m_unset; }
  83.    
  84.     [[nodiscard]]
  85.     std::string_view getName() const
  86.     { return m_name; }
  87.    
  88.     void lap()
  89.     {
  90.         if (m_start == m_unset)
  91.             throw std::runtime_error(m_name+" must start before you can lap!");
  92.        
  93.         m_laps.push_back(clock::now());
  94.     }
  95.    
  96.     void clearLaps()
  97.     { m_laps.clear(); }
  98.    
  99.     [[nodiscard]] std::size_t numberOfLaps() const noexcept
  100.     { return m_laps.size(); }
  101.    
  102.     [[nodiscard]] std::string getLap(const std::size_t number) const
  103.     {
  104.         if (m_laps.size() == 0)
  105.             throw std::runtime_error(m_name+" has no laps");
  106.        
  107.         if (number == 0)
  108.             throw std::invalid_argument(m_name+" getLap number must be > 0");
  109.        
  110.         const std::size_t i{ number-1 };
  111.        
  112.         if (i > m_laps.size())
  113.             throw std::invalid_argument(m_name+" getLap number out of range");
  114.        
  115.         const auto lap{ m_laps.at(i) };
  116.         const auto lastLap{ i > 1 ? m_laps.at(i-1) : m_start };
  117.         std::stringstream sstream;
  118.         sstream
  119.             << m_name << " lap " << number << ": "
  120.             << formatted(lap - lastLap);
  121.        
  122.         return sstream.str();
  123.     }
  124.    
  125.     [[nodiscard]] std::string previousLap() const
  126.     { return getLap(m_laps.size()); }
  127.  
  128.     [[nodiscard]] std::string allLaps() const
  129.     {  
  130.         std::stringstream sstream;
  131.        
  132.         auto lastLap{ m_start };
  133.         std::size_t counter{ 1 };
  134.         for (const auto& lap : m_laps)
  135.         {
  136.             sstream
  137.                 << m_name << " Lap " << counter << ": "
  138.                 << formatted(lap - lastLap) << "\n";
  139.             lastLap = lap;
  140.             ++counter;
  141.         }
  142.        
  143.         return sstream.str();
  144.     }
  145.    
  146.     [[nodiscard]] std::string averageLaps() const
  147.     {
  148.         if (m_laps.size() == 0)
  149.             throw std::runtime_error(m_name+" has no laps");
  150.        
  151.         std::chrono::nanoseconds accum{ std::chrono::nanoseconds::zero() };
  152.         auto lastLap{ m_start };
  153.         for (const auto& lap : m_laps)
  154.         {
  155.             accum += lap - lastLap;
  156.             lastLap = lap;
  157.         }
  158.        
  159.         const auto avg{ accum / m_laps.size() };
  160.        
  161.         return formatted(avg);
  162.     }
  163.    
  164.     [[nodiscard]]
  165.     friend std::ostream& operator<<(std::ostream& os, const Stopwatch& sw)
  166.     {
  167.         return os
  168.             << sw.m_name << ": "
  169.             << sw.formatted(sw.m_stop - sw.m_start);
  170.     }
  171.  
  172.     explicit Stopwatch(std::string_view name)
  173.     : m_name(name)
  174.     {
  175.         m_laps.reserve(10000);
  176.         static_assert(clock::is_steady,
  177.             "stopwatch chrono::steady_clock is not steady; not OS supported?");
  178.     }
  179.  
  180.     ~Stopwatch() = default;
  181. };
  182. }// namespace stopwatch
  183.  
  184. #endif // STOPWATCH_H_INCLUDED
Add Comment
Please, Sign In to add comment