1/*
2 * Copyright 2013-present Facebook, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
19#include <folly/Conv.h>
20#include <folly/stats/BucketedTimeSeries-defs.h>
21#include <folly/stats/Histogram-defs.h>
22#include <folly/stats/MultiLevelTimeSeries-defs.h>
23#include <folly/stats/TimeseriesHistogram.h>
24
25namespace folly {
26
27template <typename T, typename CT, typename C>
28TimeseriesHistogram<T, CT, C>::TimeseriesHistogram(
29 ValueType bucketSize,
30 ValueType min,
31 ValueType max,
32 const ContainerType& copyMe)
33 : buckets_(bucketSize, min, max, copyMe),
34 haveNotSeenValue_(true),
35 singleUniqueValue_(false) {}
36
37template <typename T, typename CT, typename C>
38void TimeseriesHistogram<T, CT, C>::addValue(
39 TimePoint now,
40 const ValueType& value) {
41 buckets_.getByValue(value).addValue(now, value);
42 maybeHandleSingleUniqueValue(value);
43}
44
45template <typename T, typename CT, typename C>
46void TimeseriesHistogram<T, CT, C>::addValue(
47 TimePoint now,
48 const ValueType& value,
49 uint64_t times) {
50 buckets_.getByValue(value).addValue(now, value, times);
51 maybeHandleSingleUniqueValue(value);
52}
53
54template <typename T, typename CT, typename C>
55void TimeseriesHistogram<T, CT, C>::addValues(
56 TimePoint now,
57 const folly::Histogram<ValueType>& hist) {
58 CHECK_EQ(hist.getMin(), getMin());
59 CHECK_EQ(hist.getMax(), getMax());
60 CHECK_EQ(hist.getBucketSize(), getBucketSize());
61 CHECK_EQ(hist.getNumBuckets(), getNumBuckets());
62
63 for (size_t n = 0; n < hist.getNumBuckets(); ++n) {
64 const typename folly::Histogram<ValueType>::Bucket& histBucket =
65 hist.getBucketByIndex(n);
66 Bucket& myBucket = buckets_.getByIndex(n);
67 myBucket.addValueAggregated(now, histBucket.sum, histBucket.count);
68 }
69
70 // We don't bother with the singleUniqueValue_ tracking.
71 haveNotSeenValue_ = false;
72 singleUniqueValue_ = false;
73}
74
75template <typename T, typename CT, typename C>
76void TimeseriesHistogram<T, CT, C>::maybeHandleSingleUniqueValue(
77 const ValueType& value) {
78 if (haveNotSeenValue_) {
79 firstValue_ = value;
80 singleUniqueValue_ = true;
81 haveNotSeenValue_ = false;
82 } else if (singleUniqueValue_) {
83 if (value != firstValue_) {
84 singleUniqueValue_ = false;
85 }
86 }
87}
88
89template <typename T, typename CT, typename C>
90T TimeseriesHistogram<T, CT, C>::getPercentileEstimate(double pct, size_t level)
91 const {
92 if (singleUniqueValue_) {
93 return firstValue_;
94 }
95
96 return buckets_.getPercentileEstimate(
97 pct / 100.0, CountFromLevel(level), AvgFromLevel(level));
98}
99
100template <typename T, typename CT, typename C>
101T TimeseriesHistogram<T, CT, C>::getPercentileEstimate(
102 double pct,
103 TimePoint start,
104 TimePoint end) const {
105 if (singleUniqueValue_) {
106 return firstValue_;
107 }
108
109 return buckets_.getPercentileEstimate(
110 pct / 100.0,
111 CountFromInterval(start, end),
112 AvgFromInterval<T>(start, end));
113}
114
115template <typename T, typename CT, typename C>
116size_t TimeseriesHistogram<T, CT, C>::getPercentileBucketIdx(
117 double pct,
118 size_t level) const {
119 return buckets_.getPercentileBucketIdx(pct / 100.0, CountFromLevel(level));
120}
121
122template <typename T, typename CT, typename C>
123size_t TimeseriesHistogram<T, CT, C>::getPercentileBucketIdx(
124 double pct,
125 TimePoint start,
126 TimePoint end) const {
127 return buckets_.getPercentileBucketIdx(
128 pct / 100.0, CountFromInterval(start, end));
129}
130
131template <typename T, typename CT, typename C>
132void TimeseriesHistogram<T, CT, C>::clear() {
133 for (size_t i = 0; i < buckets_.getNumBuckets(); i++) {
134 buckets_.getByIndex(i).clear();
135 }
136}
137
138template <typename T, typename CT, typename C>
139void TimeseriesHistogram<T, CT, C>::update(TimePoint now) {
140 for (size_t i = 0; i < buckets_.getNumBuckets(); i++) {
141 buckets_.getByIndex(i).update(now);
142 }
143}
144
145template <typename T, typename CT, typename C>
146std::string TimeseriesHistogram<T, CT, C>::getString(size_t level) const {
147 std::string result;
148
149 for (size_t i = 0; i < buckets_.getNumBuckets(); i++) {
150 if (i > 0) {
151 toAppend(",", &result);
152 }
153 const ContainerType& cont = buckets_.getByIndex(i);
154 toAppend(
155 buckets_.getBucketMin(i),
156 ":",
157 cont.count(level),
158 ":",
159 cont.template avg<ValueType>(level),
160 &result);
161 }
162
163 return result;
164}
165
166template <typename T, typename CT, typename C>
167std::string TimeseriesHistogram<T, CT, C>::getString(
168 TimePoint start,
169 TimePoint end) const {
170 std::string result;
171
172 for (size_t i = 0; i < buckets_.getNumBuckets(); i++) {
173 if (i > 0) {
174 toAppend(",", &result);
175 }
176 const ContainerType& cont = buckets_.getByIndex(i);
177 toAppend(
178 buckets_.getBucketMin(i),
179 ":",
180 cont.count(start, end),
181 ":",
182 cont.avg(start, end),
183 &result);
184 }
185
186 return result;
187}
188
189template <class T, class CT, class C>
190void TimeseriesHistogram<T, CT, C>::computeAvgData(
191 ValueType* total,
192 uint64_t* nsamples,
193 size_t level) const {
194 for (size_t b = 0; b < buckets_.getNumBuckets(); ++b) {
195 const auto& levelObj = buckets_.getByIndex(b).getLevel(level);
196 *total += levelObj.sum();
197 *nsamples += levelObj.count();
198 }
199}
200
201template <class T, class CT, class C>
202void TimeseriesHistogram<T, CT, C>::computeAvgData(
203 ValueType* total,
204 uint64_t* nsamples,
205 TimePoint start,
206 TimePoint end) const {
207 for (size_t b = 0; b < buckets_.getNumBuckets(); ++b) {
208 const auto& levelObj = buckets_.getByIndex(b).getLevel(start);
209 *total += levelObj.sum(start, end);
210 *nsamples += levelObj.count(start, end);
211 }
212}
213
214template <typename T, typename CT, typename C>
215void TimeseriesHistogram<T, CT, C>::computeRateData(
216 ValueType* total,
217 Duration* elapsed,
218 size_t level) const {
219 for (size_t b = 0; b < buckets_.getNumBuckets(); ++b) {
220 const auto& levelObj = buckets_.getByIndex(b).getLevel(level);
221 *total += levelObj.sum();
222 *elapsed = std::max(*elapsed, levelObj.elapsed());
223 }
224}
225
226template <class T, class CT, class C>
227void TimeseriesHistogram<T, CT, C>::computeRateData(
228 ValueType* total,
229 Duration* elapsed,
230 TimePoint start,
231 TimePoint end) const {
232 for (size_t b = 0; b < buckets_.getNumBuckets(); ++b) {
233 const auto& level = buckets_.getByIndex(b).getLevel(start);
234 *total += level.sum(start, end);
235 *elapsed = std::max(*elapsed, level.elapsed(start, end));
236 }
237}
238
239} // namespace folly
240