namespace folly {
-template <typename VT, typename TT>
-BucketedTimeSeries<VT, TT>::BucketedTimeSeries(size_t nBuckets,
- TimeType maxDuration)
- : firstTime_(1),
- latestTime_(0),
- duration_(maxDuration) {
+template <typename VT, typename CT>
+BucketedTimeSeries<VT, CT>::BucketedTimeSeries(
+ size_t nBuckets,
+ TimeType maxDuration)
+ : firstTime_(1), latestTime_(0), duration_(maxDuration) {
// For tracking all-time data we only use total_, and don't need to bother
// with buckets_
if (!isAllTime()) {
}
}
-template <typename VT, typename TT>
-bool BucketedTimeSeries<VT, TT>::addValue(TimeType now, const ValueType& val) {
+template <typename VT, typename CT>
+bool BucketedTimeSeries<VT, CT>::addValue(TimeType now, const ValueType& val) {
return addValueAggregated(now, val, 1);
}
-template <typename VT, typename TT>
-bool BucketedTimeSeries<VT, TT>::addValue(TimeType now,
- const ValueType& val,
- int64_t times) {
+template <typename VT, typename CT>
+bool BucketedTimeSeries<VT, CT>::addValue(
+ TimeType now,
+ const ValueType& val,
+ int64_t times) {
return addValueAggregated(now, val * times, times);
}
-template <typename VT, typename TT>
-bool BucketedTimeSeries<VT, TT>::addValueAggregated(TimeType now,
- const ValueType& total,
- int64_t nsamples) {
+template <typename VT, typename CT>
+bool BucketedTimeSeries<VT, CT>::addValueAggregated(
+ TimeType now,
+ const ValueType& total,
+ int64_t nsamples) {
if (isAllTime()) {
if (UNLIKELY(empty())) {
firstTime_ = now;
return true;
}
-template <typename VT, typename TT>
-size_t BucketedTimeSeries<VT, TT>::update(TimeType now) {
+template <typename VT, typename CT>
+size_t BucketedTimeSeries<VT, CT>::update(TimeType now) {
if (empty()) {
// This is the first data point.
firstTime_ = now;
return updateBuckets(now);
}
-template <typename VT, typename TT>
-size_t BucketedTimeSeries<VT, TT>::updateBuckets(TimeType now) {
+template <typename VT, typename CT>
+size_t BucketedTimeSeries<VT, CT>::updateBuckets(TimeType 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
}
}
-template <typename VT, typename TT>
-void BucketedTimeSeries<VT, TT>::clear() {
+template <typename VT, typename CT>
+void BucketedTimeSeries<VT, CT>::clear() {
for (Bucket& bucket : buckets_) {
bucket.clear();
}
latestTime_ = TimeType(0);
}
-
-template <typename VT, typename TT>
-TT BucketedTimeSeries<VT, TT>::getEarliestTime() const {
+template <typename VT, typename CT>
+typename CT::duration BucketedTimeSeries<VT, CT>::getEarliestTime() const {
if (empty()) {
return TimeType(0);
}
return earliestTime;
}
-template <typename VT, typename TT>
-TT BucketedTimeSeries<VT, TT>::getEarliestTimeNonEmpty() const {
+template <typename VT, typename CT>
+typename CT::duration BucketedTimeSeries<VT, CT>::getEarliestTimeNonEmpty()
+ const {
size_t currentBucket;
TimeType currentBucketStart;
TimeType nextBucketStart;
return nextBucketStart - duration_;
}
-template <typename VT, typename TT>
-TT BucketedTimeSeries<VT, TT>::elapsed() const {
+template <typename VT, typename CT>
+typename CT::duration BucketedTimeSeries<VT, CT>::elapsed() const {
if (empty()) {
return TimeType(0);
}
return latestTime_ - getEarliestTime() + TimeType(1);
}
-template <typename VT, typename TT>
-TT BucketedTimeSeries<VT, TT>::elapsed(TimeType start, TimeType end) const {
+template <typename VT, typename CT>
+typename CT::duration BucketedTimeSeries<VT, CT>::elapsed(
+ TimeType start,
+ TimeType end) const {
if (empty()) {
return TimeType(0);
}
return end - start;
}
-template <typename VT, typename TT>
-VT BucketedTimeSeries<VT, TT>::sum(TimeType start, TimeType end) const {
+template <typename VT, typename CT>
+VT BucketedTimeSeries<VT, CT>::sum(TimeType start, TimeType end) const {
ValueType total = ValueType();
forEachBucket(start, end, [&](const Bucket& bucket,
TimeType bucketStart,
return total;
}
-template <typename VT, typename TT>
-uint64_t BucketedTimeSeries<VT, TT>::count(TimeType start, TimeType end) const {
+template <typename VT, typename CT>
+uint64_t BucketedTimeSeries<VT, CT>::count(TimeType start, TimeType end) const {
uint64_t sample_count = 0;
forEachBucket(start, end, [&](const Bucket& bucket,
TimeType bucketStart,
return sample_count;
}
-template <typename VT, typename TT>
+template <typename VT, typename CT>
template <typename ReturnType>
-ReturnType BucketedTimeSeries<VT, TT>::avg(TimeType start, TimeType end) const {
+ReturnType BucketedTimeSeries<VT, CT>::avg(TimeType start, TimeType end) const {
ValueType total = ValueType();
uint64_t sample_count = 0;
forEachBucket(start, end, [&](const Bucket& bucket,
* into, we then divide by duration_.
*/
-template <typename VT, typename TT>
-size_t BucketedTimeSeries<VT, TT>::getBucketIdx(TimeType time) const {
+template <typename VT, typename CT>
+size_t BucketedTimeSeries<VT, CT>::getBucketIdx(TimeType time) const {
// For all-time data we don't use buckets_. Everything is tracked in total_.
DCHECK(!isAllTime());
* Compute the bucket index for the specified time, as well as the earliest
* time that falls into this bucket.
*/
-template <typename VT, typename TT>
-void BucketedTimeSeries<VT, TT>::getBucketInfo(
- TimeType time, size_t *bucketIdx,
- TimeType* bucketStart, TimeType* nextBucketStart) const {
+template <typename VT, typename CT>
+void BucketedTimeSeries<VT, CT>::getBucketInfo(
+ TimeType time,
+ size_t* bucketIdx,
+ TimeType* bucketStart,
+ TimeType* nextBucketStart) const {
typedef typename TimeType::rep TimeInt;
DCHECK(!isAllTime());
*nextBucketStart = nextBucketStartMod + durationStart;
}
-template <typename VT, typename TT>
+template <typename VT, typename CT>
template <typename Function>
-void BucketedTimeSeries<VT, TT>::forEachBucket(Function fn) const {
+void BucketedTimeSeries<VT, CT>::forEachBucket(Function fn) const {
if (isAllTime()) {
fn(total_, firstTime_, latestTime_ + TimeType(1));
return;
* For example, if the bucket spans time [10, 20), but we only care about the
* range [10, 16), this will return 60% of the input value.
*/
-template<typename VT, typename TT>
-VT BucketedTimeSeries<VT, TT>::rangeAdjust(
- TimeType bucketStart, TimeType nextBucketStart,
- TimeType start, TimeType end, ValueType input) const {
+template <typename VT, typename CT>
+VT BucketedTimeSeries<VT, CT>::rangeAdjust(
+ TimeType bucketStart,
+ TimeType nextBucketStart,
+ TimeType start,
+ TimeType 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
// querying for all of the data up to latestTime_. Even though latestTime_
(nextBucketStart - bucketStart);
}
-template <typename VT, typename TT>
+template <typename VT, typename CT>
template <typename Function>
-void BucketedTimeSeries<VT, TT>::forEachBucket(TimeType start, TimeType end,
- Function fn) const {
+void BucketedTimeSeries<VT, CT>::forEachBucket(
+ TimeType start,
+ TimeType end,
+ Function fn) const {
forEachBucket([&start, &end, &fn] (const Bucket& bucket, TimeType bucketStart,
TimeType nextBucketStart) -> bool {
if (start >= nextBucketStart) {
namespace folly {
+/*
+ * A helper clock type to helper older code using BucketedTimeSeries with
+ * std::chrono::seconds transition to properly using clock types and time_point
+ * objects.
+ */
+template <typename TT = std::chrono::seconds>
+class LegacyStatsClock {
+ public:
+ using duration = TT;
+ using time_point = std::chrono::time_point<LegacyStatsClock, TT>;
+
+ // This clock does not actually implement now(), since the older API
+ // did not really specify what clock should be used. (In practice most
+ // callers unfortuantely used wall clock time rather than a monotonic clock.)
+};
+
/*
* This class represents a bucketed time series which keeps track of values
* added in the recent past, and merges these values together into a fixed
*
* This class is not thread-safe -- use your own synchronization!
*/
-template <typename VT, typename TT=std::chrono::seconds>
+template <typename VT, typename CT = LegacyStatsClock<std::chrono::seconds>>
class BucketedTimeSeries {
public:
- typedef VT ValueType;
- typedef TT TimeType;
- typedef detail::Bucket<ValueType> Bucket;
+ using ValueType = VT;
+ 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<ValueType>;
/*
* Create a new BucketedTimeSeries.
* and does not need the rolling buckets. The numBuckets parameter is
* ignored when duration is 0.
*/
- BucketedTimeSeries(size_t numBuckets, TimeType duration);
+ BucketedTimeSeries(size_t numBuckets, Duration duration);
/*
* Adds the value 'val' at time 'now'
namespace folly {
-template <typename VT, typename TT>
-MultiLevelTimeSeries<VT, TT>::MultiLevelTimeSeries(
- size_t nBuckets,
- size_t nLevels,
- const TimeType levelDurations[])
- : cachedTime_(0),
- cachedSum_(0),
- cachedCount_(0) {
- CHECK_GT(nLevels, 0);
- CHECK(levelDurations);
+template <typename VT, typename CT>
+MultiLevelTimeSeries<VT, CT>::MultiLevelTimeSeries(
+ size_t nBuckets,
+ size_t nLevels,
+ const TimeType levelDurations[])
+ : cachedTime_(0), cachedSum_(0), cachedCount_(0) {
+ CHECK_GT(nLevels, 0);
+ CHECK(levelDurations);
- levels_.reserve(nLevels);
- for (size_t i = 0; i < nLevels; ++i) {
- if (levelDurations[i] == TT(0)) {
- CHECK_EQ(i, nLevels - 1);
- } else if (i > 0) {
- CHECK(levelDurations[i-1] < levelDurations[i]);
- }
- levels_.emplace_back(nBuckets, levelDurations[i]);
+ levels_.reserve(nLevels);
+ for (size_t i = 0; i < nLevels; ++i) {
+ if (levelDurations[i] == Duration(0)) {
+ CHECK_EQ(i, nLevels - 1);
+ } else if (i > 0) {
+ CHECK(levelDurations[i - 1] < levelDurations[i]);
}
+ levels_.emplace_back(nBuckets, levelDurations[i]);
+ }
}
-template <typename VT, typename TT>
-MultiLevelTimeSeries<VT, TT>::MultiLevelTimeSeries(
+template <typename VT, typename CT>
+MultiLevelTimeSeries<VT, CT>::MultiLevelTimeSeries(
size_t nBuckets,
std::initializer_list<TimeType> durations)
: cachedTime_(0), cachedSum_(0), cachedCount_(0) {
int i = 0;
TimeType prev;
for (auto dur : durations) {
- if (dur == TT(0)) {
+ if (dur == Duration(0)) {
CHECK_EQ(i, durations.size() - 1);
} else if (i > 0) {
CHECK(prev < dur);
}
}
-template <typename VT, typename TT>
-void MultiLevelTimeSeries<VT, TT>::addValue(
+template <typename VT, typename CT>
+void MultiLevelTimeSeries<VT, CT>::addValue(
TimeType now,
const ValueType& val) {
addValueAggregated(now, val, 1);
}
-template <typename VT, typename TT>
-void MultiLevelTimeSeries<VT, TT>::addValue(TimeType now,
- const ValueType& val,
- int64_t times) {
+template <typename VT, typename CT>
+void MultiLevelTimeSeries<VT, CT>::addValue(
+ TimeType now,
+ const ValueType& val,
+ int64_t times) {
addValueAggregated(now, val * times, times);
}
-template <typename VT, typename TT>
-void MultiLevelTimeSeries<VT, TT>::addValueAggregated(TimeType now,
- const ValueType& total,
- int64_t nsamples) {
+template <typename VT, typename CT>
+void MultiLevelTimeSeries<VT, CT>::addValueAggregated(
+ TimeType now,
+ const ValueType& total,
+ int64_t nsamples) {
if (cachedTime_ != now) {
flush();
cachedTime_ = now;
cachedCount_ += nsamples;
}
-template <typename VT, typename TT>
-void MultiLevelTimeSeries<VT, TT>::update(TimeType now) {
+template <typename VT, typename CT>
+void MultiLevelTimeSeries<VT, CT>::update(TimeType now) {
flush();
for (size_t i = 0; i < levels_.size(); ++i) {
levels_[i].update(now);
}
}
-template <typename VT, typename TT>
-void MultiLevelTimeSeries<VT, TT>::flush() {
+template <typename VT, typename CT>
+void MultiLevelTimeSeries<VT, CT>::flush() {
// update all the underlying levels
if (cachedCount_ > 0) {
for (size_t i = 0; i < levels_.size(); ++i) {
}
}
-template <typename VT, typename TT>
-void MultiLevelTimeSeries<VT, TT>::clear() {
+template <typename VT, typename CT>
+void MultiLevelTimeSeries<VT, CT>::clear() {
for (auto & level : levels_) {
level.clear();
}
*
* The class is not thread-safe -- use your own synchronization!
*/
-template <typename VT, typename TT=std::chrono::seconds>
+template <typename VT, typename CT = LegacyStatsClock<std::chrono::seconds>>
class MultiLevelTimeSeries {
public:
- typedef VT ValueType;
- typedef TT TimeType;
- typedef folly::BucketedTimeSeries<ValueType, TimeType> Level;
+ using ValueType = VT;
+ 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>;
/*
* Create a new MultiLevelTimeSeries.
namespace folly {
-template <typename T, typename TT, typename C>
-TimeseriesHistogram<T, TT, C>::TimeseriesHistogram(ValueType bucketSize,
- ValueType min,
- ValueType max,
- const ContainerType& copyMe)
- : buckets_(bucketSize, min, max, copyMe),
- haveNotSeenValue_(true),
- singleUniqueValue_(false) {
-}
-
-template <typename T, typename TT, typename C>
-void TimeseriesHistogram<T, TT, C>::addValue(TimeType now,
- const ValueType& value) {
+template <typename T, typename CT, typename C>
+TimeseriesHistogram<T, CT, C>::TimeseriesHistogram(
+ ValueType bucketSize,
+ ValueType min,
+ ValueType max,
+ const ContainerType& copyMe)
+ : buckets_(bucketSize, min, max, copyMe),
+ haveNotSeenValue_(true),
+ singleUniqueValue_(false) {}
+
+template <typename T, typename CT, typename C>
+void TimeseriesHistogram<T, CT, C>::addValue(
+ TimeType now,
+ const ValueType& value) {
buckets_.getByValue(value).addValue(now, value);
maybeHandleSingleUniqueValue(value);
}
-template <typename T, typename TT, typename C>
-void TimeseriesHistogram<T, TT, C>::addValue(TimeType now,
- const ValueType& value,
- int64_t times) {
+template <typename T, typename CT, typename C>
+void TimeseriesHistogram<T, CT, C>::addValue(
+ TimeType now,
+ const ValueType& value,
+ int64_t times) {
buckets_.getByValue(value).addValue(now, value, times);
maybeHandleSingleUniqueValue(value);
}
-template <typename T, typename TT, typename C>
-void TimeseriesHistogram<T, TT, C>::addValues(
- TimeType now, const folly::Histogram<ValueType>& hist) {
+template <typename T, typename CT, typename C>
+void TimeseriesHistogram<T, CT, C>::addValues(
+ TimeType now,
+ const folly::Histogram<ValueType>& hist) {
CHECK_EQ(hist.getMin(), getMin());
CHECK_EQ(hist.getMax(), getMax());
CHECK_EQ(hist.getBucketSize(), getBucketSize());
singleUniqueValue_ = false;
}
-template <typename T, typename TT, typename C>
-void TimeseriesHistogram<T, TT, C>::maybeHandleSingleUniqueValue(
- const ValueType& value) {
+template <typename T, typename CT, typename C>
+void TimeseriesHistogram<T, CT, C>::maybeHandleSingleUniqueValue(
+ const ValueType& value) {
if (haveNotSeenValue_) {
firstValue_ = value;
singleUniqueValue_ = true;
}
}
-template <typename T, typename TT, typename C>
-T TimeseriesHistogram<T, TT, C>::getPercentileEstimate(double pct,
- int level) const {
+template <typename T, typename CT, typename C>
+T TimeseriesHistogram<T, CT, C>::getPercentileEstimate(double pct, int level)
+ const {
if (singleUniqueValue_) {
return firstValue_;
}
AvgFromLevel(level));
}
-template <typename T, typename TT, typename C>
-T TimeseriesHistogram<T, TT, C>::getPercentileEstimate(double pct,
- TimeType start,
- TimeType end) const {
+template <typename T, typename CT, typename C>
+T TimeseriesHistogram<T, CT, C>::getPercentileEstimate(
+ double pct,
+ TimeType start,
+ TimeType end) const {
if (singleUniqueValue_) {
return firstValue_;
}
AvgFromInterval<T>(start, end));
}
-template <typename T, typename TT, typename C>
-int TimeseriesHistogram<T, TT, C>::getPercentileBucketIdx(
- double pct,
- int level
-) const {
+template <typename T, typename CT, typename C>
+int TimeseriesHistogram<T, CT, C>::getPercentileBucketIdx(double pct, int level)
+ const {
return buckets_.getPercentileBucketIdx(pct / 100.0, CountFromLevel(level));
}
-template <typename T, typename TT, typename C>
-int TimeseriesHistogram<T, TT, C>::getPercentileBucketIdx(double pct,
- TimeType start,
- TimeType end) const {
+template <typename T, typename CT, typename C>
+int TimeseriesHistogram<T, CT, C>::getPercentileBucketIdx(
+ double pct,
+ TimeType start,
+ TimeType end) const {
return buckets_.getPercentileBucketIdx(pct / 100.0,
CountFromInterval(start, end));
}
-template <typename T, typename TT, typename C>
-void TimeseriesHistogram<T, TT, C>::clear() {
+template <typename T, typename CT, typename C>
+void TimeseriesHistogram<T, CT, C>::clear() {
for (size_t i = 0; i < buckets_.getNumBuckets(); i++) {
buckets_.getByIndex(i).clear();
}
}
-template <typename T, typename TT, typename C>
-void TimeseriesHistogram<T, TT, C>::update(TimeType now) {
+template <typename T, typename CT, typename C>
+void TimeseriesHistogram<T, CT, C>::update(TimeType now) {
for (size_t i = 0; i < buckets_.getNumBuckets(); i++) {
buckets_.getByIndex(i).update(now);
}
}
-template <typename T, typename TT, typename C>
-std::string TimeseriesHistogram<T, TT, C>::getString(int level) const {
+template <typename T, typename CT, typename C>
+std::string TimeseriesHistogram<T, CT, C>::getString(int level) const {
std::string result;
for (size_t i = 0; i < buckets_.getNumBuckets(); i++) {
return result;
}
-template <typename T, typename TT, typename C>
-std::string TimeseriesHistogram<T, TT, C>::getString(TimeType start,
- TimeType end) const {
+template <typename T, typename CT, typename C>
+std::string TimeseriesHistogram<T, CT, C>::getString(
+ TimeType start,
+ TimeType end) const {
std::string result;
for (size_t i = 0; i < buckets_.getNumBuckets(); i++) {
return result;
}
-template <class T, class TT, class C>
-void TimeseriesHistogram<T, TT, C>::computeAvgData(
+template <class T, class CT, class C>
+void TimeseriesHistogram<T, CT, C>::computeAvgData(
ValueType* total,
int64_t* nsamples,
int level) const {
}
}
-template <class T, class TT, class C>
-void TimeseriesHistogram<T, TT, C>::computeAvgData(
+template <class T, class CT, class C>
+void TimeseriesHistogram<T, CT, C>::computeAvgData(
ValueType* total,
int64_t* nsamples,
TimeType start,
}
}
-template <typename T, typename TT, typename C>
-void TimeseriesHistogram<T, TT, C>::computeRateData(
+template <typename T, typename CT, typename C>
+void TimeseriesHistogram<T, CT, C>::computeRateData(
ValueType* total,
TimeType* elapsed,
int level) const {
}
}
-template <class T, class TT, class C>
-void TimeseriesHistogram<T, TT, C>::computeRateData(
+template <class T, class CT, class C>
+void TimeseriesHistogram<T, CT, C>::computeRateData(
ValueType* total,
TimeType* elapsed,
TimeType start,
* The memory usage for a typical histogram is roughly 3k * (# of buckets). All
* insertion operations are amortized O(1), and all queries are O(# of buckets).
*/
-template <class T, class TT=std::chrono::seconds,
- class C=folly::MultiLevelTimeSeries<T, TT>>
+template <
+ class T,
+ class CT = LegacyStatsClock<std::chrono::seconds>,
+ class C = folly::MultiLevelTimeSeries<T, CT>>
class TimeseriesHistogram {
private:
// NOTE: T must be equivalent to _signed_ numeric type for our math.
static_assert(std::numeric_limits<T>::is_signed, "");
public:
- // values to be inserted into container
- typedef T ValueType;
- // the container type we use internally for each bucket
- typedef C ContainerType;
- // The time type.
- typedef TT TimeType;
+ // Values to be inserted into container
+ using ValueType = T;
+ // The container type we use internally for each bucket
+ using ContainerType = C;
+ // Clock, duration, and time point types
+ 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.