You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
284 lines
9.2 KiB
284 lines
9.2 KiB
/*
|
|
* Copyright (C) 2013 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#ifndef ART_LIBARTBASE_BASE_HISTOGRAM_INL_H_
|
|
#define ART_LIBARTBASE_BASE_HISTOGRAM_INL_H_
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <limits>
|
|
#include <ostream>
|
|
|
|
#include "histogram.h"
|
|
|
|
#include <android-base/logging.h>
|
|
|
|
#include "bit_utils.h"
|
|
#include "time_utils.h"
|
|
#include "utils.h"
|
|
|
|
namespace art {
|
|
|
|
template <class Value> inline void Histogram<Value>::AddValue(Value value) {
|
|
CHECK_GE(value, static_cast<Value>(0));
|
|
if (value >= max_) {
|
|
Value new_max = ((value + 1) / bucket_width_ + 1) * bucket_width_;
|
|
DCHECK_GT(new_max, max_);
|
|
GrowBuckets(new_max);
|
|
}
|
|
BucketiseValue(value);
|
|
}
|
|
|
|
template <class Value> inline void Histogram<Value>::AdjustAndAddValue(Value value) {
|
|
AddValue(value / kAdjust);
|
|
}
|
|
|
|
template <class Value> inline Histogram<Value>::Histogram(const char* name)
|
|
: kAdjust(0),
|
|
kInitialBucketCount(0),
|
|
name_(name),
|
|
max_buckets_(0),
|
|
sample_size_(0) {
|
|
}
|
|
|
|
template <class Value>
|
|
inline Histogram<Value>::Histogram(const char* name, Value initial_bucket_width,
|
|
size_t max_buckets)
|
|
: kAdjust(1000),
|
|
kInitialBucketCount(kMinBuckets),
|
|
name_(name),
|
|
max_buckets_(max_buckets),
|
|
bucket_width_(initial_bucket_width) {
|
|
CHECK_GE(max_buckets, kInitialBucketCount);
|
|
CHECK_EQ(max_buckets_ % 2, 0u);
|
|
Reset();
|
|
}
|
|
|
|
template <class Value>
|
|
inline void Histogram<Value>::GrowBuckets(Value new_max) {
|
|
while (max_ < new_max) {
|
|
// If we have reached the maximum number of buckets, merge buckets together.
|
|
DCHECK_LE(frequency_.size(), max_buckets_);
|
|
if (frequency_.size() == max_buckets_) {
|
|
DCHECK_EQ(frequency_.size() % 2, 0u);
|
|
// We double the width of each bucket to reduce the number of buckets by a factor of 2.
|
|
bucket_width_ *= 2;
|
|
const size_t limit = frequency_.size() / 2;
|
|
// Merge the frequencies by adding each adjacent two together.
|
|
for (size_t i = 0; i < limit; ++i) {
|
|
frequency_[i] = frequency_[i * 2] + frequency_[i * 2 + 1];
|
|
}
|
|
// Remove frequencies in the second half of the array which were added to the first half.
|
|
while (frequency_.size() > limit) {
|
|
frequency_.pop_back();
|
|
}
|
|
}
|
|
max_ += bucket_width_;
|
|
frequency_.push_back(0);
|
|
}
|
|
}
|
|
|
|
template <class Value> inline size_t Histogram<Value>::FindBucket(Value val) const {
|
|
// Since this is only a linear histogram, bucket index can be found simply with
|
|
// dividing the value by the bucket width.
|
|
DCHECK_GE(val, min_);
|
|
DCHECK_LE(val, max_);
|
|
const size_t bucket_idx = static_cast<size_t>((val - min_) / bucket_width_);
|
|
DCHECK_GE(bucket_idx, 0ul);
|
|
DCHECK_LE(bucket_idx, GetBucketCount());
|
|
return bucket_idx;
|
|
}
|
|
|
|
template <class Value>
|
|
inline void Histogram<Value>::BucketiseValue(Value val) {
|
|
CHECK_LT(val, max_);
|
|
sum_ += val;
|
|
sum_of_squares_ += val * val;
|
|
++sample_size_;
|
|
++frequency_[FindBucket(val)];
|
|
max_value_added_ = std::max(val, max_value_added_);
|
|
min_value_added_ = std::min(val, min_value_added_);
|
|
}
|
|
|
|
template <class Value> inline void Histogram<Value>::Initialize() {
|
|
for (size_t idx = 0; idx < kInitialBucketCount; idx++) {
|
|
frequency_.push_back(0);
|
|
}
|
|
// Cumulative frequency and ranges has a length of 1 over frequency.
|
|
max_ = bucket_width_ * GetBucketCount();
|
|
}
|
|
|
|
template <class Value> inline size_t Histogram<Value>::GetBucketCount() const {
|
|
return frequency_.size();
|
|
}
|
|
|
|
template <class Value> inline void Histogram<Value>::Reset() {
|
|
sum_of_squares_ = 0;
|
|
sample_size_ = 0;
|
|
min_ = 0;
|
|
sum_ = 0;
|
|
min_value_added_ = std::numeric_limits<Value>::max();
|
|
max_value_added_ = std::numeric_limits<Value>::min();
|
|
frequency_.clear();
|
|
Initialize();
|
|
}
|
|
|
|
template <class Value> inline Value Histogram<Value>::GetRange(size_t bucket_idx) const {
|
|
DCHECK_LE(bucket_idx, GetBucketCount());
|
|
return min_ + bucket_idx * bucket_width_;
|
|
}
|
|
|
|
template <class Value> inline double Histogram<Value>::Mean() const {
|
|
DCHECK_GT(sample_size_, 0ull);
|
|
return static_cast<double>(sum_) / static_cast<double>(sample_size_);
|
|
}
|
|
|
|
template <class Value> inline double Histogram<Value>::Variance() const {
|
|
DCHECK_GT(sample_size_, 0ull);
|
|
// Using algorithms for calculating variance over a population:
|
|
// http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
|
|
Value sum_squared = sum_ * sum_;
|
|
double sum_squared_by_n_squared =
|
|
static_cast<double>(sum_squared) /
|
|
static_cast<double>(sample_size_ * sample_size_);
|
|
double sum_of_squares_by_n =
|
|
static_cast<double>(sum_of_squares_) / static_cast<double>(sample_size_);
|
|
return sum_of_squares_by_n - sum_squared_by_n_squared;
|
|
}
|
|
|
|
template <class Value>
|
|
inline void Histogram<Value>::PrintBins(std::ostream& os, const CumulativeData& data) const {
|
|
DCHECK_GT(sample_size_, 0ull);
|
|
for (size_t bin_idx = 0; bin_idx < data.freq_.size(); ++bin_idx) {
|
|
if (bin_idx > 0 && data.perc_[bin_idx] == data.perc_[bin_idx - 1]) {
|
|
bin_idx++;
|
|
continue;
|
|
}
|
|
os << GetRange(bin_idx) << ": " << data.freq_[bin_idx] << "\t"
|
|
<< data.perc_[bin_idx] * 100.0 << "%\n";
|
|
}
|
|
}
|
|
|
|
template <class Value>
|
|
inline void Histogram<Value>::DumpBins(std::ostream& os) const {
|
|
DCHECK_GT(sample_size_, 0ull);
|
|
bool dumped_one = false;
|
|
for (size_t bin_idx = 0; bin_idx < frequency_.size(); ++bin_idx) {
|
|
if (frequency_[bin_idx] != 0U) {
|
|
if (dumped_one) {
|
|
// Prepend a comma if not the first bin.
|
|
os << ",";
|
|
} else {
|
|
dumped_one = true;
|
|
}
|
|
os << GetRange(bin_idx) << ":" << frequency_[bin_idx];
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class Value>
|
|
inline void Histogram<Value>::PrintConfidenceIntervals(std::ostream &os, double interval,
|
|
const CumulativeData& data) const {
|
|
static constexpr size_t kFractionalDigits = 3;
|
|
DCHECK_GT(interval, 0);
|
|
DCHECK_LT(interval, 1.0);
|
|
const double per_0 = (1.0 - interval) / 2.0;
|
|
const double per_1 = per_0 + interval;
|
|
const TimeUnit unit = GetAppropriateTimeUnit(Mean() * kAdjust);
|
|
os << Name() << ":\tSum: " << PrettyDuration(Sum() * kAdjust) << " "
|
|
<< (interval * 100) << "% C.I. " << FormatDuration(Percentile(per_0, data) * kAdjust, unit,
|
|
kFractionalDigits)
|
|
<< "-" << FormatDuration(Percentile(per_1, data) * kAdjust, unit, kFractionalDigits) << " "
|
|
<< "Avg: " << FormatDuration(Mean() * kAdjust, unit, kFractionalDigits) << " Max: "
|
|
<< FormatDuration(Max() * kAdjust, unit, kFractionalDigits) << std::endl;
|
|
}
|
|
|
|
template <class Value>
|
|
inline void Histogram<Value>::PrintMemoryUse(std::ostream &os) const {
|
|
os << Name();
|
|
if (sample_size_ != 0u) {
|
|
os << ": Avg: " << PrettySize(Mean()) << " Max: "
|
|
<< PrettySize(Max()) << " Min: " << PrettySize(Min()) << "\n";
|
|
} else {
|
|
os << ": <no data>\n";
|
|
}
|
|
}
|
|
|
|
template <class Value>
|
|
inline void Histogram<Value>::CreateHistogram(CumulativeData* out_data) const {
|
|
DCHECK_GT(sample_size_, 0ull);
|
|
out_data->freq_.clear();
|
|
out_data->perc_.clear();
|
|
uint64_t accumulated = 0;
|
|
out_data->freq_.push_back(accumulated);
|
|
out_data->perc_.push_back(0.0);
|
|
for (size_t idx = 0; idx < frequency_.size(); idx++) {
|
|
accumulated += frequency_[idx];
|
|
out_data->freq_.push_back(accumulated);
|
|
out_data->perc_.push_back(static_cast<double>(accumulated) / static_cast<double>(sample_size_));
|
|
}
|
|
DCHECK_EQ(out_data->freq_.back(), sample_size_);
|
|
DCHECK_LE(std::abs(out_data->perc_.back() - 1.0), 0.001);
|
|
}
|
|
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wfloat-equal"
|
|
|
|
template <class Value>
|
|
inline double Histogram<Value>::Percentile(double per, const CumulativeData& data) const {
|
|
DCHECK_GT(data.perc_.size(), 0ull);
|
|
size_t upper_idx = 0, lower_idx = 0;
|
|
for (size_t idx = 0; idx < data.perc_.size(); idx++) {
|
|
if (per <= data.perc_[idx]) {
|
|
upper_idx = idx;
|
|
break;
|
|
}
|
|
|
|
if (per >= data.perc_[idx] && idx != 0 && data.perc_[idx] != data.perc_[idx - 1]) {
|
|
lower_idx = idx;
|
|
}
|
|
}
|
|
|
|
const double lower_perc = data.perc_[lower_idx];
|
|
const double lower_value = static_cast<double>(GetRange(lower_idx));
|
|
if (per == lower_perc) {
|
|
return lower_value;
|
|
}
|
|
|
|
const double upper_perc = data.perc_[upper_idx];
|
|
const double upper_value = static_cast<double>(GetRange(upper_idx));
|
|
if (per == upper_perc) {
|
|
return upper_value;
|
|
}
|
|
DCHECK_GT(upper_perc, lower_perc);
|
|
|
|
double value = lower_value + (upper_value - lower_value) *
|
|
(per - lower_perc) / (upper_perc - lower_perc);
|
|
|
|
if (value < min_value_added_) {
|
|
value = min_value_added_;
|
|
} else if (value > max_value_added_) {
|
|
value = max_value_added_;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
#pragma clang diagnostic pop
|
|
|
|
} // namespace art
|
|
#endif // ART_LIBARTBASE_BASE_HISTOGRAM_INL_H_
|