Program Listing for File timer.h

Return to documentation for file (include/apriltag_mit/AprilTags/timer.h)

#ifndef APRILTAGS_TIMER_H_
#define APRILTAGS_TIMER_H_

#include <chrono>
#include <string>
#include <cassert>
#include <ostream>
#include <iomanip>
#include <iostream>
#include <thread>

#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics.hpp>

namespace AprilTags {

// Useful typedefs
using sec = std::chrono::seconds;
using ms = std::chrono::milliseconds;
using us = std::chrono::microseconds;
using ns = std::chrono::nanoseconds;

// SIFANE!
template <typename>
struct is_duration : std::false_type {};

template <typename T, typename U>
struct is_duration<std::chrono::duration<T, U>> : std::true_type {};

// Units
template <typename T>
std::string durationUnit() {
  return "unknown unit";
}

#define TIMER_UNIT(time)             \
  template <>                        \
  std::string durationUnit<time>() { \
    return #time;                    \
  }

TIMER_UNIT(sec)
TIMER_UNIT(ms)
TIMER_UNIT(us)
TIMER_UNIT(ns)

#undef TIMER_UNIT

template <typename NumT, typename DenT>
double ratio() {
  typedef typename NumT::period NumPeriod;
  typedef typename DenT::period DenPeriod;
  typedef typename std::ratio_divide<NumPeriod, DenPeriod>::type RatioType;
  return static_cast<double>(RatioType::num) / RatioType::den;
}

namespace bac = boost::accumulators;

template <typename DurationT,
          typename ClockT = std::chrono::high_resolution_clock>
class Timer {
  static_assert(is_duration<DurationT>::value, "Not a valid duration type");

 public:
  explicit Timer(const std::string& name, bool start_now = true) : name_(name) {
    if (start_now) start();
  }

  const std::string& name() const { return name_; }

  void start() {
    assert(!timing_);
    timing_ = true;
    start_ = ClockT::now();
  }

  void stop() {
    elapsed_ = std::chrono::duration_cast<DurationT>(ClockT::now() - start_);
    assert(timing_);
    timing_ = false;
    acc_(elapsed());  // Update accumulator
  }

  void reset() {
    timing_ = false;
    elapsed_ = DurationT(0);
    acc_ = bac::accumulator_set<double, AccumulatorFeatures>();
  }

  template <typename T = DurationT>
  void sleep(int tick) {
    std::this_thread::sleep_for(T(tick));
  }

  template <typename T = DurationT>
  double elapsed() const {
    return elapsed_.count() * ratio<DurationT, T>();
  }

  template <typename T = DurationT>
  std::string elapsedStr() const {
    return std::to_string(elapsed<T>()) + unitStr<T>();
  }

  template <typename T = DurationT>
  T elapsedDuration() const {
    return std::chrono::duration_cast<T>(elapsed_);
  }

  template <typename T = DurationT>
  std::string unitStr() const {
    return durationUnit<T>();
  }

  std::string baseUnitStr() const { return unitStr(); }

  template <typename T = DurationT>
  double mean() const {
    return bac::extract_result<bac::tag::mean>(acc_) * ratio<DurationT, T>();
  }

  template <typename T = DurationT>
  double max() const {
    return bac::extract_result<bac::tag::max>(acc_) * ratio<DurationT, T>();
  }

  template <typename T = DurationT>
  double min() const {
    return bac::extract_result<bac::tag::min>(acc_) * ratio<DurationT, T>();
  }

  template <typename T = DurationT>
  double sum() const {
    return bac::extract_result<bac::tag::sum>(acc_) * ratio<DurationT, T>();
  }

  size_t count() const { return bac::extract_result<bac::tag::count>(acc_); }

  template <typename T = DurationT>
  void report(std::ostream& os = std::cout) {
    os << "[" << name() << "] " << elapsedStr<T>() << std::endl;
  }

  template <typename T = DurationT>
  std::string statsStr() {
    return std::string("count: ") + std::to_string(count()) + ", mean: " +
           std::to_string(mean<T>()) + ", min: " + std::to_string(min<T>()) +
           ", max: " + std::to_string(max<T>()) + ", unit: " + unitStr<T>();
  }

  template <typename T = DurationT>
  void reportStats(std::ostream& os = std::cout) {
    os << "[" << name() << "] " << statsStr<T>() << std::endl;
  }

 private:
  using AccumulatorFeatures =
      bac::features<bac::tag::sum, bac::tag::min, bac::tag::max, bac::tag::mean,
                    bac::tag::count>;
  std::string name_{"timer"};
  bool timing_{false};
  DurationT elapsed_{0};
  typename ClockT::time_point start_;
  bac::accumulator_set<double, AccumulatorFeatures> acc_;
};

// More useful typedefs
using TimerSec = Timer<sec>;
using TimerMs = Timer<ms>;
using TimerUs = Timer<us>;
using TimerNs = Timer<ns>;

template <typename ClockType>
void printClockData() {
  std::cout << "- precision: ";
  // If time unit is less or equal one millisecond
  typedef typename ClockType::period RatioType;
  if (std::ratio_less_equal<RatioType, std::milli>::value) {
    // Convert to and print as milliseconds
    typedef typename std::ratio_multiply<RatioType, std::kilo>::type TT;
    std::cout << std::fixed << static_cast<double>(TT::num) / TT::den << " ms"
              << std::endl;
  } else {
    // Print as seconds
    std::cout << std::fixed
              << static_cast<double>(RatioType::num) / RatioType::den << " sec"
              << std::endl;
  }
  std::cout << "- is_steady: " << std::boolalpha << ClockType::is_steady
            << std::endl;
}

template <typename DurationT,
          typename ClockT = std::chrono::high_resolution_clock>
class ScopedTimer {
 public:
  ScopedTimer(const std::string& name) : timer_(name) {}
  ~ScopedTimer() {
    timer_.stop();
    timer_.report();
  }

 private:
  Timer<DurationT, ClockT> timer_;
};

using ScopedTimerSec = ScopedTimer<sec>;
using ScopedTimerMs = ScopedTimer<ms>;
using ScopedTimerUs = ScopedTimer<us>;
using ScopedTimerNs = ScopedTimer<ns>;

}  // namespace AprilTags

#endif