/Users/deen/code/yugabyte-db/src/yb/rocksdb/util/histogram.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
2 | | // This source code is licensed under the BSD-style license found in the |
3 | | // LICENSE file in the root directory of this source tree. An additional grant |
4 | | // of patent rights can be found in the PATENTS file in the same directory. |
5 | | // |
6 | | // The following only applies to changes made to this file as part of YugaByte development. |
7 | | // |
8 | | // Portions Copyright (c) YugaByte, Inc. |
9 | | // |
10 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
11 | | // in compliance with the License. You may obtain a copy of the License at |
12 | | // |
13 | | // http://www.apache.org/licenses/LICENSE-2.0 |
14 | | // |
15 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
16 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
17 | | // or implied. See the License for the specific language governing permissions and limitations |
18 | | // under the License. |
19 | | // |
20 | | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. |
21 | | // Use of this source code is governed by a BSD-style license that can be |
22 | | // found in the LICENSE file. See the AUTHORS file for names of contributors. |
23 | | |
24 | | #include "yb/rocksdb/util/histogram.h" |
25 | | |
26 | | #include <math.h> |
27 | | |
28 | | #include "yb/gutil/port.h" |
29 | | |
30 | | namespace rocksdb { |
31 | | |
32 | | HistogramBucketMapper::HistogramBucketMapper() |
33 | | : |
34 | | // Add newer bucket index here. |
35 | | // Should be always added in sorted order. |
36 | | // If you change this, you also need to change the size of array buckets_ and the initial |
37 | | // value of min_ in HistogramImpl. |
38 | | bucketValues_( |
39 | | {1, 2, 3, 4, 5, 6, |
40 | | 7, 8, 9, 10, 12, 14, |
41 | | 16, 18, 20, 25, 30, 35, |
42 | | 40, 45, 50, 60, 70, 80, |
43 | | 90, 100, 120, 140, 160, 180, |
44 | | 200, 250, 300, 350, 400, 450, |
45 | | 500, 600, 700, 800, 900, 1000, |
46 | | 1200, 1400, 1600, 1800, 2000, 2500, |
47 | | 3000, 3500, 4000, 4500, 5000, 6000, |
48 | | 7000, 8000, 9000, 10000, 12000, 14000, |
49 | | 16000, 18000, 20000, 25000, 30000, 35000, |
50 | | 40000, 45000, 50000, 60000, 70000, 80000, |
51 | | 90000, 100000, 120000, 140000, 160000, 180000, |
52 | | 200000, 250000, 300000, 350000, 400000, 450000, |
53 | | 500000, 600000, 700000, 800000, 900000, 1000000, |
54 | | 1200000, 1400000, 1600000, 1800000, 2000000, 2500000, |
55 | | 3000000, 3500000, 4000000, 4500000, 5000000, 6000000, |
56 | | 7000000, 8000000, 9000000, 10000000, 12000000, 14000000, |
57 | | 16000000, 18000000, 20000000, 25000000, 30000000, 35000000, |
58 | | 40000000, 45000000, 50000000, 60000000, 70000000, 80000000, |
59 | | 90000000, 100000000, 120000000, 140000000, 160000000, 180000000, |
60 | | 200000000, 250000000, 300000000, 350000000, 400000000, 450000000, |
61 | | 500000000, 600000000, 700000000, 800000000, 900000000, 1000000000}), |
62 | | maxBucketValue_(bucketValues_.back()), |
63 | 28.1k | minBucketValue_(bucketValues_.front()) { |
64 | 28.1k | assert(kHistogramNumBuckets == BucketCount()); |
65 | 3.90M | for (size_t i =0; i < bucketValues_.size(); ++i3.88M ) { |
66 | 3.88M | valueIndexMap_[bucketValues_[i]] = i; |
67 | 3.88M | } |
68 | 28.1k | } |
69 | | |
70 | 5.08M | size_t HistogramBucketMapper::IndexForValue(const uint64_t value) const { |
71 | 5.08M | if (value >= maxBucketValue_) { |
72 | 2 | return bucketValues_.size() - 1; |
73 | 5.08M | } else if ( value >= minBucketValue_ ) { |
74 | 3.78M | std::map<uint64_t, uint64_t>::const_iterator lowerBound = |
75 | 3.78M | valueIndexMap_.lower_bound(value); |
76 | 3.78M | if (lowerBound != valueIndexMap_.end()3.78M ) { |
77 | 3.78M | return static_cast<size_t>(lowerBound->second); |
78 | 18.4E | } else { |
79 | 18.4E | return 0; |
80 | 18.4E | } |
81 | 3.78M | } else { |
82 | 1.30M | return 0; |
83 | 1.30M | } |
84 | 5.08M | } |
85 | | |
86 | | namespace { |
87 | | const HistogramBucketMapper bucketMapper; |
88 | | } |
89 | | |
90 | 37 | void HistogramImpl::Clear() { |
91 | 37 | set_min(static_cast<double>(bucketMapper.LastValue())); |
92 | 37 | set_max(0); |
93 | 37 | num_ = 0; |
94 | 37 | sum_ = 0; |
95 | 37 | sum_squares_ = 0; |
96 | 5.14k | for (int i = 0; i < kHistogramNumBuckets; ++i5.10k ) { |
97 | 5.10k | set_bucket(i, 0); |
98 | 5.10k | } |
99 | 37 | } |
100 | | |
101 | 28 | bool HistogramImpl::Empty() { return num_ == 0; } |
102 | | |
103 | 5.08M | void HistogramImpl::Add(uint64_t value) { |
104 | 5.08M | update_min(value); |
105 | 5.08M | update_max(value); |
106 | | |
107 | 5.08M | add_to_num(1); |
108 | 5.08M | add_to_sum(value); |
109 | 5.08M | add_to_sum_squares(static_cast<double>(value) * value); |
110 | | |
111 | | // TODO: maybe we should use int for IndexForValue's return value? |
112 | | // There are only 200 or so buckets. |
113 | 5.08M | add_to_bucket(static_cast<int>(bucketMapper.IndexForValue(value)), 1); |
114 | 5.08M | } |
115 | | |
116 | 0 | void HistogramImpl::Merge(const HistogramImpl& other) { |
117 | 0 | update_min(other.min()); |
118 | 0 | update_max(other.max()); |
119 | 0 | add_to_num(other.num_); |
120 | 0 | add_to_sum(other.sum_); |
121 | 0 | add_to_sum_squares(other.sum_squares_); |
122 | 0 | for (unsigned int b = 0; b < bucketMapper.BucketCount(); b++) { |
123 | 0 | add_to_bucket(b, other.bucket(b)); |
124 | 0 | } |
125 | 0 | } |
126 | | |
127 | 3.89k | double HistogramImpl::Median() const { |
128 | 3.89k | return Percentile(50.0); |
129 | 3.89k | } |
130 | | |
131 | 23.3k | double HistogramImpl::Percentile(double p) const { |
132 | 23.3k | double threshold = num_ * (p / 100.0); |
133 | 23.3k | double sum = 0; |
134 | 23.3k | const double current_min = min(); |
135 | 23.3k | const double current_max = max(); |
136 | 138k | for (unsigned int b = 0; b < bucketMapper.BucketCount(); b++114k ) { |
137 | 138k | sum += buckets_[b]; |
138 | 138k | if (sum >= threshold) { |
139 | | // Scale linearly within this bucket |
140 | 23.3k | double left_point = |
141 | 23.3k | static_cast<double>((b == 0) ? 016.1k : bucketMapper.BucketLimit(b-1)7.22k ); |
142 | 23.3k | double right_point = |
143 | 23.3k | static_cast<double>(bucketMapper.BucketLimit(b)); |
144 | 23.3k | double left_sum = sum - bucket(b); |
145 | 23.3k | double right_sum = sum; |
146 | 23.3k | double pos = 0; |
147 | 23.3k | double right_left_diff = right_sum - left_sum; |
148 | 23.3k | if (right_left_diff != 0) { |
149 | 23.3k | pos = (threshold - left_sum) / (right_sum - left_sum); |
150 | 23.3k | } |
151 | 23.3k | double r = left_point + (right_point - left_point) * pos; |
152 | 23.3k | if (r < current_min) r = current_min15.0k ; |
153 | 23.3k | if (r > current_max) r = current_max2.99k ; |
154 | 23.3k | return r; |
155 | 23.3k | } |
156 | 138k | } |
157 | 0 | return current_max; |
158 | 23.3k | } |
159 | | |
160 | 3.91k | double HistogramImpl::Average() const { |
161 | 3.91k | if (num_ == 0.0) return 02 ; |
162 | 3.91k | return sum_ / num_; |
163 | 3.91k | } |
164 | | |
165 | 3.89k | double HistogramImpl::StandardDeviation() const { |
166 | 3.89k | if (num_ == 0.0) return 00 ; |
167 | 3.89k | double variance = (sum_squares_ * num_ - sum_ * sum_) / (num_ * num_); |
168 | 3.89k | return sqrt(variance); |
169 | 3.89k | } |
170 | | |
171 | 3.89k | std::string HistogramImpl::ToString() const { |
172 | 3.89k | std::string r; |
173 | 3.89k | char buf[200]; |
174 | 3.89k | auto current_num = num(); |
175 | 3.89k | snprintf(buf, sizeof(buf), |
176 | 3.89k | "Count: %.0f Average: %.4f StdDev: %.2f\n", |
177 | 3.89k | static_cast<double>(current_num), Average(), StandardDeviation()); |
178 | 3.89k | r.append(buf); |
179 | 3.89k | snprintf(buf, sizeof(buf), |
180 | 3.89k | "Min: %.4f Median: %.4f Max: %.4f\n", |
181 | 3.89k | (current_num == 0.0 ? 0.00 : min()), Median(), max()); |
182 | 3.89k | r.append(buf); |
183 | 3.89k | snprintf(buf, sizeof(buf), |
184 | 3.89k | "Percentiles: " |
185 | 3.89k | "P50: %.2f P75: %.2f P99: %.2f P99.9: %.2f P99.99: %.2f\n", |
186 | 3.89k | Percentile(50), Percentile(75), Percentile(99), Percentile(99.9), |
187 | 3.89k | Percentile(99.99)); |
188 | 3.89k | r.append(buf); |
189 | 3.89k | r.append("------------------------------------------------------\n"); |
190 | 3.89k | const double mult = 100.0 / current_num; |
191 | 3.89k | double sum = 0; |
192 | 540k | for (unsigned int b = 0; b < bucketMapper.BucketCount(); b++536k ) { |
193 | 536k | const auto bucket_value = bucket(b); |
194 | 536k | if (bucket_value <= 0) continue530k ; |
195 | 6.70k | sum += bucket_value; |
196 | 6.70k | snprintf(buf, sizeof(buf), |
197 | 6.70k | "[ %7" PRIu64 ", %7" PRIu64 " ) %8" PRIu64 " %7.3f%% %7.3f%% ", |
198 | 6.70k | b == 0 ? 03.03k : bucketMapper.BucketLimit(b - 1)3.66k , // left |
199 | 6.70k | bucketMapper.BucketLimit(b), // right |
200 | 6.70k | bucket_value, // count |
201 | 6.70k | mult * bucket_value, // percentage |
202 | 6.70k | mult * sum); // cumulative percentage |
203 | 6.70k | r.append(buf); |
204 | | |
205 | | // Add hash marks based on percentage; 20 marks for 100%. |
206 | 6.70k | int marks = static_cast<int>(20*(bucket_value / current_num) + 0.5); |
207 | 6.70k | r.append(marks, '#'); |
208 | 6.70k | r.push_back('\n'); |
209 | 6.70k | } |
210 | 3.89k | return r; |
211 | 3.89k | } |
212 | | |
213 | 0 | void HistogramImpl::Data(HistogramData * const data) const { |
214 | 0 | assert(data); |
215 | 0 | data->count = num_; |
216 | 0 | data->sum = sum_; |
217 | 0 | data->min = min(); |
218 | 0 | data->max = max(); |
219 | 0 | data->median = Median(); |
220 | 0 | data->percentile95 = Percentile(95); |
221 | 0 | data->percentile99 = Percentile(99); |
222 | 0 | data->average = Average(); |
223 | 0 | data->standard_deviation = StandardDeviation(); |
224 | 0 | } |
225 | | |
226 | | } // namespace rocksdb |