Commit 42ca5613 authored by Adam Simpkins's avatar Adam Simpkins Committed by Facebook Github Bot 2

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
parent 864eb2a6
......@@ -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 <typename ReturnType=double,
typename TimeType=std::chrono::seconds,
typename Interval=TimeType>
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<TimeType::period::den,
TimeType::period::num>> NativeRate;
ReturnType,
std::ratio<Duration::period::den, Duration::period::num>>
NativeRate;
typedef std::chrono::duration<
ReturnType, std::ratio<Interval::period::den,
Interval::period::num>> DesiredRate;
......
This diff is collapsed.
This diff is collapsed.
......@@ -25,8 +25,8 @@ template <typename VT, typename CT>
MultiLevelTimeSeries<VT, CT>::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<VT, CT>::MultiLevelTimeSeries(
template <typename VT, typename CT>
MultiLevelTimeSeries<VT, CT>::MultiLevelTimeSeries(
size_t nBuckets,
std::initializer_list<TimeType> durations)
: cachedTime_(0), cachedSum_(0), cachedCount_(0) {
std::initializer_list<Duration> 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<VT, CT>::MultiLevelTimeSeries(
template <typename VT, typename CT>
void MultiLevelTimeSeries<VT, CT>::addValue(
TimeType now,
TimePoint now,
const ValueType& val) {
addValueAggregated(now, val, 1);
}
template <typename VT, typename CT>
void MultiLevelTimeSeries<VT, CT>::addValue(
TimeType now,
TimePoint now,
const ValueType& val,
int64_t times) {
addValueAggregated(now, val * times, times);
......@@ -80,7 +80,7 @@ void MultiLevelTimeSeries<VT, CT>::addValue(
template <typename VT, typename CT>
void MultiLevelTimeSeries<VT, CT>::addValueAggregated(
TimeType now,
TimePoint now,
const ValueType& total,
int64_t nsamples) {
if (cachedTime_ != now) {
......@@ -92,7 +92,7 @@ void MultiLevelTimeSeries<VT, CT>::addValueAggregated(
}
template <typename VT, typename CT>
void MultiLevelTimeSeries<VT, CT>::update(TimeType now) {
void MultiLevelTimeSeries<VT, CT>::update(TimePoint now) {
flush();
for (size_t i = 0; i < levels_.size(); ++i) {
levels_[i].update(now);
......@@ -117,7 +117,7 @@ void MultiLevelTimeSeries<VT, CT>::clear() {
level.clear();
}
cachedTime_ = TimeType(0);
cachedTime_ = TimePoint();
cachedSum_ = 0;
cachedCount_ = 0;
}
......
......@@ -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<ValueType, Clock>;
/*
......@@ -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<TimeType> durations);
std::initializer_list<Duration> 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 <typename ReturnType=double, typename Interval=TimeType>
template <typename ReturnType = double, typename Interval = Duration>
ReturnType rate(int level) const {
return getLevel(level).template rate<ReturnType, Interval>();
}
......@@ -212,7 +209,7 @@ class MultiLevelTimeSeries {
* data. Otherwise you may be reading stale data if update() or flush() has
* not been called recently.
*/
template <typename ReturnType=double, typename Interval=TimeType>
template <typename ReturnType = double, typename Interval = Duration>
ReturnType countRate(int level) const {
return getLevel(level).template countRate<ReturnType, Interval>();
}
......@@ -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 <typename ReturnType = double>
ReturnType avg(TimeType duration) const {
ReturnType avg(Duration duration) const {
return getLevelByDuration(duration).template avg<ReturnType>();
}
......@@ -258,8 +255,8 @@ class MultiLevelTimeSeries {
* data. Otherwise you may be reading stale data if update() or flush() has
* not been called recently.
*/
template <typename ReturnType = double, typename Interval = TimeType>
ReturnType rate(TimeType duration) const {
template <typename ReturnType = double, typename Interval = Duration>
ReturnType rate(Duration duration) const {
return getLevelByDuration(duration).template rate<ReturnType, Interval>();
}
......@@ -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 <typename ReturnType = double, typename Interval = TimeType>
ReturnType countRate(TimeType duration) const {
template <typename ReturnType = double, typename Interval = Duration>
ReturnType countRate(Duration duration) const {
return getLevelByDuration(duration)
.template countRate<ReturnType, Interval>();
}
......@@ -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 <typename ReturnType=double>
ReturnType avg(TimeType start, TimeType end) const {
template <typename ReturnType = double>
ReturnType avg(TimePoint start, TimePoint end) const {
return getLevel(start).template avg<ReturnType>(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 <typename ReturnType=double>
ReturnType rate(TimeType start, TimeType end) const {
template <typename ReturnType = double>
ReturnType rate(TimePoint start, TimePoint end) const {
return getLevel(start).template rate<ReturnType>(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<Level> 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_;
};
......
......@@ -35,7 +35,7 @@ TimeseriesHistogram<T, CT, C>::TimeseriesHistogram(
template <typename T, typename CT, typename C>
void TimeseriesHistogram<T, CT, C>::addValue(
TimeType now,
TimePoint now,
const ValueType& value) {
buckets_.getByValue(value).addValue(now, value);
maybeHandleSingleUniqueValue(value);
......@@ -43,7 +43,7 @@ void TimeseriesHistogram<T, CT, C>::addValue(
template <typename T, typename CT, typename C>
void TimeseriesHistogram<T, CT, C>::addValue(
TimeType now,
TimePoint now,
const ValueType& value,
int64_t times) {
buckets_.getByValue(value).addValue(now, value, times);
......@@ -52,7 +52,7 @@ void TimeseriesHistogram<T, CT, C>::addValue(
template <typename T, typename CT, typename C>
void TimeseriesHistogram<T, CT, C>::addValues(
TimeType now,
TimePoint now,
const folly::Histogram<ValueType>& hist) {
CHECK_EQ(hist.getMin(), getMin());
CHECK_EQ(hist.getMax(), getMax());
......@@ -99,8 +99,8 @@ T TimeseriesHistogram<T, CT, C>::getPercentileEstimate(double pct, int level)
template <typename T, typename CT, typename C>
T TimeseriesHistogram<T, CT, C>::getPercentileEstimate(
double pct,
TimeType start,
TimeType end) const {
TimePoint start,
TimePoint end) const {
if (singleUniqueValue_) {
return firstValue_;
}
......@@ -119,8 +119,8 @@ int TimeseriesHistogram<T, CT, C>::getPercentileBucketIdx(double pct, int level)
template <typename T, typename CT, typename C>
int TimeseriesHistogram<T, CT, C>::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<T, CT, C>::clear() {
}
template <typename T, typename CT, typename C>
void TimeseriesHistogram<T, CT, C>::update(TimeType now) {
void TimeseriesHistogram<T, CT, C>::update(TimePoint now) {
for (size_t i = 0; i < buckets_.getNumBuckets(); i++) {
buckets_.getByIndex(i).update(now);
}
......@@ -158,8 +158,8 @@ std::string TimeseriesHistogram<T, CT, C>::getString(int level) const {
template <typename T, typename CT, typename C>
std::string TimeseriesHistogram<T, CT, C>::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 <class T, class CT, class C>
void TimeseriesHistogram<T, CT, C>::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<T, CT, C>::computeAvgData(
template <typename T, typename CT, typename C>
void TimeseriesHistogram<T, CT, C>::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<T, CT, C>::computeRateData(
template <class T, class CT, class C>
void TimeseriesHistogram<T, CT, C>::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);
......
......@@ -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 <typename ReturnType = double>
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 <typename ReturnType = double>
ReturnType rate(int level) const {
auto total = ValueType();
TimeType elapsed(0);
Duration elapsed(0);
computeRateData(&total, &elapsed, level);
return folly::detail::rateHelper<ReturnType, TimeType, TimeType>(
return folly::detail::rateHelper<ReturnType, Duration, Duration>(
total, elapsed);
}
......@@ -190,11 +186,11 @@ class TimeseriesHistogram {
* This is the sum of all values divided by the time interval (in seconds).
*/
template <typename ReturnType = double>
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<ReturnType, TimeType, TimeType>(
return folly::detail::rateHelper<ReturnType, Duration, Duration>(
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<ValueType>& values);
void addValues(TimePoint now, const folly::Histogram<ValueType>& 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<ValueType>& 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 <typename ReturnType>
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<ReturnType>(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<ValueType, ContainerType> buckets_;
bool haveNotSeenValue_;
......
......@@ -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<std::chrono::seconds>;
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::seconds>(
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<int>::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<double>(IntMTMHTS::HOUR));
EXPECT_EQ(4950, hist.rate<double>(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<double>(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<double>(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<double>(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<double>(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<double>(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<double>(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,
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment