Last active
September 27, 2016 11:12
-
-
Save flisboac/480258a52c28af37a92a444828314542 to your computer and use it in GitHub Desktop.
A pretty simple timer class (header-only) in C++11 that also calculates the average/minimum/maximum time of multiple measurements.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #ifndef TIMER_HPP_ | |
| #define TIMER_HPP_ | |
| #include <iosfwd> | |
| #include <type_traits> | |
| #include <chrono> | |
| class Timer { | |
| public: | |
| using Clock = std::conditional< | |
| std::chrono::high_resolution_clock::is_steady, | |
| std::chrono::high_resolution_clock, | |
| std::chrono::steady_clock | |
| >::type; | |
| using TimePoint = Clock::time_point; | |
| using Duration = Clock::duration; | |
| enum class EScale : char { | |
| Unscaled = '\0', | |
| Minute = 'M', | |
| Hour = 'h', | |
| Day ='d', | |
| Mili = 'm', | |
| Micro = 'u', | |
| Nano = 'n', | |
| Pico = 'p' | |
| }; | |
| template <EScale Value = EScale::Unscaled> | |
| struct Scale {}; | |
| template <typename T> | |
| struct Value { | |
| T value = 0; | |
| EScale scale = EScale::Unscaled; | |
| Value(T value_, EScale scale_ = EScale::Unscaled) : value(value_), scale(scale_) {} | |
| Value() = default; | |
| Value(const Value& rhs) = default; | |
| Value(Value&& rhs) = default; | |
| Value& operator=(const Value& rhs) = default; | |
| Value& operator=(Value&& rhs) = default; | |
| inline Value<T>& rescale() { | |
| if (scale == EScale::Unscaled) { | |
| T abs_value = value < 0 ? -value : value; | |
| if (abs_value > 1800) { | |
| value = value / 60; | |
| scale = EScale::Minute; | |
| abs_value = value < 0 ? -value : value; | |
| if (abs_value > 60) { | |
| value = value / 60; | |
| scale = EScale::Hour; | |
| abs_value = value < 0 ? -value : value; | |
| if (abs_value > 96) { | |
| value = value / 24; | |
| scale = EScale::Day; | |
| } | |
| } | |
| } else { | |
| while (abs_value < 1 && scale != EScale::Pico) { | |
| switch(scale) { | |
| case EScale::Unscaled: scale = EScale::Mili; break; | |
| case EScale::Mili: scale = EScale::Micro; break; | |
| case EScale::Micro: scale = EScale::Nano; break; | |
| case EScale::Nano: scale = EScale::Pico; break; | |
| } | |
| value = value * 1000; | |
| abs_value = value < 0 ? -value : value; | |
| } | |
| } | |
| } | |
| return *this; | |
| } | |
| }; | |
| template <typename Callable> | |
| inline Timer& execute(Callable fn) | |
| { start(); fn(); return stop(); } | |
| template <typename T, typename Callable> | |
| inline Timer& execute(T context, Callable fn) | |
| { start(); fn(context); return stop(); } | |
| inline void start() | |
| { _start = Clock::now(); } | |
| inline Timer& stop() { | |
| TimePoint stop = Clock::now(); | |
| Duration _diff = stop - _start; | |
| _count++; | |
| if (_count > 1) { | |
| _avg = (_avg * (_count - 1) + _diff) / _count; | |
| if (_diff > _max) { | |
| _max = _diff; | |
| _slowest = _count; | |
| } | |
| if (_diff < _min) { | |
| _min = _diff; | |
| _fastest = _count; | |
| } | |
| } else { | |
| _fastest = _slowest = _count; | |
| _min = _max = _avg = _diff; | |
| } | |
| } | |
| inline Timer& reset() { | |
| _count = _fastest = _slowest = 0; | |
| } | |
| inline long count() const | |
| { return _count; } | |
| inline bool empty() const | |
| { return _count <= 0; } | |
| template <typename V = double> | |
| inline V min() const | |
| { return !empty() ? std::chrono::duration<V>(_min).count() : _zero<V>(); } | |
| template <typename V = double> | |
| inline Value<V> min_value() const | |
| { return Timer::value(min<V>()); } | |
| template <typename V = double> | |
| inline V max() const | |
| { return !empty() ? std::chrono::duration<V>(_max).count() : _zero<V>(); } | |
| template <typename V = double> | |
| inline Value<V> max_value() const | |
| { return Timer::value(max<V>()); } | |
| template <typename V = double> | |
| inline V avg() const | |
| { return !empty() ? std::chrono::duration<V>(_avg).count() : _zero<V>(); } | |
| template <typename V = double> | |
| inline Value<V> avg_value() const | |
| { return Timer::value(avg<V>()); } | |
| inline bool is_fastest() const | |
| { return _count > 0 && _count == _fastest; } | |
| inline bool is_slowest() const | |
| { return _count > 0 && _count == _slowest; } | |
| inline bool operator<(const Timer& rhs) const | |
| { return _avg < rhs._avg; } | |
| template <typename V> | |
| static inline Value<V> value(V sec) { | |
| return Value<V>(sec).rescale(); | |
| } | |
| private: | |
| template <typename V = double> | |
| inline V _zero() const | |
| { return std::chrono::duration<V>().count(); } | |
| long _count = 0; | |
| long _fastest = 0; | |
| long _slowest = 0; | |
| TimePoint _start; | |
| Duration _min; | |
| Duration _max; | |
| Duration _avg; | |
| }; | |
| template <> struct Timer::Scale<Timer::EScale::Minute> { | |
| constexpr static const Timer::EScale id = Timer::EScale::Minute; | |
| constexpr static const char *const unit_name = "min"; | |
| constexpr static const char *const units_name = "mins"; | |
| }; | |
| template <> struct Timer::Scale<Timer::EScale::Hour> { | |
| constexpr static const Timer::EScale id = Timer::EScale::Hour; | |
| constexpr static const char *const unit_name = "hour"; | |
| constexpr static const char *const units_name = "hours"; | |
| }; | |
| template <> struct Timer::Scale<Timer::EScale::Day> { | |
| constexpr static const Timer::EScale id = Timer::EScale::Day; | |
| constexpr static const char *const unit_name = "day"; | |
| constexpr static const char *const units_name = "days"; | |
| }; | |
| template <> struct Timer::Scale<Timer::EScale::Mili> { | |
| constexpr static const Timer::EScale id = Timer::EScale::Mili; | |
| constexpr static const char *const unit_name = "msec"; | |
| constexpr static const char *const units_name = "msecs"; | |
| }; | |
| template <> struct Timer::Scale<Timer::EScale::Micro> { | |
| constexpr static const Timer::EScale id = Timer::EScale::Micro; | |
| constexpr static const char *const unit_name = "usec"; | |
| constexpr static const char *const units_name = "usec"; | |
| }; | |
| template <> struct Timer::Scale<Timer::EScale::Nano> { | |
| constexpr static const Timer::EScale id = Timer::EScale::Nano; | |
| constexpr static const char *const unit_name = "nsec"; | |
| constexpr static const char *const units_name = "nsecs"; | |
| }; | |
| template <> struct Timer::Scale<Timer::EScale::Pico> { | |
| constexpr static const Timer::EScale id = Timer::EScale::Pico; | |
| constexpr static const char *const unit_name = "psec"; | |
| constexpr static const char *const units_name = "psecs"; | |
| }; | |
| template <> struct Timer::Scale<Timer::EScale::Unscaled> { | |
| constexpr static const Timer::EScale id = Timer::EScale::Unscaled; | |
| constexpr static const char *const unit_name = "sec"; | |
| constexpr static const char *const units_name = "secs"; | |
| template <typename V> | |
| static const char *name(const Timer::Value<V>& value) { | |
| return name(value.scale, value.value); | |
| } | |
| template <typename V> | |
| static const char *name(Timer::EScale scale, V value = V()) { | |
| switch(scale) { | |
| case EScale::Unscaled: | |
| return value <= 1 && value >= -1 | |
| ? Scale<EScale::Unscaled>::unit_name | |
| : Scale<EScale::Unscaled>::units_name; | |
| case EScale::Minute: | |
| return value <= 1 && value >= -1 | |
| ? Scale<EScale::Minute>::unit_name | |
| : Scale<EScale::Minute>::units_name; | |
| case EScale::Hour: | |
| return value <= 1 && value >= -1 | |
| ? Scale<EScale::Hour>::unit_name | |
| : Scale<EScale::Hour>::units_name; | |
| case EScale::Day: | |
| return value <= 1 && value >= -1 | |
| ? Scale<EScale::Day>::unit_name | |
| : Scale<EScale::Day>::units_name; | |
| case EScale::Mili: | |
| return value <= 1 && value >= -1 | |
| ? Scale<EScale::Mili>::unit_name | |
| : Scale<EScale::Mili>::units_name; | |
| case EScale::Micro: | |
| return value <= 1 && value >= -1 | |
| ? Scale<EScale::Micro>::unit_name | |
| : Scale<EScale::Micro>::units_name; | |
| case EScale::Nano: | |
| return value <= 1 && value >= -1 | |
| ? Scale<EScale::Nano>::unit_name | |
| : Scale<EScale::Nano>::units_name; | |
| case EScale::Pico: | |
| return value <= 1 && value >= -1 | |
| ? Scale<EScale::Pico>::unit_name | |
| : Scale<EScale::Pico>::units_name; | |
| } | |
| } | |
| }; | |
| template <> | |
| inline Timer::Duration Timer::_zero<Timer::Duration>() const | |
| { return Timer::Duration(); } | |
| template <> | |
| inline Timer::Duration Timer::min<Timer::Duration>() const | |
| { return !empty() ? _min : _zero<Timer::Duration>(); } | |
| template <> | |
| inline Timer::Duration Timer::max<Timer::Duration>() const | |
| { return !empty() ? _max : _zero<Timer::Duration>(); } | |
| template <> | |
| inline Timer::Duration Timer::avg<Timer::Duration>() const | |
| { return !empty() ? _avg : _zero<Timer::Duration>(); } | |
| template <typename T> | |
| inline std::ostream& operator<<(std::ostream& os, const Timer::Value<T>& sec) { | |
| os << sec.value << " " << Timer::Scale<>::name(sec); | |
| return os; | |
| } | |
| #endif /* TIMER_HPP_ */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment