Tracking Rates in C++

Recently I have wanted to figure out how often a certain even happens in my research project, Alaska. For example, I want to know how many allocations have been made, and the rate (allocations per second) they are made at. Importantly, though, I want this tracking to occur in constant time and constant space (and ideally very quickly).

As an example of what I wanted to have, imagine we want to track how fast a memory allocator can operate:

RateCounter counter; // a counter for events

for (...) {
    malloc(...);
    counter++; // track the event
}

Then, you should be able to ask the counter for stats:

printf("%d allocations at %f per second\n",
          counter.read(),
          counter.digest());

read returns the number of events which have occurred, and digest returns the rate of those events. I also only care about the most recent rate, so digest resets the counting.

The code

Here's the code for that RateCounter. Notice there is the concept of a TimeCache, which allows you to re-use the time measurement because asking the kernel or whatever for the current time is pretty slow on some systems.

class TimeCache {
  uint64_t m_now;

 public:
  inline TimeCache(void) {
    // get the current time in nanoseconds
    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    m_now = (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec;
  }
  inline auto now(void) const { return m_now; }
};

class RateCounter {
  uint64_t last_nanoseconds = 0;
  uint64_t last_value = 0;
  uint64_t value = 0;
  float last_rate = 0.0;

 public:
  inline RateCounter() {
    value = 0;
    digest();
  }


  // Read the value, and return the rate (per second) since the last digest.
  // This function can take a time cache, which can be used to
  // record the exact same time for many RateCounter readings. This
  // is useful because reading time from the kernel could be expensive
  inline float digest(const TimeCache &tc) {
    auto now = tc.now();
    auto ns_passed = now - last_nanoseconds;
    float seconds_passed = ns_passed / 1000.0 / 1000.0 / 1000.0;
    uint64_t change = value - last_value;

    // Only update the rate every 100ms
    if (seconds_passed < 0.1) return last_rate;

    last_nanoseconds = now;
    last_value = value;
    last_rate = change / seconds_passed;

    return last_rate;
  }

  inline float digest(void) {
    alaska::TimeCache tc;
    return digest(tc);
  }


  // Track that an event happened
  inline void track(int incr = 1) { value += incr; }
  // Return the total count tracked by the counter
  inline uint64_t read(void) const { return value; }

  auto &operator++(int) {
    track(1);
    return *this;
  }
};

It probably has some problems w/ overflowing nanoseconds, but it suits my purpose :). Hope you find it useful!