Commit 8b030a19 authored by Adam Simpkins's avatar Adam Simpkins Committed by Facebook Github Bot 9

a simple first step towards using clocks properly in the stats code

Summary:
Update the timeseries and histogram classes to accept a clock parameter as a
template parameter, instead of a time duration type.

This is a first step towards transitioning the code to correctly distinguishing
between time_point and duration types.  This defines TimePoint and Duration
type aliases, but does not start using them yet.

In upcoming diffs I will start converting more APIs to correctly use TimePoint
instead of just Duration.

For now the default clock type is folly::LegacyStatsClock, which still uses
std::chrono::seconds as the default duration.  At the moment the stats code is
optimized for second granularity--the addValue() code has a fast path when
called in the same second as the last update.  When using finer granularity
durations this fast path can't be used as often.  I will also send out
subsequent diffs to make the code optimized for updates within the same bucket,
rather than just updates with the exact same time value.

Reviewed By: yfeldblum

Differential Revision: D3807715

fbshipit-source-id: 77696c4f44a8d85e4d6ff84d7656fe7a9709797c
parent 9637519e
...@@ -22,12 +22,11 @@ ...@@ -22,12 +22,11 @@
namespace folly { namespace folly {
template <typename VT, typename TT> template <typename VT, typename CT>
BucketedTimeSeries<VT, TT>::BucketedTimeSeries(size_t nBuckets, BucketedTimeSeries<VT, CT>::BucketedTimeSeries(
TimeType maxDuration) size_t nBuckets,
: firstTime_(1), TimeType maxDuration)
latestTime_(0), : firstTime_(1), latestTime_(0), duration_(maxDuration) {
duration_(maxDuration) {
// For tracking all-time data we only use total_, and don't need to bother // For tracking all-time data we only use total_, and don't need to bother
// with buckets_ // with buckets_
if (!isAllTime()) { if (!isAllTime()) {
...@@ -43,22 +42,24 @@ BucketedTimeSeries<VT, TT>::BucketedTimeSeries(size_t nBuckets, ...@@ -43,22 +42,24 @@ BucketedTimeSeries<VT, TT>::BucketedTimeSeries(size_t nBuckets,
} }
} }
template <typename VT, typename TT> template <typename VT, typename CT>
bool BucketedTimeSeries<VT, TT>::addValue(TimeType now, const ValueType& val) { bool BucketedTimeSeries<VT, CT>::addValue(TimeType now, const ValueType& val) {
return addValueAggregated(now, val, 1); return addValueAggregated(now, val, 1);
} }
template <typename VT, typename TT> template <typename VT, typename CT>
bool BucketedTimeSeries<VT, TT>::addValue(TimeType now, bool BucketedTimeSeries<VT, CT>::addValue(
const ValueType& val, TimeType now,
int64_t times) { const ValueType& val,
int64_t times) {
return addValueAggregated(now, val * times, times); return addValueAggregated(now, val * times, times);
} }
template <typename VT, typename TT> template <typename VT, typename CT>
bool BucketedTimeSeries<VT, TT>::addValueAggregated(TimeType now, bool BucketedTimeSeries<VT, CT>::addValueAggregated(
const ValueType& total, TimeType now,
int64_t nsamples) { const ValueType& total,
int64_t nsamples) {
if (isAllTime()) { if (isAllTime()) {
if (UNLIKELY(empty())) { if (UNLIKELY(empty())) {
firstTime_ = now; firstTime_ = now;
...@@ -98,8 +99,8 @@ bool BucketedTimeSeries<VT, TT>::addValueAggregated(TimeType now, ...@@ -98,8 +99,8 @@ bool BucketedTimeSeries<VT, TT>::addValueAggregated(TimeType now,
return true; return true;
} }
template <typename VT, typename TT> template <typename VT, typename CT>
size_t BucketedTimeSeries<VT, TT>::update(TimeType now) { size_t BucketedTimeSeries<VT, CT>::update(TimeType now) {
if (empty()) { if (empty()) {
// This is the first data point. // This is the first data point.
firstTime_ = now; firstTime_ = now;
...@@ -121,8 +122,8 @@ size_t BucketedTimeSeries<VT, TT>::update(TimeType now) { ...@@ -121,8 +122,8 @@ size_t BucketedTimeSeries<VT, TT>::update(TimeType now) {
return updateBuckets(now); return updateBuckets(now);
} }
template <typename VT, typename TT> template <typename VT, typename CT>
size_t BucketedTimeSeries<VT, TT>::updateBuckets(TimeType now) { size_t BucketedTimeSeries<VT, CT>::updateBuckets(TimeType now) {
// We could cache nextBucketStart as a member variable, so we don't have to // 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. // recompute it each time update() is called with a new timestamp value.
// This makes things faster when update() (or addValue()) is called once // This makes things faster when update() (or addValue()) is called once
...@@ -172,8 +173,8 @@ size_t BucketedTimeSeries<VT, TT>::updateBuckets(TimeType now) { ...@@ -172,8 +173,8 @@ size_t BucketedTimeSeries<VT, TT>::updateBuckets(TimeType now) {
} }
} }
template <typename VT, typename TT> template <typename VT, typename CT>
void BucketedTimeSeries<VT, TT>::clear() { void BucketedTimeSeries<VT, CT>::clear() {
for (Bucket& bucket : buckets_) { for (Bucket& bucket : buckets_) {
bucket.clear(); bucket.clear();
} }
...@@ -184,9 +185,8 @@ void BucketedTimeSeries<VT, TT>::clear() { ...@@ -184,9 +185,8 @@ void BucketedTimeSeries<VT, TT>::clear() {
latestTime_ = TimeType(0); latestTime_ = TimeType(0);
} }
template <typename VT, typename CT>
template <typename VT, typename TT> typename CT::duration BucketedTimeSeries<VT, CT>::getEarliestTime() const {
TT BucketedTimeSeries<VT, TT>::getEarliestTime() const {
if (empty()) { if (empty()) {
return TimeType(0); return TimeType(0);
} }
...@@ -203,8 +203,9 @@ TT BucketedTimeSeries<VT, TT>::getEarliestTime() const { ...@@ -203,8 +203,9 @@ TT BucketedTimeSeries<VT, TT>::getEarliestTime() const {
return earliestTime; return earliestTime;
} }
template <typename VT, typename TT> template <typename VT, typename CT>
TT BucketedTimeSeries<VT, TT>::getEarliestTimeNonEmpty() const { typename CT::duration BucketedTimeSeries<VT, CT>::getEarliestTimeNonEmpty()
const {
size_t currentBucket; size_t currentBucket;
TimeType currentBucketStart; TimeType currentBucketStart;
TimeType nextBucketStart; TimeType nextBucketStart;
...@@ -216,8 +217,8 @@ TT BucketedTimeSeries<VT, TT>::getEarliestTimeNonEmpty() const { ...@@ -216,8 +217,8 @@ TT BucketedTimeSeries<VT, TT>::getEarliestTimeNonEmpty() const {
return nextBucketStart - duration_; return nextBucketStart - duration_;
} }
template <typename VT, typename TT> template <typename VT, typename CT>
TT BucketedTimeSeries<VT, TT>::elapsed() const { typename CT::duration BucketedTimeSeries<VT, CT>::elapsed() const {
if (empty()) { if (empty()) {
return TimeType(0); return TimeType(0);
} }
...@@ -226,8 +227,10 @@ TT BucketedTimeSeries<VT, TT>::elapsed() const { ...@@ -226,8 +227,10 @@ TT BucketedTimeSeries<VT, TT>::elapsed() const {
return latestTime_ - getEarliestTime() + TimeType(1); return latestTime_ - getEarliestTime() + TimeType(1);
} }
template <typename VT, typename TT> template <typename VT, typename CT>
TT BucketedTimeSeries<VT, TT>::elapsed(TimeType start, TimeType end) const { typename CT::duration BucketedTimeSeries<VT, CT>::elapsed(
TimeType start,
TimeType end) const {
if (empty()) { if (empty()) {
return TimeType(0); return TimeType(0);
} }
...@@ -237,8 +240,8 @@ TT BucketedTimeSeries<VT, TT>::elapsed(TimeType start, TimeType end) const { ...@@ -237,8 +240,8 @@ TT BucketedTimeSeries<VT, TT>::elapsed(TimeType start, TimeType end) const {
return end - start; return end - start;
} }
template <typename VT, typename TT> template <typename VT, typename CT>
VT BucketedTimeSeries<VT, TT>::sum(TimeType start, TimeType end) const { VT BucketedTimeSeries<VT, CT>::sum(TimeType start, TimeType end) const {
ValueType total = ValueType(); ValueType total = ValueType();
forEachBucket(start, end, [&](const Bucket& bucket, forEachBucket(start, end, [&](const Bucket& bucket,
TimeType bucketStart, TimeType bucketStart,
...@@ -251,8 +254,8 @@ VT BucketedTimeSeries<VT, TT>::sum(TimeType start, TimeType end) const { ...@@ -251,8 +254,8 @@ VT BucketedTimeSeries<VT, TT>::sum(TimeType start, TimeType end) const {
return total; return total;
} }
template <typename VT, typename TT> template <typename VT, typename CT>
uint64_t BucketedTimeSeries<VT, TT>::count(TimeType start, TimeType end) const { uint64_t BucketedTimeSeries<VT, CT>::count(TimeType start, TimeType end) const {
uint64_t sample_count = 0; uint64_t sample_count = 0;
forEachBucket(start, end, [&](const Bucket& bucket, forEachBucket(start, end, [&](const Bucket& bucket,
TimeType bucketStart, TimeType bucketStart,
...@@ -265,9 +268,9 @@ uint64_t BucketedTimeSeries<VT, TT>::count(TimeType start, TimeType end) const { ...@@ -265,9 +268,9 @@ uint64_t BucketedTimeSeries<VT, TT>::count(TimeType start, TimeType end) const {
return sample_count; return sample_count;
} }
template <typename VT, typename TT> template <typename VT, typename CT>
template <typename ReturnType> 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(); ValueType total = ValueType();
uint64_t sample_count = 0; uint64_t sample_count = 0;
forEachBucket(start, end, [&](const Bucket& bucket, forEachBucket(start, end, [&](const Bucket& bucket,
...@@ -304,8 +307,8 @@ ReturnType BucketedTimeSeries<VT, TT>::avg(TimeType start, TimeType end) const { ...@@ -304,8 +307,8 @@ ReturnType BucketedTimeSeries<VT, TT>::avg(TimeType start, TimeType end) const {
* into, we then divide by duration_. * into, we then divide by duration_.
*/ */
template <typename VT, typename TT> template <typename VT, typename CT>
size_t BucketedTimeSeries<VT, TT>::getBucketIdx(TimeType time) const { size_t BucketedTimeSeries<VT, CT>::getBucketIdx(TimeType time) const {
// For all-time data we don't use buckets_. Everything is tracked in total_. // For all-time data we don't use buckets_. Everything is tracked in total_.
DCHECK(!isAllTime()); DCHECK(!isAllTime());
...@@ -317,10 +320,12 @@ size_t BucketedTimeSeries<VT, TT>::getBucketIdx(TimeType time) const { ...@@ -317,10 +320,12 @@ size_t BucketedTimeSeries<VT, TT>::getBucketIdx(TimeType time) const {
* Compute the bucket index for the specified time, as well as the earliest * Compute the bucket index for the specified time, as well as the earliest
* time that falls into this bucket. * time that falls into this bucket.
*/ */
template <typename VT, typename TT> template <typename VT, typename CT>
void BucketedTimeSeries<VT, TT>::getBucketInfo( void BucketedTimeSeries<VT, CT>::getBucketInfo(
TimeType time, size_t *bucketIdx, TimeType time,
TimeType* bucketStart, TimeType* nextBucketStart) const { size_t* bucketIdx,
TimeType* bucketStart,
TimeType* nextBucketStart) const {
typedef typename TimeType::rep TimeInt; typedef typename TimeType::rep TimeInt;
DCHECK(!isAllTime()); DCHECK(!isAllTime());
...@@ -349,9 +354,9 @@ void BucketedTimeSeries<VT, TT>::getBucketInfo( ...@@ -349,9 +354,9 @@ void BucketedTimeSeries<VT, TT>::getBucketInfo(
*nextBucketStart = nextBucketStartMod + durationStart; *nextBucketStart = nextBucketStartMod + durationStart;
} }
template <typename VT, typename TT> template <typename VT, typename CT>
template <typename Function> template <typename Function>
void BucketedTimeSeries<VT, TT>::forEachBucket(Function fn) const { void BucketedTimeSeries<VT, CT>::forEachBucket(Function fn) const {
if (isAllTime()) { if (isAllTime()) {
fn(total_, firstTime_, latestTime_ + TimeType(1)); fn(total_, firstTime_, latestTime_ + TimeType(1));
return; return;
...@@ -417,10 +422,13 @@ void BucketedTimeSeries<VT, TT>::forEachBucket(Function fn) const { ...@@ -417,10 +422,13 @@ void BucketedTimeSeries<VT, TT>::forEachBucket(Function fn) const {
* For example, if the bucket spans time [10, 20), but we only care about the * 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. * range [10, 16), this will return 60% of the input value.
*/ */
template<typename VT, typename TT> template <typename VT, typename CT>
VT BucketedTimeSeries<VT, TT>::rangeAdjust( VT BucketedTimeSeries<VT, CT>::rangeAdjust(
TimeType bucketStart, TimeType nextBucketStart, TimeType bucketStart,
TimeType start, TimeType end, ValueType input) const { TimeType nextBucketStart,
TimeType start,
TimeType end,
ValueType input) const {
// If nextBucketStart is greater than latestTime_, treat nextBucketStart as // If nextBucketStart is greater than latestTime_, treat nextBucketStart as
// if it were latestTime_. This makes us more accurate when someone is // if it were latestTime_. This makes us more accurate when someone is
// querying for all of the data up to latestTime_. Even though latestTime_ // querying for all of the data up to latestTime_. Even though latestTime_
...@@ -442,10 +450,12 @@ VT BucketedTimeSeries<VT, TT>::rangeAdjust( ...@@ -442,10 +450,12 @@ VT BucketedTimeSeries<VT, TT>::rangeAdjust(
(nextBucketStart - bucketStart); (nextBucketStart - bucketStart);
} }
template <typename VT, typename TT> template <typename VT, typename CT>
template <typename Function> template <typename Function>
void BucketedTimeSeries<VT, TT>::forEachBucket(TimeType start, TimeType end, void BucketedTimeSeries<VT, CT>::forEachBucket(
Function fn) const { TimeType start,
TimeType end,
Function fn) const {
forEachBucket([&start, &end, &fn] (const Bucket& bucket, TimeType bucketStart, forEachBucket([&start, &end, &fn] (const Bucket& bucket, TimeType bucketStart,
TimeType nextBucketStart) -> bool { TimeType nextBucketStart) -> bool {
if (start >= nextBucketStart) { if (start >= nextBucketStart) {
......
...@@ -23,6 +23,22 @@ ...@@ -23,6 +23,22 @@
namespace folly { 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 * 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 * added in the recent past, and merges these values together into a fixed
...@@ -44,12 +60,18 @@ namespace folly { ...@@ -44,12 +60,18 @@ namespace folly {
* *
* This class is not thread-safe -- use your own synchronization! * 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 { class BucketedTimeSeries {
public: public:
typedef VT ValueType; using ValueType = VT;
typedef TT TimeType; using Clock = CT;
typedef detail::Bucket<ValueType> Bucket; 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. * Create a new BucketedTimeSeries.
...@@ -61,7 +83,7 @@ class BucketedTimeSeries { ...@@ -61,7 +83,7 @@ class BucketedTimeSeries {
* and does not need the rolling buckets. The numBuckets parameter is * and does not need the rolling buckets. The numBuckets parameter is
* ignored when duration is 0. * ignored when duration is 0.
*/ */
BucketedTimeSeries(size_t numBuckets, TimeType duration); BucketedTimeSeries(size_t numBuckets, Duration duration);
/* /*
* Adds the value 'val' at time 'now' * Adds the value 'val' at time 'now'
......
...@@ -21,30 +21,28 @@ ...@@ -21,30 +21,28 @@
namespace folly { namespace folly {
template <typename VT, typename TT> template <typename VT, typename CT>
MultiLevelTimeSeries<VT, TT>::MultiLevelTimeSeries( MultiLevelTimeSeries<VT, CT>::MultiLevelTimeSeries(
size_t nBuckets, size_t nBuckets,
size_t nLevels, size_t nLevels,
const TimeType levelDurations[]) const TimeType levelDurations[])
: cachedTime_(0), : cachedTime_(0), cachedSum_(0), cachedCount_(0) {
cachedSum_(0), CHECK_GT(nLevels, 0);
cachedCount_(0) { CHECK(levelDurations);
CHECK_GT(nLevels, 0);
CHECK(levelDurations);
levels_.reserve(nLevels); levels_.reserve(nLevels);
for (size_t i = 0; i < nLevels; ++i) { for (size_t i = 0; i < nLevels; ++i) {
if (levelDurations[i] == TT(0)) { if (levelDurations[i] == Duration(0)) {
CHECK_EQ(i, nLevels - 1); CHECK_EQ(i, nLevels - 1);
} else if (i > 0) { } else if (i > 0) {
CHECK(levelDurations[i-1] < levelDurations[i]); CHECK(levelDurations[i - 1] < levelDurations[i]);
}
levels_.emplace_back(nBuckets, levelDurations[i]);
} }
levels_.emplace_back(nBuckets, levelDurations[i]);
}
} }
template <typename VT, typename TT> template <typename VT, typename CT>
MultiLevelTimeSeries<VT, TT>::MultiLevelTimeSeries( MultiLevelTimeSeries<VT, CT>::MultiLevelTimeSeries(
size_t nBuckets, size_t nBuckets,
std::initializer_list<TimeType> durations) std::initializer_list<TimeType> durations)
: cachedTime_(0), cachedSum_(0), cachedCount_(0) { : cachedTime_(0), cachedSum_(0), cachedCount_(0) {
...@@ -54,7 +52,7 @@ MultiLevelTimeSeries<VT, TT>::MultiLevelTimeSeries( ...@@ -54,7 +52,7 @@ MultiLevelTimeSeries<VT, TT>::MultiLevelTimeSeries(
int i = 0; int i = 0;
TimeType prev; TimeType prev;
for (auto dur : durations) { for (auto dur : durations) {
if (dur == TT(0)) { if (dur == Duration(0)) {
CHECK_EQ(i, durations.size() - 1); CHECK_EQ(i, durations.size() - 1);
} else if (i > 0) { } else if (i > 0) {
CHECK(prev < dur); CHECK(prev < dur);
...@@ -65,24 +63,26 @@ MultiLevelTimeSeries<VT, TT>::MultiLevelTimeSeries( ...@@ -65,24 +63,26 @@ MultiLevelTimeSeries<VT, TT>::MultiLevelTimeSeries(
} }
} }
template <typename VT, typename TT> template <typename VT, typename CT>
void MultiLevelTimeSeries<VT, TT>::addValue( void MultiLevelTimeSeries<VT, CT>::addValue(
TimeType now, TimeType now,
const ValueType& val) { const ValueType& val) {
addValueAggregated(now, val, 1); addValueAggregated(now, val, 1);
} }
template <typename VT, typename TT> template <typename VT, typename CT>
void MultiLevelTimeSeries<VT, TT>::addValue(TimeType now, void MultiLevelTimeSeries<VT, CT>::addValue(
const ValueType& val, TimeType now,
int64_t times) { const ValueType& val,
int64_t times) {
addValueAggregated(now, val * times, times); addValueAggregated(now, val * times, times);
} }
template <typename VT, typename TT> template <typename VT, typename CT>
void MultiLevelTimeSeries<VT, TT>::addValueAggregated(TimeType now, void MultiLevelTimeSeries<VT, CT>::addValueAggregated(
const ValueType& total, TimeType now,
int64_t nsamples) { const ValueType& total,
int64_t nsamples) {
if (cachedTime_ != now) { if (cachedTime_ != now) {
flush(); flush();
cachedTime_ = now; cachedTime_ = now;
...@@ -91,16 +91,16 @@ void MultiLevelTimeSeries<VT, TT>::addValueAggregated(TimeType now, ...@@ -91,16 +91,16 @@ void MultiLevelTimeSeries<VT, TT>::addValueAggregated(TimeType now,
cachedCount_ += nsamples; cachedCount_ += nsamples;
} }
template <typename VT, typename TT> template <typename VT, typename CT>
void MultiLevelTimeSeries<VT, TT>::update(TimeType now) { void MultiLevelTimeSeries<VT, CT>::update(TimeType now) {
flush(); flush();
for (size_t i = 0; i < levels_.size(); ++i) { for (size_t i = 0; i < levels_.size(); ++i) {
levels_[i].update(now); levels_[i].update(now);
} }
} }
template <typename VT, typename TT> template <typename VT, typename CT>
void MultiLevelTimeSeries<VT, TT>::flush() { void MultiLevelTimeSeries<VT, CT>::flush() {
// update all the underlying levels // update all the underlying levels
if (cachedCount_ > 0) { if (cachedCount_ > 0) {
for (size_t i = 0; i < levels_.size(); ++i) { for (size_t i = 0; i < levels_.size(); ++i) {
...@@ -111,8 +111,8 @@ void MultiLevelTimeSeries<VT, TT>::flush() { ...@@ -111,8 +111,8 @@ void MultiLevelTimeSeries<VT, TT>::flush() {
} }
} }
template <typename VT, typename TT> template <typename VT, typename CT>
void MultiLevelTimeSeries<VT, TT>::clear() { void MultiLevelTimeSeries<VT, CT>::clear() {
for (auto & level : levels_) { for (auto & level : levels_) {
level.clear(); level.clear();
} }
......
...@@ -49,12 +49,18 @@ namespace folly { ...@@ -49,12 +49,18 @@ namespace folly {
* *
* The class is not thread-safe -- use your own synchronization! * 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 { class MultiLevelTimeSeries {
public: public:
typedef VT ValueType; using ValueType = VT;
typedef TT TimeType; using Clock = CT;
typedef folly::BucketedTimeSeries<ValueType, TimeType> Level; 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. * Create a new MultiLevelTimeSeries.
......
...@@ -23,34 +23,37 @@ ...@@ -23,34 +23,37 @@
namespace folly { namespace folly {
template <typename T, typename TT, typename C> template <typename T, typename CT, typename C>
TimeseriesHistogram<T, TT, C>::TimeseriesHistogram(ValueType bucketSize, TimeseriesHistogram<T, CT, C>::TimeseriesHistogram(
ValueType min, ValueType bucketSize,
ValueType max, ValueType min,
const ContainerType& copyMe) ValueType max,
: buckets_(bucketSize, min, max, copyMe), const ContainerType& copyMe)
haveNotSeenValue_(true), : buckets_(bucketSize, min, max, copyMe),
singleUniqueValue_(false) { haveNotSeenValue_(true),
} singleUniqueValue_(false) {}
template <typename T, typename TT, typename C> template <typename T, typename CT, typename C>
void TimeseriesHistogram<T, TT, C>::addValue(TimeType now, void TimeseriesHistogram<T, CT, C>::addValue(
const ValueType& value) { TimeType now,
const ValueType& value) {
buckets_.getByValue(value).addValue(now, value); buckets_.getByValue(value).addValue(now, value);
maybeHandleSingleUniqueValue(value); maybeHandleSingleUniqueValue(value);
} }
template <typename T, typename TT, typename C> template <typename T, typename CT, typename C>
void TimeseriesHistogram<T, TT, C>::addValue(TimeType now, void TimeseriesHistogram<T, CT, C>::addValue(
const ValueType& value, TimeType now,
int64_t times) { const ValueType& value,
int64_t times) {
buckets_.getByValue(value).addValue(now, value, times); buckets_.getByValue(value).addValue(now, value, times);
maybeHandleSingleUniqueValue(value); maybeHandleSingleUniqueValue(value);
} }
template <typename T, typename TT, typename C> template <typename T, typename CT, typename C>
void TimeseriesHistogram<T, TT, C>::addValues( void TimeseriesHistogram<T, CT, C>::addValues(
TimeType now, const folly::Histogram<ValueType>& hist) { TimeType now,
const folly::Histogram<ValueType>& hist) {
CHECK_EQ(hist.getMin(), getMin()); CHECK_EQ(hist.getMin(), getMin());
CHECK_EQ(hist.getMax(), getMax()); CHECK_EQ(hist.getMax(), getMax());
CHECK_EQ(hist.getBucketSize(), getBucketSize()); CHECK_EQ(hist.getBucketSize(), getBucketSize());
...@@ -68,9 +71,9 @@ void TimeseriesHistogram<T, TT, C>::addValues( ...@@ -68,9 +71,9 @@ void TimeseriesHistogram<T, TT, C>::addValues(
singleUniqueValue_ = false; singleUniqueValue_ = false;
} }
template <typename T, typename TT, typename C> template <typename T, typename CT, typename C>
void TimeseriesHistogram<T, TT, C>::maybeHandleSingleUniqueValue( void TimeseriesHistogram<T, CT, C>::maybeHandleSingleUniqueValue(
const ValueType& value) { const ValueType& value) {
if (haveNotSeenValue_) { if (haveNotSeenValue_) {
firstValue_ = value; firstValue_ = value;
singleUniqueValue_ = true; singleUniqueValue_ = true;
...@@ -82,9 +85,9 @@ void TimeseriesHistogram<T, TT, C>::maybeHandleSingleUniqueValue( ...@@ -82,9 +85,9 @@ void TimeseriesHistogram<T, TT, C>::maybeHandleSingleUniqueValue(
} }
} }
template <typename T, typename TT, typename C> template <typename T, typename CT, typename C>
T TimeseriesHistogram<T, TT, C>::getPercentileEstimate(double pct, T TimeseriesHistogram<T, CT, C>::getPercentileEstimate(double pct, int level)
int level) const { const {
if (singleUniqueValue_) { if (singleUniqueValue_) {
return firstValue_; return firstValue_;
} }
...@@ -93,10 +96,11 @@ T TimeseriesHistogram<T, TT, C>::getPercentileEstimate(double pct, ...@@ -93,10 +96,11 @@ T TimeseriesHistogram<T, TT, C>::getPercentileEstimate(double pct,
AvgFromLevel(level)); AvgFromLevel(level));
} }
template <typename T, typename TT, typename C> template <typename T, typename CT, typename C>
T TimeseriesHistogram<T, TT, C>::getPercentileEstimate(double pct, T TimeseriesHistogram<T, CT, C>::getPercentileEstimate(
TimeType start, double pct,
TimeType end) const { TimeType start,
TimeType end) const {
if (singleUniqueValue_) { if (singleUniqueValue_) {
return firstValue_; return firstValue_;
} }
...@@ -106,38 +110,37 @@ T TimeseriesHistogram<T, TT, C>::getPercentileEstimate(double pct, ...@@ -106,38 +110,37 @@ T TimeseriesHistogram<T, TT, C>::getPercentileEstimate(double pct,
AvgFromInterval<T>(start, end)); AvgFromInterval<T>(start, end));
} }
template <typename T, typename TT, typename C> template <typename T, typename CT, typename C>
int TimeseriesHistogram<T, TT, C>::getPercentileBucketIdx( int TimeseriesHistogram<T, CT, C>::getPercentileBucketIdx(double pct, int level)
double pct, const {
int level
) const {
return buckets_.getPercentileBucketIdx(pct / 100.0, CountFromLevel(level)); return buckets_.getPercentileBucketIdx(pct / 100.0, CountFromLevel(level));
} }
template <typename T, typename TT, typename C> template <typename T, typename CT, typename C>
int TimeseriesHistogram<T, TT, C>::getPercentileBucketIdx(double pct, int TimeseriesHistogram<T, CT, C>::getPercentileBucketIdx(
TimeType start, double pct,
TimeType end) const { TimeType start,
TimeType end) const {
return buckets_.getPercentileBucketIdx(pct / 100.0, return buckets_.getPercentileBucketIdx(pct / 100.0,
CountFromInterval(start, end)); CountFromInterval(start, end));
} }
template <typename T, typename TT, typename C> template <typename T, typename CT, typename C>
void TimeseriesHistogram<T, TT, C>::clear() { void TimeseriesHistogram<T, CT, C>::clear() {
for (size_t i = 0; i < buckets_.getNumBuckets(); i++) { for (size_t i = 0; i < buckets_.getNumBuckets(); i++) {
buckets_.getByIndex(i).clear(); buckets_.getByIndex(i).clear();
} }
} }
template <typename T, typename TT, typename C> template <typename T, typename CT, typename C>
void TimeseriesHistogram<T, TT, C>::update(TimeType now) { void TimeseriesHistogram<T, CT, C>::update(TimeType now) {
for (size_t i = 0; i < buckets_.getNumBuckets(); i++) { for (size_t i = 0; i < buckets_.getNumBuckets(); i++) {
buckets_.getByIndex(i).update(now); buckets_.getByIndex(i).update(now);
} }
} }
template <typename T, typename TT, typename C> template <typename T, typename CT, typename C>
std::string TimeseriesHistogram<T, TT, C>::getString(int level) const { std::string TimeseriesHistogram<T, CT, C>::getString(int level) const {
std::string result; std::string result;
for (size_t i = 0; i < buckets_.getNumBuckets(); i++) { for (size_t i = 0; i < buckets_.getNumBuckets(); i++) {
...@@ -153,9 +156,10 @@ std::string TimeseriesHistogram<T, TT, C>::getString(int level) const { ...@@ -153,9 +156,10 @@ std::string TimeseriesHistogram<T, TT, C>::getString(int level) const {
return result; return result;
} }
template <typename T, typename TT, typename C> template <typename T, typename CT, typename C>
std::string TimeseriesHistogram<T, TT, C>::getString(TimeType start, std::string TimeseriesHistogram<T, CT, C>::getString(
TimeType end) const { TimeType start,
TimeType end) const {
std::string result; std::string result;
for (size_t i = 0; i < buckets_.getNumBuckets(); i++) { for (size_t i = 0; i < buckets_.getNumBuckets(); i++) {
...@@ -171,8 +175,8 @@ std::string TimeseriesHistogram<T, TT, C>::getString(TimeType start, ...@@ -171,8 +175,8 @@ std::string TimeseriesHistogram<T, TT, C>::getString(TimeType start,
return result; return result;
} }
template <class T, class TT, class C> template <class T, class CT, class C>
void TimeseriesHistogram<T, TT, C>::computeAvgData( void TimeseriesHistogram<T, CT, C>::computeAvgData(
ValueType* total, ValueType* total,
int64_t* nsamples, int64_t* nsamples,
int level) const { int level) const {
...@@ -183,8 +187,8 @@ void TimeseriesHistogram<T, TT, C>::computeAvgData( ...@@ -183,8 +187,8 @@ void TimeseriesHistogram<T, TT, C>::computeAvgData(
} }
} }
template <class T, class TT, class C> template <class T, class CT, class C>
void TimeseriesHistogram<T, TT, C>::computeAvgData( void TimeseriesHistogram<T, CT, C>::computeAvgData(
ValueType* total, ValueType* total,
int64_t* nsamples, int64_t* nsamples,
TimeType start, TimeType start,
...@@ -196,8 +200,8 @@ void TimeseriesHistogram<T, TT, C>::computeAvgData( ...@@ -196,8 +200,8 @@ void TimeseriesHistogram<T, TT, C>::computeAvgData(
} }
} }
template <typename T, typename TT, typename C> template <typename T, typename CT, typename C>
void TimeseriesHistogram<T, TT, C>::computeRateData( void TimeseriesHistogram<T, CT, C>::computeRateData(
ValueType* total, ValueType* total,
TimeType* elapsed, TimeType* elapsed,
int level) const { int level) const {
...@@ -208,8 +212,8 @@ void TimeseriesHistogram<T, TT, C>::computeRateData( ...@@ -208,8 +212,8 @@ void TimeseriesHistogram<T, TT, C>::computeRateData(
} }
} }
template <class T, class TT, class C> template <class T, class CT, class C>
void TimeseriesHistogram<T, TT, C>::computeRateData( void TimeseriesHistogram<T, CT, C>::computeRateData(
ValueType* total, ValueType* total,
TimeType* elapsed, TimeType* elapsed,
TimeType start, TimeType start,
......
...@@ -47,20 +47,28 @@ namespace folly { ...@@ -47,20 +47,28 @@ namespace folly {
* The memory usage for a typical histogram is roughly 3k * (# of buckets). All * 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). * insertion operations are amortized O(1), and all queries are O(# of buckets).
*/ */
template <class T, class TT=std::chrono::seconds, template <
class C=folly::MultiLevelTimeSeries<T, TT>> class T,
class CT = LegacyStatsClock<std::chrono::seconds>,
class C = folly::MultiLevelTimeSeries<T, CT>>
class TimeseriesHistogram { class TimeseriesHistogram {
private: private:
// NOTE: T must be equivalent to _signed_ numeric type for our math. // NOTE: T must be equivalent to _signed_ numeric type for our math.
static_assert(std::numeric_limits<T>::is_signed, ""); static_assert(std::numeric_limits<T>::is_signed, "");
public: public:
// values to be inserted into container // Values to be inserted into container
typedef T ValueType; using ValueType = T;
// the container type we use internally for each bucket // The container type we use internally for each bucket
typedef C ContainerType; using ContainerType = C;
// The time type. // Clock, duration, and time point types
typedef TT TimeType; 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. * Create a TimeSeries histogram and initialize the bucketing and levels.
......
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