| // Copyright 2021 Google LLC |
| // |
| // 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. |
| |
| #pragma once |
| #include <math.h> |
| #include <stdio.h> |
| |
| #include <vector> |
| template <typename ValueType> |
| class Histogram |
| { |
| public: |
| Histogram() |
| { |
| num_entries_ = 0; |
| num_low_outliers_ = 0; |
| num_high_outliers_ = 0; |
| low_percentile_ = 0; |
| high_percentile_ = 0; |
| SetWindowSize(10000); |
| SetCumulativeDensityThresholds(0.01f, 0.99f); |
| } |
| |
| void AddSample(ValueType s) |
| { |
| int N = static_cast<int>(samples_.size()); |
| samples_[num_entries_ % N] = s; |
| num_entries_++; |
| } |
| |
| void SetBucketCount(int bc) |
| { |
| buckets_.resize(bc); |
| } |
| |
| void SetWindowSize(int s) |
| { |
| samples_.resize(s); |
| } |
| |
| void SetCumulativeDensityThresholds(float low_cd, float high_cd) |
| { |
| low_cum_density_ = low_cd; |
| high_cum_density_ = high_cd; |
| } |
| |
| void ComputeHistogram() |
| { |
| const int NS = static_cast<int>(samples_.size()); |
| int N = NS; |
| if (num_entries_ < N) |
| { |
| N = num_entries_; |
| } |
| if (N <= 0) |
| { |
| return; |
| } |
| std::vector<ValueType> sorted; |
| if (N == NS) |
| sorted = samples_; |
| else |
| sorted.insert(sorted.end(), samples_.begin(), samples_.begin() + N); |
| sort(sorted.begin(), sorted.end()); |
| int idx_low = static_cast<int>(N * low_cum_density_); |
| int idx_high = static_cast<int>(N * high_cum_density_); |
| if (idx_high - idx_low + 1 < 1) |
| { |
| return; // No entries can be shown, quit |
| } |
| max_bucket_height_ = 0; |
| ValueType value_low = sorted[idx_low]; |
| ValueType value_high = sorted[idx_high]; |
| low_percentile_ = value_low; |
| high_percentile_ = value_high; |
| const int NB = static_cast<int>(buckets_.size()); // Number of Bins |
| float bucket_width = (value_high - value_low) / NB; |
| // If all values are the same, manually extend the range to |
| // (value-1, value+1) |
| const float EPS = 1e-4; |
| if (fabs(value_high - value_low) <= EPS) |
| { |
| value_low = value_low - 1; |
| value_high = value_high + 1; |
| bucket_width = (value_high - value_low) / NB; |
| } |
| else |
| {} |
| buckets_.assign(NB, 0); |
| num_low_outliers_ = 0; |
| num_high_outliers_ = 0; |
| for (int i = idx_low; i <= idx_high; i++) |
| { |
| ValueType v = sorted[i]; |
| ValueType dist = (v - value_low); |
| int bucket_idx = dist / bucket_width; |
| if (bucket_idx < 0) |
| { |
| num_low_outliers_++; |
| } |
| else if (bucket_idx >= NB) |
| { |
| num_high_outliers_++; |
| } |
| else |
| { |
| buckets_[bucket_idx]++; |
| max_bucket_height_ = |
| std::max(max_bucket_height_, buckets_[bucket_idx]); |
| } |
| } |
| } |
| |
| int BucketHeight(int idx) |
| { |
| if (idx < 0 || idx >= static_cast<int>(buckets_.size())) |
| return 0; |
| return buckets_[idx]; |
| } |
| |
| void Assign(Histogram<ValueType>* out) |
| { |
| out->num_entries_ = num_entries_; |
| out->samples_ = samples_; |
| out->num_low_outliers_ = num_low_outliers_; |
| out->num_high_outliers_ = num_high_outliers_; |
| out->buckets_ = buckets_; |
| out->low_cum_density_ = low_cum_density_; |
| out->high_cum_density_ = high_cum_density_; |
| out->low_percentile_ = low_percentile_; |
| out->high_percentile_ = high_percentile_; |
| out->max_bucket_height_ = max_bucket_height_; |
| } |
| |
| int MaxBucketHeight() |
| { |
| return max_bucket_height_; |
| } |
| |
| ValueType LowPercentile() |
| { |
| return low_percentile_; |
| } |
| |
| ValueType HighPercentile() |
| { |
| return high_percentile_; |
| } |
| |
| float LowCumDensity() |
| { |
| return low_cum_density_; |
| } |
| |
| float HighCumDensity() |
| { |
| return high_cum_density_; |
| } |
| |
| bool Empty() |
| { |
| return num_entries_ == 0; |
| } |
| |
| int num_entries_; |
| std::vector<ValueType> samples_; |
| int num_low_outliers_, num_high_outliers_; |
| std::vector<int> buckets_; |
| float low_cum_density_, high_cum_density_; |
| // "1% percentile" means "1% of the samples are below this value" |
| ValueType low_percentile_, high_percentile_; |
| int max_bucket_height_; |
| }; |