From 42ca5613a7c6e5d03523fb57f0ce718feb03dc26 Mon Sep 17 00:00:00 2001 From: Adam Simpkins Date: Fri, 16 Sep 2016 12:23:41 -0700 Subject: [PATCH] update stats APIs to use TimePoint vs Duration correctly Summary: Update the stats APIs to correcly distinguish between TimePoint and Duration types. This does leave addValue() and update() APIs in place that accept Duration values, for backwards compatibility. These should eventually be removed once all code has been converted to call the new APIs. Reviewed By: yfeldblum Differential Revision: D3808805 fbshipit-source-id: 36d6574ba4a09db7eb9f1a35e47addd3e07f8461 --- folly/detail/Stats.h | 16 +- folly/stats/BucketedTimeSeries-defs.h | 209 +++++++++--------- folly/stats/BucketedTimeSeries.h | 122 ++++++----- folly/stats/MultiLevelTimeSeries-defs.h | 20 +- folly/stats/MultiLevelTimeSeries.h | 91 ++++---- folly/stats/TimeseriesHistogram-defs.h | 32 +-- folly/stats/TimeseriesHistogram.h | 88 ++++---- folly/test/TimeseriesHistogramTest.cpp | 107 +++++----- folly/test/TimeseriesTest.cpp | 271 ++++++++++++++---------- 9 files changed, 540 insertions(+), 416 deletions(-) diff --git a/folly/detail/Stats.h b/folly/detail/Stats.h index 1620cb5d..d99ac008 100644 --- a/folly/detail/Stats.h +++ b/folly/detail/Stats.h @@ -53,11 +53,12 @@ avgHelper(ValueType sum, uint64_t count) { * Helper function to compute the rate per Interval, * given the specified count recorded over the elapsed time period. */ -template -ReturnType rateHelper(ReturnType count, TimeType elapsed) { - if (elapsed == TimeType(0)) { +template < + typename ReturnType = double, + typename Duration = std::chrono::seconds, + typename Interval = Duration> +ReturnType rateHelper(ReturnType count, Duration elapsed) { + if (elapsed == Duration(0)) { return 0; } @@ -68,8 +69,9 @@ ReturnType rateHelper(ReturnType count, TimeType elapsed) { // is less than the desired interval, which will incorrectly result in // an infinite rate. typedef std::chrono::duration< - ReturnType, std::ratio> NativeRate; + ReturnType, + std::ratio> + NativeRate; typedef std::chrono::duration< ReturnType, std::ratio> DesiredRate; diff --git a/folly/stats/BucketedTimeSeries-defs.h b/folly/stats/BucketedTimeSeries-defs.h index 8941503c..bd38f6d5 100644 --- a/folly/stats/BucketedTimeSeries-defs.h +++ b/folly/stats/BucketedTimeSeries-defs.h @@ -25,8 +25,8 @@ namespace folly { template BucketedTimeSeries::BucketedTimeSeries( size_t nBuckets, - TimeType maxDuration) - : firstTime_(1), latestTime_(0), duration_(maxDuration) { + Duration maxDuration) + : firstTime_(Duration(1)), latestTime_(), duration_(maxDuration) { // For tracking all-time data we only use total_, and don't need to bother // with buckets_ if (!isAllTime()) { @@ -43,13 +43,13 @@ BucketedTimeSeries::BucketedTimeSeries( } template -bool BucketedTimeSeries::addValue(TimeType now, const ValueType& val) { +bool BucketedTimeSeries::addValue(TimePoint now, const ValueType& val) { return addValueAggregated(now, val, 1); } template bool BucketedTimeSeries::addValue( - TimeType now, + TimePoint now, const ValueType& val, int64_t times) { return addValueAggregated(now, val * times, times); @@ -57,7 +57,7 @@ bool BucketedTimeSeries::addValue( template bool BucketedTimeSeries::addValueAggregated( - TimeType now, + TimePoint now, const ValueType& total, int64_t nsamples) { if (isAllTime()) { @@ -100,7 +100,7 @@ bool BucketedTimeSeries::addValueAggregated( } template -size_t BucketedTimeSeries::update(TimeType now) { +size_t BucketedTimeSeries::update(TimePoint now) { if (empty()) { // This is the first data point. firstTime_ = now; @@ -123,7 +123,7 @@ size_t BucketedTimeSeries::update(TimeType now) { } template -size_t BucketedTimeSeries::updateBuckets(TimeType now) { +size_t BucketedTimeSeries::updateBuckets(TimePoint now) { // We could cache nextBucketStart as a member variable, so we don't have to // recompute it each time update() is called with a new timestamp value. // This makes things faster when update() (or addValue()) is called once @@ -134,8 +134,8 @@ size_t BucketedTimeSeries::updateBuckets(TimeType now) { // Get info about the bucket that latestTime_ points at size_t currentBucket; - TimeType currentBucketStart; - TimeType nextBucketStart; + TimePoint currentBucketStart; + TimePoint nextBucketStart; getBucketInfo(latestTime_, ¤tBucket, ¤tBucketStart, &nextBucketStart); @@ -181,21 +181,21 @@ void BucketedTimeSeries::clear() { total_.clear(); // Set firstTime_ larger than latestTime_, // to indicate that the timeseries is empty - firstTime_ = TimeType(1); - latestTime_ = TimeType(0); + firstTime_ = TimePoint(Duration(1)); + latestTime_ = TimePoint(); } template -typename CT::duration BucketedTimeSeries::getEarliestTime() const { +typename CT::time_point BucketedTimeSeries::getEarliestTime() const { if (empty()) { - return TimeType(0); + return TimePoint(); } if (isAllTime()) { return firstTime_; } // Compute the earliest time we can track - TimeType earliestTime = getEarliestTimeNonEmpty(); + TimePoint earliestTime = getEarliestTimeNonEmpty(); // We're never tracking data before firstTime_ earliestTime = std::max(earliestTime, firstTime_); @@ -204,11 +204,11 @@ typename CT::duration BucketedTimeSeries::getEarliestTime() const { } template -typename CT::duration BucketedTimeSeries::getEarliestTimeNonEmpty() +typename CT::time_point BucketedTimeSeries::getEarliestTimeNonEmpty() const { size_t currentBucket; - TimeType currentBucketStart; - TimeType nextBucketStart; + TimePoint currentBucketStart; + TimePoint nextBucketStart; getBucketInfo(latestTime_, ¤tBucket, ¤tBucketStart, &nextBucketStart); @@ -220,68 +220,79 @@ typename CT::duration BucketedTimeSeries::getEarliestTimeNonEmpty() template typename CT::duration BucketedTimeSeries::elapsed() const { if (empty()) { - return TimeType(0); + return Duration(0); } // Add 1 since [latestTime_, earliestTime] is an inclusive interval. - return latestTime_ - getEarliestTime() + TimeType(1); + return latestTime_ - getEarliestTime() + Duration(1); } template typename CT::duration BucketedTimeSeries::elapsed( - TimeType start, - TimeType end) const { + TimePoint start, + TimePoint end) const { if (empty()) { - return TimeType(0); + return Duration(0); } start = std::max(start, getEarliestTime()); - end = std::min(end, latestTime_ + TimeType(1)); + end = std::min(end, latestTime_ + Duration(1)); end = std::max(start, end); return end - start; } template -VT BucketedTimeSeries::sum(TimeType start, TimeType end) const { +VT BucketedTimeSeries::sum(TimePoint start, TimePoint end) const { ValueType total = ValueType(); - forEachBucket(start, end, [&](const Bucket& bucket, - TimeType bucketStart, - TimeType nextBucketStart) -> bool { - total += this->rangeAdjust(bucketStart, nextBucketStart, start, end, - bucket.sum); - return true; - }); + forEachBucket( + start, + end, + [&](const Bucket& bucket, + TimePoint bucketStart, + TimePoint nextBucketStart) -> bool { + total += this->rangeAdjust( + bucketStart, nextBucketStart, start, end, bucket.sum); + return true; + }); return total; } template -uint64_t BucketedTimeSeries::count(TimeType start, TimeType end) const { +uint64_t BucketedTimeSeries::count(TimePoint start, TimePoint end) + const { uint64_t sample_count = 0; - forEachBucket(start, end, [&](const Bucket& bucket, - TimeType bucketStart, - TimeType nextBucketStart) -> bool { - sample_count += this->rangeAdjust(bucketStart, nextBucketStart, start, end, - bucket.count); - return true; - }); + forEachBucket( + start, + end, + [&](const Bucket& bucket, + TimePoint bucketStart, + TimePoint nextBucketStart) -> bool { + sample_count += this->rangeAdjust( + bucketStart, nextBucketStart, start, end, bucket.count); + return true; + }); return sample_count; } template template -ReturnType BucketedTimeSeries::avg(TimeType start, TimeType end) const { +ReturnType BucketedTimeSeries::avg(TimePoint start, TimePoint end) + const { ValueType total = ValueType(); uint64_t sample_count = 0; - forEachBucket(start, end, [&](const Bucket& bucket, - TimeType bucketStart, - TimeType nextBucketStart) -> bool { - total += this->rangeAdjust(bucketStart, nextBucketStart, start, end, - bucket.sum); - sample_count += this->rangeAdjust(bucketStart, nextBucketStart, start, end, - bucket.count); - return true; - }); + forEachBucket( + start, + end, + [&](const Bucket& bucket, + TimePoint bucketStart, + TimePoint nextBucketStart) -> bool { + total += this->rangeAdjust( + bucketStart, nextBucketStart, start, end, bucket.sum); + sample_count += this->rangeAdjust( + bucketStart, nextBucketStart, start, end, bucket.count); + return true; + }); if (sample_count == 0) { return ReturnType(0); @@ -308,12 +319,12 @@ ReturnType BucketedTimeSeries::avg(TimeType start, TimeType end) const { */ template -size_t BucketedTimeSeries::getBucketIdx(TimeType time) const { +size_t BucketedTimeSeries::getBucketIdx(TimePoint time) const { // For all-time data we don't use buckets_. Everything is tracked in total_. DCHECK(!isAllTime()); - time %= duration_; - return time.count() * buckets_.size() / duration_.count(); + auto timeIntoCurrentCycle = (time.time_since_epoch() % duration_); + return timeIntoCurrentCycle.count() * buckets_.size() / duration_.count(); } /* @@ -322,17 +333,17 @@ size_t BucketedTimeSeries::getBucketIdx(TimeType time) const { */ template void BucketedTimeSeries::getBucketInfo( - TimeType time, + TimePoint time, size_t* bucketIdx, - TimeType* bucketStart, - TimeType* nextBucketStart) const { - typedef typename TimeType::rep TimeInt; + TimePoint* bucketStart, + TimePoint* nextBucketStart) const { + typedef typename Duration::rep TimeInt; DCHECK(!isAllTime()); // Keep these two lines together. The compiler should be able to compute // both the division and modulus with a single operation. - TimeType timeMod = time % duration_; - TimeInt numFullDurations = time / duration_; + Duration timeMod = time.time_since_epoch() % duration_; + TimeInt numFullDurations = time.time_since_epoch() / duration_; TimeInt scaledTime = timeMod.count() * buckets_.size(); @@ -344,12 +355,12 @@ void BucketedTimeSeries::getBucketInfo( TimeInt scaledBucketStart = scaledTime - scaledOffsetInBucket; TimeInt scaledNextBucketStart = scaledBucketStart + duration_.count(); - TimeType bucketStartMod((scaledBucketStart + buckets_.size() - 1) / - buckets_.size()); - TimeType nextBucketStartMod((scaledNextBucketStart + buckets_.size() - 1) / - buckets_.size()); + Duration bucketStartMod( + (scaledBucketStart + buckets_.size() - 1) / buckets_.size()); + Duration nextBucketStartMod( + (scaledNextBucketStart + buckets_.size() - 1) / buckets_.size()); - TimeType durationStart(numFullDurations * duration_.count()); + TimePoint durationStart(numFullDurations * duration_); *bucketStart = bucketStartMod + durationStart; *nextBucketStart = nextBucketStartMod + durationStart; } @@ -358,17 +369,17 @@ template template void BucketedTimeSeries::forEachBucket(Function fn) const { if (isAllTime()) { - fn(total_, firstTime_, latestTime_ + TimeType(1)); + fn(total_, firstTime_, latestTime_ + Duration(1)); return; } - typedef typename TimeType::rep TimeInt; + typedef typename Duration::rep TimeInt; // Compute durationStart, latestBucketIdx, and scaledNextBucketStart, // the same way as in getBucketInfo(). - TimeType timeMod = latestTime_ % duration_; - TimeInt numFullDurations = latestTime_ / duration_; - TimeType durationStart(numFullDurations * duration_.count()); + Duration timeMod = latestTime_.time_since_epoch() % duration_; + TimeInt numFullDurations = latestTime_.time_since_epoch() / duration_; + TimePoint durationStart(numFullDurations * duration_); TimeInt scaledTime = timeMod.count() * buckets_.size(); size_t latestBucketIdx = scaledTime / duration_.count(); TimeInt scaledOffsetInBucket = scaledTime % duration_.count(); @@ -381,9 +392,10 @@ void BucketedTimeSeries::forEachBucket(Function fn) const { size_t idx = latestBucketIdx; durationStart -= duration_; - TimeType nextBucketStart = - TimeType((scaledNextBucketStart + buckets_.size() - 1) / buckets_.size()) + - durationStart; + TimePoint nextBucketStart = + Duration( + (scaledNextBucketStart + buckets_.size() - 1) / buckets_.size()) + + durationStart; while (true) { ++idx; if (idx >= buckets_.size()) { @@ -394,15 +406,19 @@ void BucketedTimeSeries::forEachBucket(Function fn) const { scaledNextBucketStart += duration_.count(); } - TimeType bucketStart = nextBucketStart; - nextBucketStart = TimeType((scaledNextBucketStart + buckets_.size() - 1) / - buckets_.size()) + durationStart; + TimePoint bucketStart = nextBucketStart; + nextBucketStart = + Duration( + (scaledNextBucketStart + buckets_.size() - 1) / buckets_.size()) + + durationStart; // Should we bother skipping buckets where firstTime_ >= nextBucketStart? // For now we go ahead and invoke the function with these buckets. // sum and count should always be 0 in these buckets. - DCHECK_LE(bucketStart.count(), latestTime_.count()); + DCHECK_LE( + bucketStart.time_since_epoch().count(), + latestTime_.time_since_epoch().count()); bool ret = fn(buckets_[idx], bucketStart, nextBucketStart); if (!ret) { break; @@ -424,10 +440,10 @@ void BucketedTimeSeries::forEachBucket(Function fn) const { */ template VT BucketedTimeSeries::rangeAdjust( - TimeType bucketStart, - TimeType nextBucketStart, - TimeType start, - TimeType end, + TimePoint bucketStart, + TimePoint nextBucketStart, + TimePoint start, + TimePoint end, ValueType input) const { // If nextBucketStart is greater than latestTime_, treat nextBucketStart as // if it were latestTime_. This makes us more accurate when someone is @@ -436,7 +452,7 @@ VT BucketedTimeSeries::rangeAdjust( // downwards in this case, because the bucket really only has data up to // latestTime_. if (bucketStart <= latestTime_ && nextBucketStart > latestTime_) { - nextBucketStart = latestTime_ + TimeType(1); + nextBucketStart = latestTime_ + Duration(1); } if (start <= bucketStart && end >= nextBucketStart) { @@ -444,8 +460,8 @@ VT BucketedTimeSeries::rangeAdjust( return input; } - TimeType intervalStart = std::max(start, bucketStart); - TimeType intervalEnd = std::min(end, nextBucketStart); + TimePoint intervalStart = std::max(start, bucketStart); + TimePoint intervalEnd = std::min(end, nextBucketStart); return input * (intervalEnd - intervalStart) / (nextBucketStart - bucketStart); } @@ -453,20 +469,23 @@ VT BucketedTimeSeries::rangeAdjust( template template void BucketedTimeSeries::forEachBucket( - TimeType start, - TimeType end, + TimePoint start, + TimePoint end, Function fn) const { - forEachBucket([&start, &end, &fn] (const Bucket& bucket, TimeType bucketStart, - TimeType nextBucketStart) -> bool { - if (start >= nextBucketStart) { - return true; - } - if (end <= bucketStart) { - return false; - } - bool ret = fn(bucket, bucketStart, nextBucketStart); - return ret; - }); + forEachBucket( + [&start, &end, &fn]( + const Bucket& bucket, + TimePoint bucketStart, + TimePoint nextBucketStart) -> bool { + if (start >= nextBucketStart) { + return true; + } + if (end <= bucketStart) { + return false; + } + bool ret = fn(bucket, bucketStart, nextBucketStart); + return ret; + }); } } // folly diff --git a/folly/stats/BucketedTimeSeries.h b/folly/stats/BucketedTimeSeries.h index c64053a8..3680acb2 100644 --- a/folly/stats/BucketedTimeSeries.h +++ b/folly/stats/BucketedTimeSeries.h @@ -67,10 +67,6 @@ class BucketedTimeSeries { using Clock = CT; using Duration = typename Clock::duration; using TimePoint = typename Clock::time_point; - // The legacy TimeType. The older code used this instead of Duration and - // TimePoint. This will eventually be removed as the code is transitioned to - // Duration and TimePoint. - using TimeType = typename Clock::duration; using Bucket = detail::Bucket; /* @@ -103,17 +99,18 @@ class BucketedTimeSeries { * Returns true on success, or false if now was older than the tracked time * window. */ - bool addValue(TimeType now, const ValueType& val); + bool addValue(TimePoint now, const ValueType& val); /* * Adds the value 'val' the given number of 'times' at time 'now' */ - bool addValue(TimeType now, const ValueType& val, int64_t times); + bool addValue(TimePoint now, const ValueType& val, int64_t times); /* - * Adds the value 'sum' as the sum of 'nsamples' samples + * Adds the value 'total' as the sum of 'nsamples' samples */ - bool addValueAggregated(TimeType now, const ValueType& sum, int64_t nsamples); + bool + addValueAggregated(TimePoint now, const ValueType& total, int64_t nsamples); /* * Updates the container to the specified time, doing all the necessary @@ -126,7 +123,7 @@ class BucketedTimeSeries { * * Returns the current bucket index after the update. */ - size_t update(TimeType now); + size_t update(TimePoint now); /* * Reset the timeseries to an empty state, @@ -139,7 +136,7 @@ class BucketedTimeSeries { * * If no data has ever been added to this timeseries, 0 will be returned. */ - TimeType getLatestTime() const { + TimePoint getLatestTime() const { return latestTime_; } @@ -157,7 +154,7 @@ class BucketedTimeSeries { * in the timeseries. This will never be older than (getLatestTime() - * duration()). */ - TimeType getEarliestTime() const; + TimePoint getEarliestTime() const; /* * Return the number of buckets. @@ -170,7 +167,7 @@ class BucketedTimeSeries { * Return the maximum duration of data that can be tracked by this * BucketedTimeSeries. */ - TimeType duration() const { + Duration duration() const { return duration_; } @@ -179,7 +176,7 @@ class BucketedTimeSeries { * ever rolling over into new buckets. */ bool isAllTime() const { - return (duration_ == TimeType(0)); + return (duration_ == Duration(0)); } /* @@ -218,7 +215,7 @@ class BucketedTimeSeries { * Note that you generally should call update() before calling elapsed(), to * make sure you are not reading stale data. */ - TimeType elapsed() const; + Duration elapsed() const; /* * Get the amount of time tracked by this timeseries, between the specified @@ -228,7 +225,7 @@ class BucketedTimeSeries { * simply returns (end - start). However, if start is earlier than * getEarliestTime(), this returns (end - getEarliestTime()). */ - TimeType elapsed(TimeType start, TimeType end) const; + Duration elapsed(TimePoint start, TimePoint end) const; /* * Return the sum of all the data points currently tracked by this @@ -272,7 +269,7 @@ class BucketedTimeSeries { * Note that you generally should call update() before calling rate(), to * make sure you are not reading stale data. */ - template + template ReturnType rate() const { return rateHelper(total_.sum, elapsed()); } @@ -289,7 +286,7 @@ class BucketedTimeSeries { * Note that you generally should call update() before calling countRate(), * to make sure you are not reading stale data. */ - template + template ReturnType countRate() const { return rateHelper(total_.count, elapsed()); } @@ -308,36 +305,36 @@ class BucketedTimeSeries { * * Note that the value returned is an estimate, and may not be precise. */ - ValueType sum(TimeType start, TimeType end) const; + ValueType sum(TimePoint start, TimePoint end) const; /* * Estimate the number of data points that occurred in the specified time * period. * - * The same caveats documented in the sum(TimeType start, TimeType end) + * The same caveats documented in the sum(TimePoint start, TimePoint end) * comments apply here as well. */ - uint64_t count(TimeType start, TimeType end) const; + uint64_t count(TimePoint start, TimePoint end) const; /* * Estimate the average value during the specified time period. * - * The same caveats documented in the sum(TimeType start, TimeType end) + * The same caveats documented in the sum(TimePoint start, TimePoint end) * comments apply here as well. */ - template - ReturnType avg(TimeType start, TimeType end) const; + template + ReturnType avg(TimePoint start, TimePoint end) const; /* * Estimate the rate during the specified time period. * - * The same caveats documented in the sum(TimeType start, TimeType end) + * The same caveats documented in the sum(TimePoint start, TimePoint end) * comments apply here as well. */ - template - ReturnType rate(TimeType start, TimeType end) const { + template + ReturnType rate(TimePoint start, TimePoint end) const { ValueType intervalSum = sum(start, end); - TimeType interval = elapsed(start, end); + Duration interval = elapsed(start, end); return rateHelper(intervalSum, interval); } @@ -345,13 +342,13 @@ class BucketedTimeSeries { * Estimate the rate of data points being added during the specified time * period. * - * The same caveats documented in the sum(TimeType start, TimeType end) + * The same caveats documented in the sum(TimePoint start, TimePoint end) * comments apply here as well. */ - template - ReturnType countRate(TimeType start, TimeType end) const { + template + ReturnType countRate(TimePoint start, TimePoint end) const { uint64_t intervalCount = count(start, end); - TimeType interval = elapsed(start, end); + Duration interval = elapsed(start, end); return rateHelper(intervalCount, interval); } @@ -365,8 +362,8 @@ class BucketedTimeSeries { * to break out of the loop and stop, without calling the function on any * more buckets. * - * bool function(const Bucket& bucket, TimeType bucketStart, - * TimeType nextBucketStart) + * bool function(const Bucket& bucket, TimePoint bucketStart, + * TimePoint nextBucketStart) */ template void forEachBucket(Function fn) const; @@ -381,7 +378,7 @@ class BucketedTimeSeries { * * This method may not be called for all-time data. */ - size_t getBucketIdx(TimeType time) const; + size_t getBucketIdx(TimePoint time) const; /* * Get the bucket at the specified index. @@ -398,29 +395,56 @@ class BucketedTimeSeries { * * This method may not be called for all-time data. */ - void getBucketInfo(TimeType time, size_t* bucketIdx, - TimeType* bucketStart, TimeType* nextBucketStart) const; + void getBucketInfo( + TimePoint time, + size_t* bucketIdx, + TimePoint* bucketStart, + TimePoint* nextBucketStart) const; + + /* + * Legacy APIs that accept a Duration parameters rather than TimePoint. + * + * These treat the Duration as relative to the clock epoch. + * Prefer using the correct TimePoint-based APIs instead. These APIs will + * eventually be deprecated and removed. + */ + bool addValue(Duration now, const ValueType& val) { + return addValueAggregated(TimePoint(now), val, 1); + } + bool addValue(Duration now, const ValueType& val, int64_t times) { + return addValueAggregated(TimePoint(now), val * times, times); + } + bool + addValueAggregated(Duration now, const ValueType& total, int64_t nsamples) { + return addValueAggregated(TimePoint(now), total, nsamples); + } + size_t update(Duration now) { + return update(TimePoint(now)); + } private: - template - ReturnType rateHelper(ReturnType numerator, TimeType elapsedTime) const { - return detail::rateHelper(numerator, - elapsedTime); + template + ReturnType rateHelper(ReturnType numerator, Duration elapsedTime) const { + return detail::rateHelper( + numerator, elapsedTime); } - TimeType getEarliestTimeNonEmpty() const; - size_t updateBuckets(TimeType now); + TimePoint getEarliestTimeNonEmpty() const; + size_t updateBuckets(TimePoint now); - ValueType rangeAdjust(TimeType bucketStart, TimeType nextBucketStart, - TimeType start, TimeType end, - ValueType input) const; + ValueType rangeAdjust( + TimePoint bucketStart, + TimePoint nextBucketStart, + TimePoint start, + TimePoint end, + ValueType input) const; template - void forEachBucket(TimeType start, TimeType end, Function fn) const; + void forEachBucket(TimePoint start, TimePoint end, Function fn) const; - TimeType firstTime_; // time of first update() since clear()/constructor - TimeType latestTime_; // time of last update() - TimeType duration_; // total duration ("window length") of the time series + TimePoint firstTime_; // time of first update() since clear()/constructor + TimePoint latestTime_; // time of last update() + Duration duration_; // total duration ("window length") of the time series Bucket total_; // sum and count of everything in time series std::vector buckets_; // actual buckets of values diff --git a/folly/stats/MultiLevelTimeSeries-defs.h b/folly/stats/MultiLevelTimeSeries-defs.h index c3ddba89..f20305a7 100644 --- a/folly/stats/MultiLevelTimeSeries-defs.h +++ b/folly/stats/MultiLevelTimeSeries-defs.h @@ -25,8 +25,8 @@ template MultiLevelTimeSeries::MultiLevelTimeSeries( size_t nBuckets, size_t nLevels, - const TimeType levelDurations[]) - : cachedTime_(0), cachedSum_(0), cachedCount_(0) { + const Duration levelDurations[]) + : cachedTime_(), cachedSum_(0), cachedCount_(0) { CHECK_GT(nLevels, 0); CHECK(levelDurations); @@ -44,13 +44,13 @@ MultiLevelTimeSeries::MultiLevelTimeSeries( template MultiLevelTimeSeries::MultiLevelTimeSeries( size_t nBuckets, - std::initializer_list durations) - : cachedTime_(0), cachedSum_(0), cachedCount_(0) { + std::initializer_list durations) + : cachedTime_(), cachedSum_(0), cachedCount_(0) { CHECK_GT(durations.size(), 0); levels_.reserve(durations.size()); int i = 0; - TimeType prev; + Duration prev; for (auto dur : durations) { if (dur == Duration(0)) { CHECK_EQ(i, durations.size() - 1); @@ -65,14 +65,14 @@ MultiLevelTimeSeries::MultiLevelTimeSeries( template void MultiLevelTimeSeries::addValue( - TimeType now, + TimePoint now, const ValueType& val) { addValueAggregated(now, val, 1); } template void MultiLevelTimeSeries::addValue( - TimeType now, + TimePoint now, const ValueType& val, int64_t times) { addValueAggregated(now, val * times, times); @@ -80,7 +80,7 @@ void MultiLevelTimeSeries::addValue( template void MultiLevelTimeSeries::addValueAggregated( - TimeType now, + TimePoint now, const ValueType& total, int64_t nsamples) { if (cachedTime_ != now) { @@ -92,7 +92,7 @@ void MultiLevelTimeSeries::addValueAggregated( } template -void MultiLevelTimeSeries::update(TimeType now) { +void MultiLevelTimeSeries::update(TimePoint now) { flush(); for (size_t i = 0; i < levels_.size(); ++i) { levels_[i].update(now); @@ -117,7 +117,7 @@ void MultiLevelTimeSeries::clear() { level.clear(); } - cachedTime_ = TimeType(0); + cachedTime_ = TimePoint(); cachedSum_ = 0; cachedCount_ = 0; } diff --git a/folly/stats/MultiLevelTimeSeries.h b/folly/stats/MultiLevelTimeSeries.h index 7a43f175..c8f0cdf2 100644 --- a/folly/stats/MultiLevelTimeSeries.h +++ b/folly/stats/MultiLevelTimeSeries.h @@ -56,10 +56,6 @@ class MultiLevelTimeSeries { using Clock = CT; using Duration = typename Clock::duration; using TimePoint = typename Clock::time_point; - // The legacy TimeType. The older code used this instead of Duration and - // TimePoint. This will eventually be removed as the code is transitioned to - // Duration and TimePoint. - using TimeType = typename Clock::duration; using Level = folly::BucketedTimeSeries; /* @@ -73,13 +69,14 @@ class MultiLevelTimeSeries { * be provided with a duration of '0' -- this will be an "all-time" level. If * an all-time level is provided, it MUST be the last level present. */ - MultiLevelTimeSeries(size_t numBuckets, - size_t numLevels, - const TimeType levelDurations[]); + MultiLevelTimeSeries( + size_t numBuckets, + size_t numLevels, + const Duration levelDurations[]); MultiLevelTimeSeries( size_t numBuckets, - std::initializer_list durations); + std::initializer_list durations); /* * Return the number of buckets used to track time series at each level. @@ -115,7 +112,7 @@ class MultiLevelTimeSeries { * data. Otherwise you may be reading stale data if update() or flush() has * not been called recently. */ - const Level& getLevel(TimeType start) const { + const Level& getLevel(TimePoint start) const { for (const auto& level : levels_) { if (level.isAllTime()) { return level; @@ -130,7 +127,7 @@ class MultiLevelTimeSeries { } // We should always have an all-time level, so this is never reached. LOG(FATAL) << "No level of timeseries covers internval" - << " from " << start.count() << " to now"; + << " from " << start.time_since_epoch().count() << " to now"; return levels_.back(); } @@ -141,7 +138,7 @@ class MultiLevelTimeSeries { * data. Otherwise you may be reading stale data if update() or flush() has * not been called recently. */ - const Level& getLevelByDuration(TimeType duration) const { + const Level& getLevelByDuration(Duration duration) const { // since the number of levels is expected to be small (less than 5 in most // cases), a simple linear scan would be efficient and is intentionally // chosen here over other alternatives for lookup. @@ -189,7 +186,7 @@ class MultiLevelTimeSeries { * data. Otherwise you may be reading stale data if update() or flush() has * not been called recently. */ - template + template ReturnType rate(int level) const { return getLevel(level).template rate(); } @@ -212,7 +209,7 @@ class MultiLevelTimeSeries { * data. Otherwise you may be reading stale data if update() or flush() has * not been called recently. */ - template + template ReturnType countRate(int level) const { return getLevel(level).template countRate(); } @@ -227,7 +224,7 @@ class MultiLevelTimeSeries { * data. Otherwise you may be reading stale data if update() or flush() has * not been called recently. */ - ValueType sum(TimeType duration) const { + ValueType sum(Duration duration) const { return getLevelByDuration(duration).sum(); } @@ -243,7 +240,7 @@ class MultiLevelTimeSeries { * not been called recently. */ template - ReturnType avg(TimeType duration) const { + ReturnType avg(Duration duration) const { return getLevelByDuration(duration).template avg(); } @@ -258,8 +255,8 @@ class MultiLevelTimeSeries { * data. Otherwise you may be reading stale data if update() or flush() has * not been called recently. */ - template - ReturnType rate(TimeType duration) const { + template + ReturnType rate(Duration duration) const { return getLevelByDuration(duration).template rate(); } @@ -273,7 +270,7 @@ class MultiLevelTimeSeries { * data. Otherwise you may be reading stale data if update() or flush() has * not been called recently. */ - int64_t count(TimeType duration) const { + int64_t count(Duration duration) const { return getLevelByDuration(duration).count(); } @@ -287,8 +284,8 @@ class MultiLevelTimeSeries { * data. Otherwise you may be reading stale data if update() or flush() has * not been called recently. */ - template - ReturnType countRate(TimeType duration) const { + template + ReturnType countRate(Duration duration) const { return getLevelByDuration(duration) .template countRate(); } @@ -311,51 +308,51 @@ class MultiLevelTimeSeries { * data. Otherwise you may be reading stale data if update() or flush() has * not been called recently. */ - ValueType sum(TimeType start, TimeType end) const { + ValueType sum(TimePoint start, TimePoint end) const { return getLevel(start).sum(start, end); } /* * Estimate the average value during the specified time period. * - * The same caveats documented in the sum(TimeType start, TimeType end) + * The same caveats documented in the sum(TimePoint start, TimePoint end) * comments apply here as well. * * Note: you should generally call update() or flush() before accessing the * data. Otherwise you may be reading stale data if update() or flush() has * not been called recently. */ - template - ReturnType avg(TimeType start, TimeType end) const { + template + ReturnType avg(TimePoint start, TimePoint end) const { return getLevel(start).template avg(start, end); } /* * Estimate the rate during the specified time period. * - * The same caveats documented in the sum(TimeType start, TimeType end) + * The same caveats documented in the sum(TimePoint start, TimePoint end) * comments apply here as well. * * Note: you should generally call update() or flush() before accessing the * data. Otherwise you may be reading stale data if update() or flush() has * not been called recently. */ - template - ReturnType rate(TimeType start, TimeType end) const { + template + ReturnType rate(TimePoint start, TimePoint end) const { return getLevel(start).template rate(start, end); } /* * Estimate the count during the specified time period. * - * The same caveats documented in the sum(TimeType start, TimeType end) + * The same caveats documented in the sum(TimePoint start, TimePoint end) * comments apply here as well. * * Note: you should generally call update() or flush() before accessing the * data. Otherwise you may be reading stale data if update() or flush() has * not been called recently. */ - int64_t count(TimeType start, TimeType end) const { + int64_t count(TimePoint start, TimePoint end) const { return getLevel(start).count(start, end); } @@ -372,18 +369,19 @@ class MultiLevelTimeSeries { * addValue() or update(), now will be ignored and the latest timestamp will * be used. */ - void addValue(TimeType now, const ValueType& val); + void addValue(TimePoint now, const ValueType& val); /* * Adds the value 'val' at time 'now' to all levels. */ - void addValue(TimeType now, const ValueType& val, int64_t times); + void addValue(TimePoint now, const ValueType& val, int64_t times); /* - * Adds the value 'val' at time 'now' to all levels as the sum of 'nsamples' - * samples. + * Adds the value 'total' at time 'now' to all levels as the sum of + * 'nsamples' samples. */ - void addValueAggregated(TimeType now, const ValueType& sum, int64_t nsamples); + void + addValueAggregated(TimePoint now, const ValueType& total, int64_t nsamples); /* * Update all the levels to the specified time, doing all the necessary @@ -393,7 +391,7 @@ class MultiLevelTimeSeries { * call update() before accessing the data. Otherwise you may be reading * stale data if update() has not been called recently. */ - void update(TimeType now); + void update(TimePoint now); /* * Reset all the timeseries to an empty state as if no data points have ever @@ -406,13 +404,34 @@ class MultiLevelTimeSeries { */ void flush(); + /* + * Legacy APIs that accept a Duration parameters rather than TimePoint. + * + * These treat the Duration as relative to the clock epoch. + * Prefer using the correct TimePoint-based APIs instead. These APIs will + * eventually be deprecated and removed. + */ + void update(Duration now) { + update(TimePoint(now)); + } + void addValue(Duration now, const ValueType& value) { + addValue(TimePoint(now), value); + } + void addValue(Duration now, const ValueType& value, int64_t times) { + addValue(TimePoint(now), value, times); + } + void + addValueAggregated(Duration now, const ValueType& total, int64_t nsamples) { + addValueAggregated(TimePoint(now), total, nsamples); + } + private: std::vector levels_; // Updates within the same time interval are cached // They are flushed out when updates from a different time comes, // or flush() is called. - TimeType cachedTime_; + TimePoint cachedTime_; ValueType cachedSum_; int cachedCount_; }; diff --git a/folly/stats/TimeseriesHistogram-defs.h b/folly/stats/TimeseriesHistogram-defs.h index 3594bec4..9375e5c4 100644 --- a/folly/stats/TimeseriesHistogram-defs.h +++ b/folly/stats/TimeseriesHistogram-defs.h @@ -35,7 +35,7 @@ TimeseriesHistogram::TimeseriesHistogram( template void TimeseriesHistogram::addValue( - TimeType now, + TimePoint now, const ValueType& value) { buckets_.getByValue(value).addValue(now, value); maybeHandleSingleUniqueValue(value); @@ -43,7 +43,7 @@ void TimeseriesHistogram::addValue( template void TimeseriesHistogram::addValue( - TimeType now, + TimePoint now, const ValueType& value, int64_t times) { buckets_.getByValue(value).addValue(now, value, times); @@ -52,7 +52,7 @@ void TimeseriesHistogram::addValue( template void TimeseriesHistogram::addValues( - TimeType now, + TimePoint now, const folly::Histogram& hist) { CHECK_EQ(hist.getMin(), getMin()); CHECK_EQ(hist.getMax(), getMax()); @@ -99,8 +99,8 @@ T TimeseriesHistogram::getPercentileEstimate(double pct, int level) template T TimeseriesHistogram::getPercentileEstimate( double pct, - TimeType start, - TimeType end) const { + TimePoint start, + TimePoint end) const { if (singleUniqueValue_) { return firstValue_; } @@ -119,8 +119,8 @@ int TimeseriesHistogram::getPercentileBucketIdx(double pct, int level) template int TimeseriesHistogram::getPercentileBucketIdx( double pct, - TimeType start, - TimeType end) const { + TimePoint start, + TimePoint end) const { return buckets_.getPercentileBucketIdx(pct / 100.0, CountFromInterval(start, end)); } @@ -133,7 +133,7 @@ void TimeseriesHistogram::clear() { } template -void TimeseriesHistogram::update(TimeType now) { +void TimeseriesHistogram::update(TimePoint now) { for (size_t i = 0; i < buckets_.getNumBuckets(); i++) { buckets_.getByIndex(i).update(now); } @@ -158,8 +158,8 @@ std::string TimeseriesHistogram::getString(int level) const { template std::string TimeseriesHistogram::getString( - TimeType start, - TimeType end) const { + TimePoint start, + TimePoint end) const { std::string result; for (size_t i = 0; i < buckets_.getNumBuckets(); i++) { @@ -191,8 +191,8 @@ template void TimeseriesHistogram::computeAvgData( ValueType* total, int64_t* nsamples, - TimeType start, - TimeType end) const { + TimePoint start, + TimePoint end) const { for (unsigned int b = 0; b < buckets_.getNumBuckets(); ++b) { const auto& levelObj = buckets_.getByIndex(b).getLevel(start); *total += levelObj.sum(start, end); @@ -203,7 +203,7 @@ void TimeseriesHistogram::computeAvgData( template void TimeseriesHistogram::computeRateData( ValueType* total, - TimeType* elapsed, + Duration* elapsed, int level) const { for (unsigned int b = 0; b < buckets_.getNumBuckets(); ++b) { const auto& levelObj = buckets_.getByIndex(b).getLevel(level); @@ -215,9 +215,9 @@ void TimeseriesHistogram::computeRateData( template void TimeseriesHistogram::computeRateData( ValueType* total, - TimeType* elapsed, - TimeType start, - TimeType end) const { + Duration* elapsed, + TimePoint start, + TimePoint end) const { for (unsigned int b = 0; b < buckets_.getNumBuckets(); ++b) { const auto& level = buckets_.getByIndex(b).getLevel(start); *total += level.sum(start, end); diff --git a/folly/stats/TimeseriesHistogram.h b/folly/stats/TimeseriesHistogram.h index 71471887..3ffa8703 100644 --- a/folly/stats/TimeseriesHistogram.h +++ b/folly/stats/TimeseriesHistogram.h @@ -65,10 +65,6 @@ class TimeseriesHistogram { using Clock = CT; using Duration = typename Clock::duration; using TimePoint = typename Clock::time_point; - // The legacy TimeType. The older code used this instead of Duration and - // TimePoint. This will eventually be removed as the code is transitioned to - // Duration and TimePoint. - using TimeType = typename Clock::duration; /* * Create a TimeSeries histogram and initialize the bucketing and levels. @@ -128,7 +124,7 @@ class TimeseriesHistogram { } /* Total count of values added during the given interval (all buckets). */ - int64_t count(TimeType start, TimeType end) const { + int64_t count(TimePoint start, TimePoint end) const { int64_t total = 0; for (unsigned int b = 0; b < buckets_.getNumBuckets(); ++b) { total += buckets_.getByIndex(b).count(start, end); @@ -146,7 +142,7 @@ class TimeseriesHistogram { } /* Total sum of values added during the given interval (all buckets). */ - ValueType sum(TimeType start, TimeType end) const { + ValueType sum(TimePoint start, TimePoint end) const { ValueType total = ValueType(); for (unsigned int b = 0; b < buckets_.getNumBuckets(); ++b) { total += buckets_.getByIndex(b).sum(start, end); @@ -165,7 +161,7 @@ class TimeseriesHistogram { /* Average of values added during the given interval (all buckets). */ template - ReturnType avg(TimeType start, TimeType end) const { + ReturnType avg(TimePoint start, TimePoint end) const { auto total = ValueType(); int64_t nsamples = 0; computeAvgData(&total, &nsamples, start, end); @@ -179,9 +175,9 @@ class TimeseriesHistogram { template ReturnType rate(int level) const { auto total = ValueType(); - TimeType elapsed(0); + Duration elapsed(0); computeRateData(&total, &elapsed, level); - return folly::detail::rateHelper( + return folly::detail::rateHelper( total, elapsed); } @@ -190,11 +186,11 @@ class TimeseriesHistogram { * This is the sum of all values divided by the time interval (in seconds). */ template - ReturnType rate(TimeType start, TimeType end) const { + ReturnType rate(TimePoint start, TimePoint end) const { auto total = ValueType(); - TimeType elapsed(0); + Duration elapsed(0); computeRateData(&total, &elapsed, start, end); - return folly::detail::rateHelper( + return folly::detail::rateHelper( total, elapsed); } @@ -203,15 +199,15 @@ class TimeseriesHistogram { * must call this directly before querying to ensure that the data in all * buckets is decayed properly. */ - void update(TimeType now); + void update(TimePoint now); /* clear all the data from the histogram. */ void clear(); /* Add a value into the histogram with timestamp 'now' */ - void addValue(TimeType now, const ValueType& value); + void addValue(TimePoint now, const ValueType& value); /* Add a value the given number of times with timestamp 'now' */ - void addValue(TimeType now, const ValueType& value, int64_t times); + void addValue(TimePoint now, const ValueType& value, int64_t times); /* * Add all of the values from the specified histogram. @@ -223,7 +219,7 @@ class TimeseriesHistogram { * Histogram that is updated frequently, and only add it to the global * TimeseriesHistogram once a second. */ - void addValues(TimeType now, const folly::Histogram& values); + void addValues(TimePoint now, const folly::Histogram& values); /* * Return an estimate of the value at the given percentile in the histogram @@ -252,8 +248,8 @@ class TimeseriesHistogram { * getPercentileEstimate(int pct, int level) for the explanation of the * estimation algorithm. */ - ValueType getPercentileEstimate(double pct, TimeType start, TimeType end) - const; + ValueType getPercentileEstimate(double pct, TimePoint start, TimePoint end) + const; /* * Return the bucket index that the given percentile falls into (in the @@ -266,14 +262,14 @@ class TimeseriesHistogram { * given historical interval). This index can then be used to retrieve either * the bucket threshold, or other data from inside the bucket. */ - int getPercentileBucketIdx(double pct, TimeType start, TimeType end) const; + int getPercentileBucketIdx(double pct, TimePoint start, TimePoint end) const; /* Get the bucket threshold for the bucket containing the given pct. */ int getPercentileBucketMin(double pct, int level) const { return getBucketMin(getPercentileBucketIdx(pct, level)); } /* Get the bucket threshold for the bucket containing the given pct. */ - int getPercentileBucketMin(double pct, TimeType start, TimeType end) const { + int getPercentileBucketMin(double pct, TimePoint start, TimePoint end) const { return getBucketMin(getPercentileBucketIdx(pct, start, end)); } @@ -288,7 +284,27 @@ class TimeseriesHistogram { * Print out serialized data for all buckets in the historical interval. * For format, please see getString(int level). */ - std::string getString(TimeType start, TimeType end) const; + std::string getString(TimePoint start, TimePoint end) const; + + /* + * Legacy APIs that accept a Duration parameters rather than TimePoint. + * + * These treat the Duration as relative to the clock epoch. + * Prefer using the correct TimePoint-based APIs instead. These APIs will + * eventually be deprecated and removed. + */ + void update(Duration now) { + update(TimePoint(now)); + } + void addValue(Duration now, const ValueType& value) { + addValue(TimePoint(now), value); + } + void addValue(Duration now, const ValueType& value, int64_t times) { + addValue(TimePoint(now), value, times); + } + void addValues(Duration now, const folly::Histogram& values) { + addValues(TimePoint(now), values); + } private: typedef ContainerType Bucket; @@ -303,17 +319,16 @@ class TimeseriesHistogram { int level_; }; struct CountFromInterval { - explicit CountFromInterval(TimeType start, TimeType end) - : start_(start), - end_(end) {} + explicit CountFromInterval(TimePoint start, TimePoint end) + : start_(start), end_(end) {} uint64_t operator()(const ContainerType& bucket) const { return bucket.count(start_, end_); } private: - TimeType start_; - TimeType end_; + TimePoint start_; + TimePoint end_; }; struct AvgFromLevel { @@ -329,17 +344,16 @@ class TimeseriesHistogram { template struct AvgFromInterval { - explicit AvgFromInterval(TimeType start, TimeType end) - : start_(start), - end_(end) {} + explicit AvgFromInterval(TimePoint start, TimePoint end) + : start_(start), end_(end) {} ReturnType operator()(const ContainerType& bucket) const { return bucket.template avg(start_, end_); } private: - TimeType start_; - TimeType end_; + TimePoint start_; + TimePoint end_; }; /* @@ -354,14 +368,14 @@ class TimeseriesHistogram { void computeAvgData( ValueType* total, int64_t* nsamples, - TimeType start, - TimeType end) const; - void computeRateData(ValueType* total, TimeType* elapsed, int level) const; + TimePoint start, + TimePoint end) const; + void computeRateData(ValueType* total, Duration* elapsed, int level) const; void computeRateData( ValueType* total, - TimeType* elapsed, - TimeType start, - TimeType end) const; + Duration* elapsed, + TimePoint start, + TimePoint end) const; folly::detail::HistogramBuckets buckets_; bool haveNotSeenValue_; diff --git a/folly/test/TimeseriesHistogramTest.cpp b/folly/test/TimeseriesHistogramTest.cpp index b672866f..cb6d134e 100644 --- a/folly/test/TimeseriesHistogramTest.cpp +++ b/folly/test/TimeseriesHistogramTest.cpp @@ -25,6 +25,7 @@ using namespace std; using namespace folly; using std::chrono::seconds; +namespace { namespace IntMTMHTS { enum Levels { MINUTE, @@ -54,6 +55,12 @@ namespace IntMHTS { typedef std::mt19937 RandomInt32; +using StatsClock = folly::LegacyStatsClock; +StatsClock::time_point mkTimePoint(int value) { + return StatsClock::time_point(StatsClock::duration(value)); +} +} + TEST(TimeseriesHistogram, Percentile) { RandomInt32 random(5); // [10, 109], 12 buckets including above and below @@ -75,14 +82,13 @@ TEST(TimeseriesHistogram, Percentile) { } int maxVal = 120; - h.addValue(seconds(0), 0); - h.addValue(seconds(0), maxVal); + h.addValue(mkTimePoint(0), 0); + h.addValue(mkTimePoint(0), maxVal); for (int i = 0; i < 98; i++) { - h.addValue(seconds(0), random() % maxVal); + h.addValue(mkTimePoint(0), random() % maxVal); } - h.update(std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch())); + h.update(mkTimePoint(1500000000)); // bucket 0 stores everything below min, so its minimum // is the lowest possible number EXPECT_EQ(std::numeric_limits::min(), @@ -106,13 +112,13 @@ TEST(TimeseriesHistogram, String) { IntMTMHTS::kDurations)); int maxVal = 120; - hist.addValue(seconds(0), 0); - hist.addValue(seconds(0), maxVal); + hist.addValue(mkTimePoint(0), 0); + hist.addValue(mkTimePoint(0), maxVal); for (int i = 0; i < 98; i++) { - hist.addValue(seconds(0), random() % maxVal); + hist.addValue(mkTimePoint(0), random() % maxVal); } - hist.update(seconds(0)); + hist.update(mkTimePoint(0)); const char* const kStringValues1[IntMTMHTS::NUM_LEVELS] = { "-2147483648:12:4,10:8:13,20:8:24,30:6:34,40:13:46,50:8:54,60:7:64," @@ -159,7 +165,7 @@ TEST(TimeseriesHistogram, Clear) { for (int now = 0; now < 3600; now++) { for (int i = 0; i < 100; i++) { - hist.addValue(seconds(now), i, 2); // adds each item 2 times + hist.addValue(mkTimePoint(now), i, 2); // adds each item 2 times } } @@ -197,11 +203,11 @@ TEST(TimeseriesHistogram, Basic) { for (int now = 0; now < 3600; now++) { for (int i = 0; i < 100; i++) { - hist.addValue(seconds(now), i); + hist.addValue(mkTimePoint(now), i); } } - hist.update(seconds(3599)); + hist.update(mkTimePoint(3599)); for (int pct = 1; pct <= 100; pct++) { int expected = (pct - 1) / 10 * 10; EXPECT_EQ(expected, hist.getPercentileBucketMin(pct, IntMTMHTS::MINUTE)); @@ -250,20 +256,20 @@ TEST(TimeseriesHistogram, Basic) { EXPECT_EQ(4950, hist.rate(IntMTMHTS::HOUR)); EXPECT_EQ(4950, hist.rate(IntMTMHTS::ALLTIME)); - EXPECT_EQ(1000, hist.count(seconds(10), seconds(20))); - EXPECT_EQ(49500, hist.sum(seconds(10), seconds(20))); - EXPECT_EQ(4950, hist.rate(seconds(10), seconds(20))); - EXPECT_EQ(49.5, hist.avg(seconds(10), seconds(20))); + EXPECT_EQ(1000, hist.count(mkTimePoint(10), mkTimePoint(20))); + EXPECT_EQ(49500, hist.sum(mkTimePoint(10), mkTimePoint(20))); + EXPECT_EQ(4950, hist.rate(mkTimePoint(10), mkTimePoint(20))); + EXPECT_EQ(49.5, hist.avg(mkTimePoint(10), mkTimePoint(20))); - EXPECT_EQ(200, hist.count(seconds(3550), seconds(3552))); - EXPECT_EQ(9900, hist.sum(seconds(3550), seconds(3552))); - EXPECT_EQ(4950, hist.rate(seconds(3550), seconds(3552))); - EXPECT_EQ(49.5, hist.avg(seconds(3550), seconds(3552))); + EXPECT_EQ(200, hist.count(mkTimePoint(3550), mkTimePoint(3552))); + EXPECT_EQ(9900, hist.sum(mkTimePoint(3550), mkTimePoint(3552))); + EXPECT_EQ(4950, hist.rate(mkTimePoint(3550), mkTimePoint(3552))); + EXPECT_EQ(49.5, hist.avg(mkTimePoint(3550), mkTimePoint(3552))); - EXPECT_EQ(0, hist.count(seconds(4550), seconds(4552))); - EXPECT_EQ(0, hist.sum(seconds(4550), seconds(4552))); - EXPECT_EQ(0, hist.rate(seconds(4550), seconds(4552))); - EXPECT_EQ(0, hist.avg(seconds(4550), seconds(4552))); + EXPECT_EQ(0, hist.count(mkTimePoint(4550), mkTimePoint(4552))); + EXPECT_EQ(0, hist.sum(mkTimePoint(4550), mkTimePoint(4552))); + EXPECT_EQ(0, hist.rate(mkTimePoint(4550), mkTimePoint(4552))); + EXPECT_EQ(0, hist.avg(mkTimePoint(4550), mkTimePoint(4552))); } // ----------------- @@ -276,11 +282,11 @@ TEST(TimeseriesHistogram, Basic) { for (int now = 0; now < 3600; now++) { for (int i = 0; i < 100; i++) { - hist.addValue(seconds(now), i, 2); // adds each item 2 times + hist.addValue(mkTimePoint(now), i, 2); // adds each item 2 times } } - hist.update(seconds(3599)); + hist.update(mkTimePoint(3599)); for (int pct = 1; pct <= 100; pct++) { int expected = (pct - 1) / 10 * 10; EXPECT_EQ(expected, hist.getPercentileBucketMin(pct, IntMTMHTS::MINUTE)); @@ -311,11 +317,11 @@ TEST(TimeseriesHistogram, Basic) { for (int now = 0; now < 3600; now++) { for (int i = 0; i < 50; i++) { - hist.addValue(seconds(now), i * 2, 2); // adds each item 2 times + hist.addValue(mkTimePoint(now), i * 2, 2); // adds each item 2 times } } - hist.update(seconds(3599)); + hist.update(mkTimePoint(3599)); for (int pct = 1; pct <= 100; pct++) { int expected = (pct - 1) / 10 * 10; EXPECT_EQ(expected, hist.getPercentileBucketMin(pct, IntMTMHTS::MINUTE)); @@ -348,9 +354,9 @@ TEST(TimeseriesHistogram, Basic) { } for (int i = 0; i < 100; ++i) { - hist.addValue(seconds(3599), 200 + i); + hist.addValue(mkTimePoint(3599), 200 + i); } - hist.update(seconds(3599)); + hist.update(mkTimePoint(3599)); EXPECT_EQ(100, hist.getBucket(hist.getNumBuckets() - 1).count( IntMTMHTS::ALLTIME)); @@ -364,27 +370,26 @@ TEST(TimeseriesHistogram, QueryByInterval) { 60, IntMHTS::NUM_LEVELS, IntMHTS::kDurations)); - mhts.update(seconds(0)); + mhts.update(mkTimePoint(0)); int curTime; for (curTime = 0; curTime < 7200; curTime++) { - mhts.addValue(seconds(curTime), 1); + mhts.addValue(mkTimePoint(curTime), 1); } for (curTime = 7200; curTime < 7200 + 3540; curTime++) { - mhts.addValue(seconds(curTime), 10); + mhts.addValue(mkTimePoint(curTime), 10); } for (curTime = 7200 + 3540; curTime < 7200 + 3600; curTime++) { - mhts.addValue(seconds(curTime), 100); + mhts.addValue(mkTimePoint(curTime), 100); } - mhts.update(seconds(7200 + 3600 - 1)); + mhts.update(mkTimePoint(7200 + 3600 - 1)); struct TimeInterval { - TimeInterval(int s, int e) - : start(s), end(e) {} + TimeInterval(int s, int e) : start(mkTimePoint(s)), end(mkTimePoint(e)) {} - std::chrono::seconds start; - std::chrono::seconds end; + StatsClock::time_point start; + StatsClock::time_point end; }; TimeInterval intervals[12] = { { curTime - 60, curTime }, @@ -442,10 +447,14 @@ TEST(TimeseriesHistogram, QueryByInterval) { // 3 levels for (int i = 1; i <= 100; i++) { EXPECT_EQ(96, mhts.getPercentileBucketMin(i, 0)); - EXPECT_EQ(96, mhts.getPercentileBucketMin(i, seconds(curTime - 60), - seconds(curTime))); - EXPECT_EQ(8, mhts.getPercentileBucketMin(i, seconds(curTime - 3540), - seconds(curTime - 60))); + EXPECT_EQ( + 96, + mhts.getPercentileBucketMin( + i, mkTimePoint(curTime - 60), mkTimePoint(curTime))); + EXPECT_EQ( + 8, + mhts.getPercentileBucketMin( + i, mkTimePoint(curTime - 3540), mkTimePoint(curTime - 60))); } EXPECT_EQ(8, mhts.getPercentileBucketMin(1, 1)); @@ -477,9 +486,9 @@ TEST(TimeseriesHistogram, QueryByInterval) { // Some of the older intervals that fall in the alltime bucket // are off by 1 or 2 in their estimated counts. size_t tolerance = 0; - if (itv.start <= seconds(curTime - 7200)) { + if (itv.start <= mkTimePoint(curTime - 7200)) { tolerance = 2; - } else if (itv.start <= seconds(curTime - 3000)) { + } else if (itv.start <= mkTimePoint(curTime - 3000)) { tolerance = 1; } size_t actualCount = (itv.end - itv.start).count(); @@ -500,9 +509,9 @@ TEST(TimeseriesHistogram, SingleUniqueValue) { const int kNumIters = 1000; for (int jj = 0; jj < kNumIters; ++jj) { - h.addValue(seconds(time(nullptr)), value); + h.addValue(mkTimePoint(1), value); } - h.update(seconds(time(nullptr))); + h.update(mkTimePoint(1)); // since we've only added one unique value, all percentiles should // be that value EXPECT_EQ(h.getPercentileEstimate(10, 0), value); @@ -512,9 +521,9 @@ TEST(TimeseriesHistogram, SingleUniqueValue) { // Things get trickier if there are multiple unique values. const int kNewValue = 750; for (int kk = 0; kk < 2*kNumIters; ++kk) { - h.addValue(seconds(time(nullptr)), kNewValue); + h.addValue(mkTimePoint(1), kNewValue); } - h.update(seconds(time(nullptr))); + h.update(mkTimePoint(1)); EXPECT_NEAR(h.getPercentileEstimate(50, 0), kNewValue+5, 5); if (value >= 0 && value <= 1000) { // only do further testing if value is within our bucket range, diff --git a/folly/test/TimeseriesTest.cpp b/folly/test/TimeseriesTest.cpp index 69b23845..5f15baf6 100644 --- a/folly/test/TimeseriesTest.cpp +++ b/folly/test/TimeseriesTest.cpp @@ -31,10 +31,39 @@ using std::string; using std::vector; using folly::BucketedTimeSeries; +using StatsClock = folly::LegacyStatsClock; +using TimePoint = StatsClock::time_point; + +/* + * Helper functions to allow us to directly log time points and duration + */ +namespace std { +std::ostream& operator<<(std::ostream& os, std::chrono::seconds s) { + os << s.count(); + return os; +} +std::ostream& operator<<(std::ostream& os, TimePoint tp) { + os << tp.time_since_epoch().count(); + return os; +} +} + +namespace { +TimePoint mkTimePoint(int value) { + return TimePoint(StatsClock::duration(value)); +} + struct TestData { - size_t duration; + TestData(int d, int b, std::initializer_list starts) + : duration(d), numBuckets(b) { + bucketStarts.reserve(starts.size()); + for (int s : starts) { + bucketStarts.push_back(mkTimePoint(s)); + } + } + seconds duration; size_t numBuckets; - vector bucketStarts; + vector bucketStarts; }; vector testData = { // 71 seconds x 4 buckets @@ -48,54 +77,56 @@ vector testData = { // 1 second x 1 buckets { 1, 1, {0}}, }; +} TEST(BucketedTimeSeries, getBucketInfo) { for (const auto& data : testData) { - BucketedTimeSeries ts(data.numBuckets, seconds(data.duration)); + BucketedTimeSeries ts(data.numBuckets, data.duration); for (uint32_t n = 0; n < 10000; n += 1234) { seconds offset(n * data.duration); for (uint32_t idx = 0; idx < data.numBuckets; ++idx) { - seconds bucketStart(data.bucketStarts[idx]); - seconds nextBucketStart; + auto bucketStart = data.bucketStarts[idx]; + TimePoint nextBucketStart; if (idx + 1 < data.numBuckets) { - nextBucketStart = seconds(data.bucketStarts[idx + 1]); + nextBucketStart = data.bucketStarts[idx + 1]; } else { - nextBucketStart = seconds(data.duration); + nextBucketStart = TimePoint(data.duration); } - seconds expectedStart = offset + bucketStart; - seconds expectedNextStart = offset + nextBucketStart; - seconds midpoint = (expectedStart + expectedNextStart) / 2; + TimePoint expectedStart = offset + bucketStart; + TimePoint expectedNextStart = offset + nextBucketStart; + TimePoint midpoint = + expectedStart + (expectedNextStart - expectedStart) / 2; - vector> timePoints = { - {"expectedStart", expectedStart}, - {"midpoint", midpoint}, - {"expectedEnd", expectedNextStart - seconds(1)}, + vector> timePoints = { + {"expectedStart", expectedStart}, + {"midpoint", midpoint}, + {"expectedEnd", expectedNextStart - seconds(1)}, }; for (const auto& point : timePoints) { // Check that getBucketIdx() returns the expected index - EXPECT_EQ(idx, ts.getBucketIdx(point.second)) << - data.duration << "x" << data.numBuckets << ": " << - point.first << "=" << point.second.count(); + EXPECT_EQ(idx, ts.getBucketIdx(point.second)) + << data.duration << "x" << data.numBuckets << ": " << point.first + << "=" << point.second; // Check the data returned by getBucketInfo() size_t returnedIdx; - seconds returnedStart; - seconds returnedNextStart; + TimePoint returnedStart; + TimePoint returnedNextStart; ts.getBucketInfo(expectedStart, &returnedIdx, &returnedStart, &returnedNextStart); - EXPECT_EQ(idx, returnedIdx) << - data.duration << "x" << data.numBuckets << ": " << - point.first << "=" << point.second.count(); - EXPECT_EQ(expectedStart.count(), returnedStart.count()) << - data.duration << "x" << data.numBuckets << ": " << - point.first << "=" << point.second.count(); - EXPECT_EQ(expectedNextStart.count(), returnedNextStart.count()) << - data.duration << "x" << data.numBuckets << ": " << - point.first << "=" << point.second.count(); + EXPECT_EQ(idx, returnedIdx) << data.duration << "x" << data.numBuckets + << ": " << point.first << "=" + << point.second; + EXPECT_EQ(expectedStart, returnedStart) + << data.duration << "x" << data.numBuckets << ": " << point.first + << "=" << point.second; + EXPECT_EQ(expectedNextStart, returnedNextStart) + << data.duration << "x" << data.numBuckets << ": " << point.first + << "=" << point.second; } } } @@ -467,20 +498,22 @@ TEST(BucketedTimeSeries, avgTypeConversion) { TEST(BucketedTimeSeries, forEachBucket) { typedef BucketedTimeSeries::Bucket Bucket; struct BucketInfo { - BucketInfo(const Bucket* b, seconds s, seconds ns) - : bucket(b), start(s), nextStart(ns) {} + BucketInfo(const Bucket* b, TimePoint s, TimePoint ns) + : bucket(b), start(s), nextStart(ns) {} const Bucket* bucket; - seconds start; - seconds nextStart; + TimePoint start; + TimePoint nextStart; }; for (const auto& data : testData) { BucketedTimeSeries ts(data.numBuckets, seconds(data.duration)); vector info; - auto fn = [&](const Bucket& bucket, seconds bucketStart, - seconds bucketEnd) -> bool { + auto fn = [&]( + const Bucket& bucket, + TimePoint bucketStart, + TimePoint bucketEnd) -> bool { info.emplace_back(&bucket, bucketStart, bucketEnd); return true; }; @@ -494,28 +527,28 @@ TEST(BucketedTimeSeries, forEachBucket) { // Check the data passed in to the function size_t infoIdx = 0; size_t bucketIdx = 1; - ssize_t offset = -data.duration; + seconds offset = -data.duration; for (size_t n = 0; n < data.numBuckets; ++n) { if (bucketIdx >= data.numBuckets) { bucketIdx = 0; offset += data.duration; } - EXPECT_EQ(data.bucketStarts[bucketIdx] + offset, - info[infoIdx].start.count()) << - data.duration << "x" << data.numBuckets << ": bucketIdx=" << - bucketIdx << ", infoIdx=" << infoIdx; + EXPECT_EQ(data.bucketStarts[bucketIdx] + offset, info[infoIdx].start) + << data.duration << "x" << data.numBuckets + << ": bucketIdx=" << bucketIdx << ", infoIdx=" << infoIdx; size_t nextBucketIdx = bucketIdx + 1; - ssize_t nextOffset = offset; + seconds nextOffset = offset; if (nextBucketIdx >= data.numBuckets) { nextBucketIdx = 0; nextOffset += data.duration; } - EXPECT_EQ(data.bucketStarts[nextBucketIdx] + nextOffset, - info[infoIdx].nextStart.count()) << - data.duration << "x" << data.numBuckets << ": bucketIdx=" << - bucketIdx << ", infoIdx=" << infoIdx; + EXPECT_EQ( + data.bucketStarts[nextBucketIdx] + nextOffset, + info[infoIdx].nextStart) + << data.duration << "x" << data.numBuckets + << ": bucketIdx=" << bucketIdx << ", infoIdx=" << infoIdx; EXPECT_EQ(&ts.getBucketByIndex(bucketIdx), info[infoIdx].bucket); @@ -535,7 +568,7 @@ TEST(BucketedTimeSeries, queryByIntervalSimple) { // This is entirely in the first bucket, which has a sum of 4. // The code knows only part of the bucket is covered, and correctly // estimates the desired sum as 3. - EXPECT_EQ(2, a.sum(seconds(0), seconds(2))); + EXPECT_EQ(2, a.sum(mkTimePoint(0), mkTimePoint(2))); } TEST(BucketedTimeSeries, queryByInterval) { @@ -546,7 +579,7 @@ TEST(BucketedTimeSeries, queryByInterval) { for (unsigned int i = 0; i < kDuration; ++i) { // add value 'i' at time 'i' - b.addValue(seconds(i), i); + b.addValue(mkTimePoint(i), i); } // Current bucket state: @@ -572,39 +605,39 @@ TEST(BucketedTimeSeries, queryByInterval) { {0, -1, -1, -1, -1, -1, -1} }; - seconds currentTime = b.getLatestTime() + seconds(1); + TimePoint currentTime = b.getLatestTime() + seconds(1); for (int i = 0; i <= kDuration + 1; i++) { for (int j = 0; j <= kDuration - i; j++) { - seconds start = currentTime - seconds(i + j); - seconds end = currentTime - seconds(i); + TimePoint start = currentTime - seconds(i + j); + TimePoint end = currentTime - seconds(i); double expectedSum = expectedSums1[i][j]; - EXPECT_EQ(expectedSum, b.sum(start, end)) << - "i=" << i << ", j=" << j << - ", interval=[" << start.count() << ", " << end.count() << ")"; + EXPECT_EQ(expectedSum, b.sum(start, end)) + << "i=" << i << ", j=" << j << ", interval=[" << start << ", " << end + << ")"; uint64_t expectedCount = expectedCounts1[i][j]; - EXPECT_EQ(expectedCount, b.count(start, end)) << - "i=" << i << ", j=" << j << - ", interval=[" << start.count() << ", " << end.count() << ")"; + EXPECT_EQ(expectedCount, b.count(start, end)) + << "i=" << i << ", j=" << j << ", interval=[" << start << ", " << end + << ")"; double expectedAvg = expectedCount ? expectedSum / expectedCount : 0; - EXPECT_EQ(expectedAvg, b.avg(start, end)) << - "i=" << i << ", j=" << j << - ", interval=[" << start.count() << ", " << end.count() << ")"; + EXPECT_EQ(expectedAvg, b.avg(start, end)) + << "i=" << i << ", j=" << j << ", interval=[" << start << ", " << end + << ")"; double expectedRate = j ? expectedSum / j : 0; - EXPECT_EQ(expectedRate, b.rate(start, end)) << - "i=" << i << ", j=" << j << - ", interval=[" << start.count() << ", " << end.count() << ")"; + EXPECT_EQ(expectedRate, b.rate(start, end)) + << "i=" << i << ", j=" << j << ", interval=[" << start << ", " << end + << ")"; } } // Add 3 more values. // This will overwrite 1 full bucket, and put us halfway through the next. for (unsigned int i = kDuration; i < kDuration + 3; ++i) { - b.addValue(seconds(i), i); + b.addValue(mkTimePoint(i), i); } - EXPECT_EQ(seconds(4), b.getEarliestTime()); + EXPECT_EQ(mkTimePoint(4), b.getEarliestTime()); // Current bucket state: // 0: time=[6, 8): values=(6, 7), sum=13, count=2 @@ -632,35 +665,35 @@ TEST(BucketedTimeSeries, queryByInterval) { currentTime = b.getLatestTime() + seconds(1); for (int i = 0; i <= kDuration + 1; i++) { for (int j = 0; j <= kDuration - i; j++) { - seconds start = currentTime - seconds(i + j); - seconds end = currentTime - seconds(i); + TimePoint start = currentTime - seconds(i + j); + TimePoint end = currentTime - seconds(i); double expectedSum = expectedSums2[i][j]; - EXPECT_EQ(expectedSum, b.sum(start, end)) << - "i=" << i << ", j=" << j << - ", interval=[" << start.count() << ", " << end.count() << ")"; + EXPECT_EQ(expectedSum, b.sum(start, end)) + << "i=" << i << ", j=" << j << ", interval=[" << start << ", " << end + << ")"; uint64_t expectedCount = expectedCounts2[i][j]; - EXPECT_EQ(expectedCount, b.count(start, end)) << - "i=" << i << ", j=" << j << - ", interval=[" << start.count() << ", " << end.count() << ")"; + EXPECT_EQ(expectedCount, b.count(start, end)) + << "i=" << i << ", j=" << j << ", interval=[" << start << ", " << end + << ")"; double expectedAvg = expectedCount ? expectedSum / expectedCount : 0; - EXPECT_EQ(expectedAvg, b.avg(start, end)) << - "i=" << i << ", j=" << j << - ", interval=[" << start.count() << ", " << end.count() << ")"; + EXPECT_EQ(expectedAvg, b.avg(start, end)) + << "i=" << i << ", j=" << j << ", interval=[" << start << ", " << end + << ")"; - seconds dataStart = std::max(start, b.getEarliestTime()); - seconds dataEnd = std::max(end, dataStart); + TimePoint dataStart = std::max(start, b.getEarliestTime()); + TimePoint dataEnd = std::max(end, dataStart); seconds expectedInterval = dataEnd - dataStart; - EXPECT_EQ(expectedInterval, b.elapsed(start, end)) << - "i=" << i << ", j=" << j << - ", interval=[" << start.count() << ", " << end.count() << ")"; + EXPECT_EQ(expectedInterval, b.elapsed(start, end)) + << "i=" << i << ", j=" << j << ", interval=[" << start << ", " << end + << ")"; double expectedRate = expectedInterval.count() ? expectedSum / expectedInterval.count() : 0; - EXPECT_EQ(expectedRate, b.rate(start, end)) << - "i=" << i << ", j=" << j << - ", interval=[" << start.count() << ", " << end.count() << ")"; + EXPECT_EQ(expectedRate, b.rate(start, end)) + << "i=" << i << ", j=" << j << ", interval=[" << start << ", " << end + << ")"; } } } @@ -673,10 +706,10 @@ TEST(BucketedTimeSeries, rateByInterval) { // Add data points at a constant rate of 10 per second. // Start adding data points at kDuration, and fill half of the buckets for // now. - seconds start = kDuration; - seconds end = kDuration + (kDuration / 2); + TimePoint start(kDuration); + TimePoint end(kDuration + (kDuration / 2)); const double kFixedRate = 10.0; - for (seconds i = start; i < end; ++i) { + for (TimePoint i = start; i < end; i += seconds(1)) { b.addValue(i, kFixedRate); } @@ -696,24 +729,24 @@ TEST(BucketedTimeSeries, rateByInterval) { // We haven't added anything before time kDuration. // Querying data earlier than this should result in a rate of 0. - EXPECT_EQ(0.0, b.rate(seconds(0), seconds(1))); - EXPECT_EQ(0.0, b.countRate(seconds(0), seconds(1))); + EXPECT_EQ(0.0, b.rate(mkTimePoint(0), mkTimePoint(1))); + EXPECT_EQ(0.0, b.countRate(mkTimePoint(0), mkTimePoint(1))); // Fill the remainder of the timeseries from kDuration to kDuration*2 start = end; - end = kDuration * 2; - for (seconds i = start; i < end; ++i) { + end = TimePoint(kDuration * 2); + for (TimePoint i = start; i < end; i += seconds(1)) { b.addValue(i, kFixedRate); } EXPECT_EQ(kFixedRate, b.rate()); - EXPECT_EQ(kFixedRate, b.rate(kDuration, kDuration * 2)); - EXPECT_EQ(kFixedRate, b.rate(seconds(0), kDuration * 2)); - EXPECT_EQ(kFixedRate, b.rate(seconds(0), kDuration * 10)); + EXPECT_EQ(kFixedRate, b.rate(TimePoint(kDuration), TimePoint(kDuration * 2))); + EXPECT_EQ(kFixedRate, b.rate(TimePoint(), TimePoint(kDuration * 2))); + EXPECT_EQ(kFixedRate, b.rate(TimePoint(), TimePoint(kDuration * 10))); EXPECT_EQ(1.0, b.countRate()); - EXPECT_EQ(1.0, b.countRate(kDuration, kDuration * 2)); - EXPECT_EQ(1.0, b.countRate(seconds(0), kDuration * 2)); - EXPECT_EQ(1.0, b.countRate(seconds(0), kDuration * 10)); + EXPECT_EQ(1.0, b.countRate(TimePoint(kDuration), TimePoint(kDuration * 2))); + EXPECT_EQ(1.0, b.countRate(TimePoint(), TimePoint(kDuration * 2))); + EXPECT_EQ(1.0, b.countRate(TimePoint(), TimePoint(kDuration * 10))); } TEST(BucketedTimeSeries, addHistorical) { @@ -722,7 +755,7 @@ TEST(BucketedTimeSeries, addHistorical) { BucketedTimeSeries b(kNumBuckets, kDuration); // Initially fill with a constant rate of data - for (seconds i = seconds(0); i < seconds(10); ++i) { + for (TimePoint i = mkTimePoint(0); i < mkTimePoint(10); i += seconds(1)) { b.addValue(i, 10.0); } @@ -731,15 +764,15 @@ TEST(BucketedTimeSeries, addHistorical) { EXPECT_EQ(10, b.count()); // Add some more data points to the middle bucket - b.addValue(seconds(4), 40.0); - b.addValue(seconds(5), 40.0); + b.addValue(mkTimePoint(4), 40.0); + b.addValue(mkTimePoint(5), 40.0); EXPECT_EQ(15.0, b.avg()); EXPECT_EQ(18.0, b.rate()); EXPECT_EQ(12, b.count()); // Now start adding more current data points, until we are about to roll over // the bucket where we added the extra historical data. - for (seconds i = seconds(10); i < seconds(14); ++i) { + for (TimePoint i = mkTimePoint(10); i < mkTimePoint(14); i += seconds(1)) { b.addValue(i, 10.0); } EXPECT_EQ(15.0, b.avg()); @@ -747,16 +780,16 @@ TEST(BucketedTimeSeries, addHistorical) { EXPECT_EQ(12, b.count()); // Now roll over the middle bucket - b.addValue(seconds(14), 10.0); - b.addValue(seconds(15), 10.0); + b.addValue(mkTimePoint(14), 10.0); + b.addValue(mkTimePoint(15), 10.0); EXPECT_EQ(10.0, b.avg()); EXPECT_EQ(10.0, b.rate()); EXPECT_EQ(10, b.count()); // Add more historical values past the bucket window. // These should be ignored. - EXPECT_FALSE(b.addValue(seconds(4), 40.0)); - EXPECT_FALSE(b.addValue(seconds(5), 40.0)); + EXPECT_FALSE(b.addValue(mkTimePoint(4), 40.0)); + EXPECT_FALSE(b.addValue(mkTimePoint(5), 40.0)); EXPECT_EQ(10.0, b.avg()); EXPECT_EQ(10.0, b.rate()); EXPECT_EQ(10, b.count()); @@ -898,22 +931,24 @@ TEST(MinuteHourTimeSeries, QueryByInterval) { folly::MultiLevelTimeSeries mhts(60, IntMHTS::NUM_LEVELS, IntMHTS::kMinuteHourDurations); - seconds curTime(0); - for (curTime = seconds(0); curTime < seconds(7200); curTime++) { + TimePoint curTime; + for (curTime = mkTimePoint(0); curTime < mkTimePoint(7200); + curTime += seconds(1)) { mhts.addValue(curTime, 1); } - for (curTime = seconds(7200); curTime < seconds(7200 + 3540); curTime++) { + for (curTime = mkTimePoint(7200); curTime < mkTimePoint(7200 + 3540); + curTime += seconds(1)) { mhts.addValue(curTime, 10); } - for (curTime = seconds(7200 + 3540); curTime < seconds(7200 + 3600); - curTime++) { + for (curTime = mkTimePoint(7200 + 3540); curTime < mkTimePoint(7200 + 3600); + curTime += seconds(1)) { mhts.addValue(curTime, 100); } mhts.flush(); struct TimeInterval { - seconds start; - seconds end; + TimePoint start; + TimePoint end; }; TimeInterval intervals[12] = { { curTime - seconds(60), curTime }, @@ -1080,22 +1115,24 @@ TEST(MultiLevelTimeSeries, QueryByInterval) { folly::MultiLevelTimeSeries mhts( 60, {seconds(60), seconds(3600), seconds(0)}); - seconds curTime(0); - for (curTime = seconds(0); curTime < seconds(7200); curTime++) { + TimePoint curTime; + for (curTime = mkTimePoint(0); curTime < mkTimePoint(7200); + curTime += seconds(1)) { mhts.addValue(curTime, 1); } - for (curTime = seconds(7200); curTime < seconds(7200 + 3540); curTime++) { + for (curTime = mkTimePoint(7200); curTime < mkTimePoint(7200 + 3540); + curTime += seconds(1)) { mhts.addValue(curTime, 10); } - for (curTime = seconds(7200 + 3540); curTime < seconds(7200 + 3600); - curTime++) { + for (curTime = mkTimePoint(7200 + 3540); curTime < mkTimePoint(7200 + 3600); + curTime += seconds(1)) { mhts.addValue(curTime, 100); } mhts.flush(); struct TimeInterval { - seconds start; - seconds end; + TimePoint start; + TimePoint end; }; std::array intervals = {{ -- 2.34.1