blob: ba2b75a29262605c74cb76cdf66d9c81219d8ac8 [file] [log] [blame]
Sameer Abu Asala8439542013-02-14 16:06:42 -08001/*
2 * Copyright (C) 2013 The Android Open Source Project
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
David Sehrc431b9d2018-03-02 12:01:51 -080017#ifndef ART_LIBARTBASE_BASE_HISTOGRAM_INL_H_
18#define ART_LIBARTBASE_BASE_HISTOGRAM_INL_H_
Sameer Abu Asala8439542013-02-14 16:06:42 -080019
Sameer Abu Asala8439542013-02-14 16:06:42 -080020#include <algorithm>
21#include <cmath>
22#include <limits>
23#include <ostream>
24
Vladimir Marko80afd022015-05-19 18:08:00 +010025#include "histogram.h"
26
Andreas Gampe57943812017-12-06 21:39:13 -080027#include <android-base/logging.h>
28
David Sehr1979c642018-04-26 14:41:18 -070029#include "bit_utils.h"
30#include "time_utils.h"
31#include "utils.h"
Vladimir Marko80afd022015-05-19 18:08:00 +010032
Sameer Abu Asala8439542013-02-14 16:06:42 -080033namespace art {
34
35template <class Value> inline void Histogram<Value>::AddValue(Value value) {
Mathieu Chartiere5426c92013-08-01 13:55:42 -070036 CHECK_GE(value, static_cast<Value>(0));
Sameer Abu Asala8439542013-02-14 16:06:42 -080037 if (value >= max_) {
38 Value new_max = ((value + 1) / bucket_width_ + 1) * bucket_width_;
39 DCHECK_GT(new_max, max_);
40 GrowBuckets(new_max);
41 }
Sameer Abu Asala8439542013-02-14 16:06:42 -080042 BucketiseValue(value);
Sameer Abu Asala8439542013-02-14 16:06:42 -080043}
44
Mathieu Chartier70a596d2014-12-17 14:56:47 -080045template <class Value> inline void Histogram<Value>::AdjustAndAddValue(Value value) {
46 AddValue(value / kAdjust);
47}
48
Mathieu Chartier19b0a912013-11-20 14:07:54 -080049template <class Value> inline Histogram<Value>::Histogram(const char* name)
50 : kAdjust(0),
51 kInitialBucketCount(0),
52 name_(name),
Andreas Gamped9911ee2017-03-27 13:27:24 -070053 max_buckets_(0),
54 sample_size_(0) {
Mathieu Chartier19b0a912013-11-20 14:07:54 -080055}
56
Sameer Abu Asala8439542013-02-14 16:06:42 -080057template <class Value>
Mathieu Chartiere5426c92013-08-01 13:55:42 -070058inline Histogram<Value>::Histogram(const char* name, Value initial_bucket_width,
59 size_t max_buckets)
Sameer Abu Asala8439542013-02-14 16:06:42 -080060 : kAdjust(1000),
Hans Boehm19736872019-04-22 10:20:22 -070061 kInitialBucketCount(kMinBuckets),
Mathieu Chartiere5426c92013-08-01 13:55:42 -070062 name_(name),
63 max_buckets_(max_buckets),
64 bucket_width_(initial_bucket_width) {
Hans Boehm19736872019-04-22 10:20:22 -070065 CHECK_GE(max_buckets, kInitialBucketCount);
66 CHECK_EQ(max_buckets_ % 2, 0u);
Sameer Abu Asala8439542013-02-14 16:06:42 -080067 Reset();
68}
69
70template <class Value>
71inline void Histogram<Value>::GrowBuckets(Value new_max) {
72 while (max_ < new_max) {
Mathieu Chartiere5426c92013-08-01 13:55:42 -070073 // If we have reached the maximum number of buckets, merge buckets together.
Hans Boehm19736872019-04-22 10:20:22 -070074 DCHECK_LE(frequency_.size(), max_buckets_);
75 if (frequency_.size() == max_buckets_) {
76 DCHECK_EQ(frequency_.size() % 2, 0u);
Mathieu Chartiere5426c92013-08-01 13:55:42 -070077 // We double the width of each bucket to reduce the number of buckets by a factor of 2.
78 bucket_width_ *= 2;
79 const size_t limit = frequency_.size() / 2;
80 // Merge the frequencies by adding each adjacent two together.
81 for (size_t i = 0; i < limit; ++i) {
82 frequency_[i] = frequency_[i * 2] + frequency_[i * 2 + 1];
83 }
84 // Remove frequencies in the second half of the array which were added to the first half.
85 while (frequency_.size() > limit) {
86 frequency_.pop_back();
87 }
88 }
Sameer Abu Asala8439542013-02-14 16:06:42 -080089 max_ += bucket_width_;
Sameer Abu Asala8439542013-02-14 16:06:42 -080090 frequency_.push_back(0);
Sameer Abu Asala8439542013-02-14 16:06:42 -080091 }
92}
93
Mathieu Chartiere5426c92013-08-01 13:55:42 -070094template <class Value> inline size_t Histogram<Value>::FindBucket(Value val) const {
Sameer Abu Asala8439542013-02-14 16:06:42 -080095 // Since this is only a linear histogram, bucket index can be found simply with
96 // dividing the value by the bucket width.
97 DCHECK_GE(val, min_);
98 DCHECK_LE(val, max_);
Mathieu Chartiere5426c92013-08-01 13:55:42 -070099 const size_t bucket_idx = static_cast<size_t>((val - min_) / bucket_width_);
Sameer Abu Asala8439542013-02-14 16:06:42 -0800100 DCHECK_GE(bucket_idx, 0ul);
Mathieu Chartiere5426c92013-08-01 13:55:42 -0700101 DCHECK_LE(bucket_idx, GetBucketCount());
Sameer Abu Asala8439542013-02-14 16:06:42 -0800102 return bucket_idx;
103}
104
105template <class Value>
Mathieu Chartiere5426c92013-08-01 13:55:42 -0700106inline void Histogram<Value>::BucketiseValue(Value val) {
107 CHECK_LT(val, max_);
108 sum_ += val;
109 sum_of_squares_ += val * val;
110 ++sample_size_;
111 ++frequency_[FindBucket(val)];
112 max_value_added_ = std::max(val, max_value_added_);
113 min_value_added_ = std::min(val, min_value_added_);
Sameer Abu Asala8439542013-02-14 16:06:42 -0800114}
115
116template <class Value> inline void Histogram<Value>::Initialize() {
Mathieu Chartiere5426c92013-08-01 13:55:42 -0700117 for (size_t idx = 0; idx < kInitialBucketCount; idx++) {
Sameer Abu Asala8439542013-02-14 16:06:42 -0800118 frequency_.push_back(0);
119 }
120 // Cumulative frequency and ranges has a length of 1 over frequency.
Mathieu Chartiere5426c92013-08-01 13:55:42 -0700121 max_ = bucket_width_ * GetBucketCount();
122}
123
124template <class Value> inline size_t Histogram<Value>::GetBucketCount() const {
125 return frequency_.size();
Sameer Abu Asala8439542013-02-14 16:06:42 -0800126}
127
128template <class Value> inline void Histogram<Value>::Reset() {
Sameer Abu Asala8439542013-02-14 16:06:42 -0800129 sum_of_squares_ = 0;
130 sample_size_ = 0;
131 min_ = 0;
132 sum_ = 0;
133 min_value_added_ = std::numeric_limits<Value>::max();
134 max_value_added_ = std::numeric_limits<Value>::min();
Sameer Abu Asala8439542013-02-14 16:06:42 -0800135 frequency_.clear();
Sameer Abu Asala8439542013-02-14 16:06:42 -0800136 Initialize();
137}
138
Mathieu Chartiere5426c92013-08-01 13:55:42 -0700139template <class Value> inline Value Histogram<Value>::GetRange(size_t bucket_idx) const {
140 DCHECK_LE(bucket_idx, GetBucketCount());
141 return min_ + bucket_idx * bucket_width_;
Sameer Abu Asala8439542013-02-14 16:06:42 -0800142}
143
144template <class Value> inline double Histogram<Value>::Mean() const {
145 DCHECK_GT(sample_size_, 0ull);
146 return static_cast<double>(sum_) / static_cast<double>(sample_size_);
147}
148
149template <class Value> inline double Histogram<Value>::Variance() const {
150 DCHECK_GT(sample_size_, 0ull);
151 // Using algorithms for calculating variance over a population:
152 // http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
153 Value sum_squared = sum_ * sum_;
154 double sum_squared_by_n_squared =
155 static_cast<double>(sum_squared) /
156 static_cast<double>(sample_size_ * sample_size_);
157 double sum_of_squares_by_n =
158 static_cast<double>(sum_of_squares_) / static_cast<double>(sample_size_);
159 return sum_of_squares_by_n - sum_squared_by_n_squared;
160}
161
162template <class Value>
Mathieu Chartiere5426c92013-08-01 13:55:42 -0700163inline void Histogram<Value>::PrintBins(std::ostream& os, const CumulativeData& data) const {
Sameer Abu Asala8439542013-02-14 16:06:42 -0800164 DCHECK_GT(sample_size_, 0ull);
Mathieu Chartiere5426c92013-08-01 13:55:42 -0700165 for (size_t bin_idx = 0; bin_idx < data.freq_.size(); ++bin_idx) {
166 if (bin_idx > 0 && data.perc_[bin_idx] == data.perc_[bin_idx - 1]) {
Sameer Abu Asala8439542013-02-14 16:06:42 -0800167 bin_idx++;
168 continue;
169 }
Mathieu Chartiere5426c92013-08-01 13:55:42 -0700170 os << GetRange(bin_idx) << ": " << data.freq_[bin_idx] << "\t"
171 << data.perc_[bin_idx] * 100.0 << "%\n";
Sameer Abu Asala8439542013-02-14 16:06:42 -0800172 }
173}
174
175template <class Value>
Hiroshi Yamauchia1c9f012015-04-02 10:18:12 -0700176inline void Histogram<Value>::DumpBins(std::ostream& os) const {
177 DCHECK_GT(sample_size_, 0ull);
178 bool dumped_one = false;
179 for (size_t bin_idx = 0; bin_idx < frequency_.size(); ++bin_idx) {
180 if (frequency_[bin_idx] != 0U) {
181 if (dumped_one) {
182 // Prepend a comma if not the first bin.
183 os << ",";
184 } else {
185 dumped_one = true;
186 }
187 os << GetRange(bin_idx) << ":" << frequency_[bin_idx];
188 }
189 }
190}
191
192template <class Value>
Mathieu Chartiere5426c92013-08-01 13:55:42 -0700193inline void Histogram<Value>::PrintConfidenceIntervals(std::ostream &os, double interval,
194 const CumulativeData& data) const {
Mathieu Chartierf5997b42014-06-20 10:37:54 -0700195 static constexpr size_t kFractionalDigits = 3;
Sameer Abu Asala8439542013-02-14 16:06:42 -0800196 DCHECK_GT(interval, 0);
197 DCHECK_LT(interval, 1.0);
Mathieu Chartierf5997b42014-06-20 10:37:54 -0700198 const double per_0 = (1.0 - interval) / 2.0;
199 const double per_1 = per_0 + interval;
200 const TimeUnit unit = GetAppropriateTimeUnit(Mean() * kAdjust);
201 os << Name() << ":\tSum: " << PrettyDuration(Sum() * kAdjust) << " "
202 << (interval * 100) << "% C.I. " << FormatDuration(Percentile(per_0, data) * kAdjust, unit,
203 kFractionalDigits)
204 << "-" << FormatDuration(Percentile(per_1, data) * kAdjust, unit, kFractionalDigits) << " "
205 << "Avg: " << FormatDuration(Mean() * kAdjust, unit, kFractionalDigits) << " Max: "
Andreas Gampe5544e722017-06-05 17:01:27 -0700206 << FormatDuration(Max() * kAdjust, unit, kFractionalDigits) << std::endl;
Sameer Abu Asala8439542013-02-14 16:06:42 -0800207}
208
Mathieu Chartierb2f99362013-11-20 17:26:00 -0800209template <class Value>
Nicolas Geoffraya4f81542016-03-08 16:57:48 +0000210inline void Histogram<Value>::PrintMemoryUse(std::ostream &os) const {
Pavel Vyssotski3e80aeb2016-05-17 16:37:53 +0600211 os << Name();
212 if (sample_size_ != 0u) {
213 os << ": Avg: " << PrettySize(Mean()) << " Max: "
214 << PrettySize(Max()) << " Min: " << PrettySize(Min()) << "\n";
215 } else {
216 os << ": <no data>\n";
217 }
Nicolas Geoffraya4f81542016-03-08 16:57:48 +0000218}
219
220template <class Value>
Mathieu Chartierb2f99362013-11-20 17:26:00 -0800221inline void Histogram<Value>::CreateHistogram(CumulativeData* out_data) const {
Mathieu Chartiere5426c92013-08-01 13:55:42 -0700222 DCHECK_GT(sample_size_, 0ull);
Ian Rogers500793f2013-11-14 17:49:12 -0800223 out_data->freq_.clear();
224 out_data->perc_.clear();
Sameer Abu Asala8439542013-02-14 16:06:42 -0800225 uint64_t accumulated = 0;
Ian Rogers500793f2013-11-14 17:49:12 -0800226 out_data->freq_.push_back(accumulated);
227 out_data->perc_.push_back(0.0);
Sameer Abu Asala8439542013-02-14 16:06:42 -0800228 for (size_t idx = 0; idx < frequency_.size(); idx++) {
229 accumulated += frequency_[idx];
Ian Rogers500793f2013-11-14 17:49:12 -0800230 out_data->freq_.push_back(accumulated);
231 out_data->perc_.push_back(static_cast<double>(accumulated) / static_cast<double>(sample_size_));
Sameer Abu Asala8439542013-02-14 16:06:42 -0800232 }
Ian Rogers500793f2013-11-14 17:49:12 -0800233 DCHECK_EQ(out_data->freq_.back(), sample_size_);
234 DCHECK_LE(std::abs(out_data->perc_.back() - 1.0), 0.001);
Sameer Abu Asala8439542013-02-14 16:06:42 -0800235}
Sameer Abu Asala8439542013-02-14 16:06:42 -0800236
Ian Rogers647b1a82014-10-10 11:02:11 -0700237#pragma clang diagnostic push
238#pragma clang diagnostic ignored "-Wfloat-equal"
Ian Rogers647b1a82014-10-10 11:02:11 -0700239
Sameer Abu Asala8439542013-02-14 16:06:42 -0800240template <class Value>
Mathieu Chartiere5426c92013-08-01 13:55:42 -0700241inline double Histogram<Value>::Percentile(double per, const CumulativeData& data) const {
242 DCHECK_GT(data.perc_.size(), 0ull);
243 size_t upper_idx = 0, lower_idx = 0;
244 for (size_t idx = 0; idx < data.perc_.size(); idx++) {
245 if (per <= data.perc_[idx]) {
Sameer Abu Asalc081e362013-02-20 16:45:38 -0800246 upper_idx = idx;
Sameer Abu Asala8439542013-02-14 16:06:42 -0800247 break;
Sameer Abu Asalc081e362013-02-20 16:45:38 -0800248 }
249
Mathieu Chartiere5426c92013-08-01 13:55:42 -0700250 if (per >= data.perc_[idx] && idx != 0 && data.perc_[idx] != data.perc_[idx - 1]) {
Sameer Abu Asalc081e362013-02-20 16:45:38 -0800251 lower_idx = idx;
252 }
Sameer Abu Asala8439542013-02-14 16:06:42 -0800253 }
Sameer Abu Asalc081e362013-02-20 16:45:38 -0800254
Mathieu Chartiere5426c92013-08-01 13:55:42 -0700255 const double lower_perc = data.perc_[lower_idx];
256 const double lower_value = static_cast<double>(GetRange(lower_idx));
Sameer Abu Asala8439542013-02-14 16:06:42 -0800257 if (per == lower_perc) {
258 return lower_value;
259 }
Mathieu Chartiere5426c92013-08-01 13:55:42 -0700260
261 const double upper_perc = data.perc_[upper_idx];
262 const double upper_value = static_cast<double>(GetRange(upper_idx));
Sameer Abu Asala8439542013-02-14 16:06:42 -0800263 if (per == upper_perc) {
264 return upper_value;
265 }
266 DCHECK_GT(upper_perc, lower_perc);
Sameer Abu Asalc081e362013-02-20 16:45:38 -0800267
Sameer Abu Asala8439542013-02-14 16:06:42 -0800268 double value = lower_value + (upper_value - lower_value) *
269 (per - lower_perc) / (upper_perc - lower_perc);
Sameer Abu Asal857a0782013-02-21 11:00:20 -0800270
271 if (value < min_value_added_) {
272 value = min_value_added_;
273 } else if (value > max_value_added_) {
274 value = max_value_added_;
275 }
276
Sameer Abu Asala8439542013-02-14 16:06:42 -0800277 return value;
278}
279
Ian Rogers647b1a82014-10-10 11:02:11 -0700280#pragma clang diagnostic pop
Ian Rogers647b1a82014-10-10 11:02:11 -0700281
Sameer Abu Asalc081e362013-02-20 16:45:38 -0800282} // namespace art
David Sehrc431b9d2018-03-02 12:01:51 -0800283#endif // ART_LIBARTBASE_BASE_HISTOGRAM_INL_H_