Skip to content

Instantly share code, notes, and snippets.

@flisboac
Last active September 27, 2016 11:12
Show Gist options
  • Save flisboac/480258a52c28af37a92a444828314542 to your computer and use it in GitHub Desktop.
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.
#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