/Users/deen/code/yugabyte-db/src/yb/util/hdr_histogram.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | // |
18 | | // The following only applies to changes made to this file as part of YugaByte development. |
19 | | // |
20 | | // Portions Copyright (c) YugaByte, Inc. |
21 | | // |
22 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
23 | | // in compliance with the License. You may obtain a copy of the License at |
24 | | // |
25 | | // http://www.apache.org/licenses/LICENSE-2.0 |
26 | | // |
27 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
28 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
29 | | // or implied. See the License for the specific language governing permissions and limitations |
30 | | // under the License. |
31 | | // |
32 | | #ifndef YB_UTIL_HDR_HISTOGRAM_H |
33 | | #define YB_UTIL_HDR_HISTOGRAM_H |
34 | | |
35 | | // C++ (TR1) port of HdrHistogram. |
36 | | // Original java implementation: http://giltene.github.io/HdrHistogram/ |
37 | | // |
38 | | // A High Dynamic Range (HDR) Histogram |
39 | | // |
40 | | // HdrHistogram supports the recording and analyzing sampled data value counts |
41 | | // across a configurable integer value range with configurable value precision |
42 | | // within the range. Value precision is expressed as the number of significant |
43 | | // digits in the value recording, and provides control over value quantization |
44 | | // behavior across the value range and the subsequent value resolution at any |
45 | | // given level. |
46 | | // |
47 | | // For example, a Histogram could be configured to track the counts of observed |
48 | | // integer values between 0 and 3,600,000,000 while maintaining a value |
49 | | // precision of 3 significant digits across that range. Value quantization |
50 | | // within the range will thus be no larger than 1/1,000th (or 0.1%) of any |
51 | | // value. This example Histogram could be used to track and analyze the counts |
52 | | // of observed response times ranging between 1 microsecond and 1 hour in |
53 | | // magnitude, while maintaining a value resolution of 1 microsecond up to 1 |
54 | | // millisecond, a resolution of 1 millisecond (or better) up to one second, and |
55 | | // a resolution of 1 second (or better) up to 1,000 seconds. At it's maximum |
56 | | // tracked value (1 hour), it would still maintain a resolution of 3.6 seconds |
57 | | // (or better). |
58 | | |
59 | | #include <iosfwd> |
60 | | #include <memory> |
61 | | |
62 | | #include "yb/gutil/atomicops.h" |
63 | | #include "yb/gutil/macros.h" |
64 | | |
65 | | #include "yb/util/status_fwd.h" |
66 | | |
67 | | namespace yb { |
68 | | |
69 | | class AbstractHistogramIterator; |
70 | | class Status; |
71 | | class RecordedValuesIterator; |
72 | | |
73 | | // This implementation allows you to specify a range and accuracy (significant |
74 | | // digits) to support in an instance of a histogram. The class takes care of |
75 | | // the rest. At this time, only uint64_t values are supported. |
76 | | // |
77 | | // An HdrHistogram consists of a set of buckets, which bucket the magnitude of |
78 | | // a value stored, and a set of sub-buckets, which implement the tunable |
79 | | // precision of the storage. So if you specify 3 significant digits of |
80 | | // precision, then you will get about 10^3 sub-buckets (as a power of 2) for |
81 | | // each level of magnitude. Magnitude buckets are tracked in powers of 2. |
82 | | // |
83 | | // This class is thread-safe. |
84 | | class HdrHistogram { |
85 | | public: |
86 | | // Specify the highest trackable value so that the class has a bound on the |
87 | | // number of buckets, and # of significant digits (in decimal) so that the |
88 | | // class can determine the granularity of those buckets. |
89 | | HdrHistogram(uint64_t highest_trackable_value, int num_significant_digits); |
90 | | |
91 | | // Copy-construct a (non-consistent) snapshot of other. |
92 | | explicit HdrHistogram(const HdrHistogram& other); |
93 | | |
94 | | // Validate your params before trying to construct the object. |
95 | | static bool IsValidHighestTrackableValue(uint64_t highest_trackable_value); |
96 | | static bool IsValidNumSignificantDigits(int num_significant_digits); |
97 | | |
98 | | // Record new data. |
99 | | void Increment(int64_t value); |
100 | | void IncrementBy(int64_t value, int64_t count); |
101 | | |
102 | | // Record new data, correcting for "coordinated omission". |
103 | | // |
104 | | // See https://groups.google.com/d/msg/mechanical-sympathy/icNZJejUHfE/BfDekfBEs_sJ |
105 | | // for more details. |
106 | | void IncrementWithExpectedInterval(int64_t value, |
107 | | int64_t expected_interval_between_samples); |
108 | | |
109 | | // Fetch configuration params. |
110 | 4 | uint64_t highest_trackable_value() const { return highest_trackable_value_; } |
111 | 0 | int num_significant_digits() const { return num_significant_digits_; } |
112 | | |
113 | | // Get indexes into histogram based on value. |
114 | | int BucketIndex(uint64_t value) const; |
115 | | int SubBucketIndex(uint64_t value, int bucket_index) const; |
116 | | |
117 | | // Count of all events recorded. |
118 | 7.47M | uint64_t TotalCount() const { return base::subtle::NoBarrier_Load(&total_count_); } |
119 | | |
120 | | // Count of all events recorded since last Reset. Resets to 0 after |
121 | | // ResetPercentiles. |
122 | 1.42G | uint64_t CurrentCount() const { |
123 | 1.42G | return base::subtle::NoBarrier_Load(¤t_count_); |
124 | 1.42G | } |
125 | | |
126 | | // Sum of all events recorded. |
127 | 5.33M | uint64_t TotalSum() const { return base::subtle::NoBarrier_Load(&total_sum_); } |
128 | | |
129 | | // Sum of all events recorded since last Reset. Resets to 0 after |
130 | | // ResetPercentiles. |
131 | 170k | uint64_t CurrentSum() const { |
132 | 170k | return base::subtle::NoBarrier_Load(¤t_sum_); |
133 | 170k | } |
134 | | |
135 | | // Return number of items at index. |
136 | | uint64_t CountAt(int bucket_index, int sub_bucket_index) const; |
137 | | |
138 | | // Return count of values in bucket with values equivalent to value. |
139 | | uint64_t CountInBucketForValue(uint64_t) const; |
140 | | |
141 | | // Return representative value based on index. |
142 | | static uint64_t ValueFromIndex(int bucket_index, int sub_bucket_index); |
143 | | |
144 | | // Get the size (in value units) of the range of values that are equivalent |
145 | | // to the given value within the histogram's resolution. Where "equivalent" |
146 | | // means that value samples recorded for any two equivalent values are |
147 | | // counted in a common total count. |
148 | | uint64_t SizeOfEquivalentValueRange(uint64_t value) const; |
149 | | |
150 | | // Get the lowest value that is equivalent to the given value within the |
151 | | // histogram's resolution. Where "equivalent" means that value samples |
152 | | // recorded for any two equivalent values are counted in a common total |
153 | | // count. |
154 | | uint64_t LowestEquivalentValue(uint64_t value) const; |
155 | | |
156 | | // Get the highest value that is equivalent to the given value within the |
157 | | // histogram's resolution. |
158 | | uint64_t HighestEquivalentValue(uint64_t value) const; |
159 | | |
160 | | // Get a value that lies in the middle (rounded up) of the range of values |
161 | | // equivalent the given value. |
162 | | uint64_t MedianEquivalentValue(uint64_t value) const; |
163 | | |
164 | | // Get the next value that is not equivalent to the given value within the |
165 | | // histogram's resolution. |
166 | | uint64_t NextNonEquivalentValue(uint64_t value) const; |
167 | | |
168 | | // Determine if two values are equivalent with the histogram's resolution. |
169 | | bool ValuesAreEquivalent(uint64_t value1, uint64_t value2) const; |
170 | | |
171 | | // Get the exact minimum value (may lie outside the histogram). |
172 | | uint64_t MinValue() const; |
173 | | |
174 | | // Get the exact maximum value (may lie outside the histogram). |
175 | | uint64_t MaxValue() const; |
176 | | |
177 | | // Get the exact mean value of all recorded values in the histogram. |
178 | | double MeanValue() const; |
179 | | |
180 | | // Get the value at a given percentile. |
181 | | // This is a percentile in percents, i.e. 99.99 percentile. |
182 | | uint64_t ValueAtPercentile(double percentile) const; |
183 | | |
184 | | // Resets the counts_ array to reset the percentiles information. |
185 | | // Preserves the values for TotalSum and TotalCount. |
186 | | void ResetPercentiles(); |
187 | | |
188 | | // Get the percentile at a given value |
189 | | // TODO: implement |
190 | | // double PercentileAtOrBelowValue(uint64_t value) const; |
191 | | |
192 | | // Get the count of recorded values within a range of value levels. |
193 | | // (inclusive to within the histogram's resolution) |
194 | | // TODO: implement |
195 | | // uint64_t CountBetweenValues(uint64_t low_value, uint64_t high_value) const; |
196 | | |
197 | | // Dump a formatted, multiline string describing this histogram to 'out'. |
198 | | void DumpHumanReadable(std::ostream* out) const; |
199 | | |
200 | | private: |
201 | | friend class AbstractHistogramIterator; |
202 | | |
203 | | static const uint64_t kMinHighestTrackableValue = 2; |
204 | | static const int kMinValidNumSignificantDigits = 1; |
205 | | static const int kMaxValidNumSignificantDigits = 5; |
206 | | |
207 | | void Init(); |
208 | | int CountsArrayIndex(int bucket_index, int sub_bucket_index) const; |
209 | | |
210 | | uint64_t highest_trackable_value_; |
211 | | int num_significant_digits_; |
212 | | int counts_array_length_; |
213 | | int bucket_count_; |
214 | | int sub_bucket_count_; |
215 | | |
216 | | // "Hot" fields in the write path. |
217 | | uint8_t sub_bucket_half_count_magnitude_; |
218 | | int sub_bucket_half_count_; |
219 | | uint32_t sub_bucket_mask_; |
220 | | |
221 | | // Also hot. |
222 | | // Non-resetting sum and counts. |
223 | | base::subtle::Atomic64 total_count_; |
224 | | base::subtle::Atomic64 total_sum_; |
225 | | // Resetting values |
226 | | base::subtle::Atomic64 current_count_; |
227 | | base::subtle::Atomic64 current_sum_; |
228 | | base::subtle::Atomic64 min_value_; |
229 | | base::subtle::Atomic64 max_value_; |
230 | | std::unique_ptr<base::subtle::Atomic64[]> counts_; |
231 | | |
232 | | HdrHistogram& operator=(const HdrHistogram& other); // Disable assignment operator. |
233 | | }; |
234 | | |
235 | | // Value returned from iterators. |
236 | | struct HistogramIterationValue { |
237 | | HistogramIterationValue() |
238 | | : value_iterated_to(0), |
239 | | value_iterated_from(0), |
240 | | count_at_value_iterated_to(0), |
241 | | count_added_in_this_iteration_step(0), |
242 | | total_count_to_this_value(0), |
243 | | total_value_to_this_value(0), |
244 | | percentile(0.0), |
245 | 101 | percentile_level_iterated_to(0.0) { |
246 | 101 | } |
247 | | |
248 | 0 | void Reset() { |
249 | 0 | value_iterated_to = 0; |
250 | 0 | value_iterated_from = 0; |
251 | 0 | count_at_value_iterated_to = 0; |
252 | 0 | count_added_in_this_iteration_step = 0; |
253 | 0 | total_count_to_this_value = 0; |
254 | 0 | total_value_to_this_value = 0; |
255 | 0 | percentile = 0.0; |
256 | 0 | percentile_level_iterated_to = 0.0; |
257 | 0 | } |
258 | | |
259 | | uint64_t value_iterated_to; |
260 | | uint64_t value_iterated_from; |
261 | | uint64_t count_at_value_iterated_to; |
262 | | uint64_t count_added_in_this_iteration_step; |
263 | | uint64_t total_count_to_this_value; |
264 | | uint64_t total_value_to_this_value; |
265 | | double percentile; |
266 | | double percentile_level_iterated_to; |
267 | | }; |
268 | | |
269 | | // Base class for iterating through histogram values. |
270 | | // |
271 | | // The underlying histogram must not be modified or destroyed while this class |
272 | | // is iterating over it. |
273 | | // |
274 | | // This class is not thread-safe. |
275 | | class AbstractHistogramIterator { |
276 | | public: |
277 | | // Create iterator with new histogram. |
278 | | // The histogram must not be mutated while the iterator is in use. |
279 | | explicit AbstractHistogramIterator(const HdrHistogram* histogram); |
280 | 1 | virtual ~AbstractHistogramIterator() { |
281 | 1 | } |
282 | | |
283 | | // Returns true if the iteration has more elements. |
284 | | virtual bool HasNext() const; |
285 | | |
286 | | // Returns the next element in the iteration. |
287 | | CHECKED_STATUS Next(HistogramIterationValue* value); |
288 | | |
289 | | virtual double PercentileIteratedTo() const; |
290 | | virtual double PercentileIteratedFrom() const; |
291 | | uint64_t ValueIteratedTo() const; |
292 | | |
293 | | protected: |
294 | | // Implementations must override these methods. |
295 | | virtual void IncrementIterationLevel() = 0; |
296 | | virtual bool ReachedIterationLevel() const = 0; |
297 | | |
298 | | const HdrHistogram* histogram_; |
299 | | HistogramIterationValue cur_iter_val_; |
300 | | |
301 | | uint64_t histogram_total_count_; |
302 | | |
303 | | int current_bucket_index_; |
304 | | int current_sub_bucket_index_; |
305 | | uint64_t current_value_at_index_; |
306 | | |
307 | | int next_bucket_index_; |
308 | | int next_sub_bucket_index_; |
309 | | uint64_t next_value_at_index_; |
310 | | |
311 | | uint64_t prev_value_iterated_to_; |
312 | | uint64_t total_count_to_prev_index_; |
313 | | |
314 | | uint64_t total_count_to_current_index_; |
315 | | uint64_t total_value_to_current_index_; |
316 | | |
317 | | uint64_t count_at_this_value_; |
318 | | |
319 | | private: |
320 | | bool ExhaustedSubBuckets() const; |
321 | | void IncrementSubBucket(); |
322 | | |
323 | | bool fresh_sub_bucket_; |
324 | | |
325 | | DISALLOW_COPY_AND_ASSIGN(AbstractHistogramIterator); |
326 | | }; |
327 | | |
328 | | // Used for iterating through all recorded histogram values using the finest |
329 | | // granularity steps supported by the underlying representation. The iteration |
330 | | // steps through all non-zero recorded value counts, and terminates when all |
331 | | // recorded histogram values are exhausted. |
332 | | // |
333 | | // The underlying histogram must not be modified or destroyed while this class |
334 | | // is iterating over it. |
335 | | // |
336 | | // This class is not thread-safe. |
337 | | class RecordedValuesIterator : public AbstractHistogramIterator { |
338 | | public: |
339 | | explicit RecordedValuesIterator(const HdrHistogram* histogram); |
340 | | |
341 | | protected: |
342 | | virtual void IncrementIterationLevel() override; |
343 | | virtual bool ReachedIterationLevel() const override; |
344 | | |
345 | | private: |
346 | | int visited_sub_bucket_index_; |
347 | | int visited_bucket_index_; |
348 | | |
349 | | DISALLOW_COPY_AND_ASSIGN(RecordedValuesIterator); |
350 | | }; |
351 | | |
352 | | // Used for iterating through histogram values according to percentile levels. |
353 | | // The iteration is performed in steps that start at 0% and reduce their |
354 | | // distance to 100% according to the percentileTicksPerHalfDistance parameter, |
355 | | // ultimately reaching 100% when all recorded histogram values are exhausted. |
356 | | // |
357 | | // The underlying histogram must not be modified or destroyed while this class |
358 | | // is iterating over it. |
359 | | // |
360 | | // This class is not thread-safe. |
361 | | class PercentileIterator : public AbstractHistogramIterator { |
362 | | public: |
363 | | // TODO: Explain percentile_ticks_per_half_distance. |
364 | | PercentileIterator(const HdrHistogram* histogram, |
365 | | int percentile_ticks_per_half_distance); |
366 | | virtual bool HasNext() const override; |
367 | | virtual double PercentileIteratedTo() const override; |
368 | | virtual double PercentileIteratedFrom() const override; |
369 | | |
370 | | protected: |
371 | | virtual void IncrementIterationLevel() override; |
372 | | virtual bool ReachedIterationLevel() const override; |
373 | | |
374 | | private: |
375 | | int percentile_ticks_per_half_distance_; |
376 | | double percentile_level_to_iterate_to_; |
377 | | double percentile_level_to_iterate_from_; |
378 | | bool reached_last_recorded_value_; |
379 | | |
380 | | DISALLOW_COPY_AND_ASSIGN(PercentileIterator); |
381 | | }; |
382 | | |
383 | | } // namespace yb |
384 | | |
385 | | #endif // YB_UTIL_HDR_HISTOGRAM_H |