1*d9f75844SAndroid Build Coastguard Worker /* 2*d9f75844SAndroid Build Coastguard Worker * Copyright 2011 The WebRTC Project Authors. All rights reserved. 3*d9f75844SAndroid Build Coastguard Worker * 4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license 5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source 6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found 7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may 8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree. 9*d9f75844SAndroid Build Coastguard Worker */ 10*d9f75844SAndroid Build Coastguard Worker 11*d9f75844SAndroid Build Coastguard Worker #ifndef RTC_BASE_ROLLING_ACCUMULATOR_H_ 12*d9f75844SAndroid Build Coastguard Worker #define RTC_BASE_ROLLING_ACCUMULATOR_H_ 13*d9f75844SAndroid Build Coastguard Worker 14*d9f75844SAndroid Build Coastguard Worker #include <stddef.h> 15*d9f75844SAndroid Build Coastguard Worker 16*d9f75844SAndroid Build Coastguard Worker #include <algorithm> 17*d9f75844SAndroid Build Coastguard Worker #include <vector> 18*d9f75844SAndroid Build Coastguard Worker 19*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h" 20*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/numerics/running_statistics.h" 21*d9f75844SAndroid Build Coastguard Worker 22*d9f75844SAndroid Build Coastguard Worker namespace rtc { 23*d9f75844SAndroid Build Coastguard Worker 24*d9f75844SAndroid Build Coastguard Worker // RollingAccumulator stores and reports statistics 25*d9f75844SAndroid Build Coastguard Worker // over N most recent samples. 26*d9f75844SAndroid Build Coastguard Worker // 27*d9f75844SAndroid Build Coastguard Worker // T is assumed to be an int, long, double or float. 28*d9f75844SAndroid Build Coastguard Worker template <typename T> 29*d9f75844SAndroid Build Coastguard Worker class RollingAccumulator { 30*d9f75844SAndroid Build Coastguard Worker public: RollingAccumulator(size_t max_count)31*d9f75844SAndroid Build Coastguard Worker explicit RollingAccumulator(size_t max_count) : samples_(max_count) { 32*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(max_count > 0); 33*d9f75844SAndroid Build Coastguard Worker Reset(); 34*d9f75844SAndroid Build Coastguard Worker } ~RollingAccumulator()35*d9f75844SAndroid Build Coastguard Worker ~RollingAccumulator() {} 36*d9f75844SAndroid Build Coastguard Worker 37*d9f75844SAndroid Build Coastguard Worker RollingAccumulator(const RollingAccumulator&) = delete; 38*d9f75844SAndroid Build Coastguard Worker RollingAccumulator& operator=(const RollingAccumulator&) = delete; 39*d9f75844SAndroid Build Coastguard Worker max_count()40*d9f75844SAndroid Build Coastguard Worker size_t max_count() const { return samples_.size(); } 41*d9f75844SAndroid Build Coastguard Worker count()42*d9f75844SAndroid Build Coastguard Worker size_t count() const { return static_cast<size_t>(stats_.Size()); } 43*d9f75844SAndroid Build Coastguard Worker Reset()44*d9f75844SAndroid Build Coastguard Worker void Reset() { 45*d9f75844SAndroid Build Coastguard Worker stats_ = webrtc::webrtc_impl::RunningStatistics<T>(); 46*d9f75844SAndroid Build Coastguard Worker next_index_ = 0U; 47*d9f75844SAndroid Build Coastguard Worker max_ = T(); 48*d9f75844SAndroid Build Coastguard Worker max_stale_ = false; 49*d9f75844SAndroid Build Coastguard Worker min_ = T(); 50*d9f75844SAndroid Build Coastguard Worker min_stale_ = false; 51*d9f75844SAndroid Build Coastguard Worker } 52*d9f75844SAndroid Build Coastguard Worker AddSample(T sample)53*d9f75844SAndroid Build Coastguard Worker void AddSample(T sample) { 54*d9f75844SAndroid Build Coastguard Worker if (count() == max_count()) { 55*d9f75844SAndroid Build Coastguard Worker // Remove oldest sample. 56*d9f75844SAndroid Build Coastguard Worker T sample_to_remove = samples_[next_index_]; 57*d9f75844SAndroid Build Coastguard Worker stats_.RemoveSample(sample_to_remove); 58*d9f75844SAndroid Build Coastguard Worker if (sample_to_remove >= max_) { 59*d9f75844SAndroid Build Coastguard Worker max_stale_ = true; 60*d9f75844SAndroid Build Coastguard Worker } 61*d9f75844SAndroid Build Coastguard Worker if (sample_to_remove <= min_) { 62*d9f75844SAndroid Build Coastguard Worker min_stale_ = true; 63*d9f75844SAndroid Build Coastguard Worker } 64*d9f75844SAndroid Build Coastguard Worker } 65*d9f75844SAndroid Build Coastguard Worker // Add new sample. 66*d9f75844SAndroid Build Coastguard Worker samples_[next_index_] = sample; 67*d9f75844SAndroid Build Coastguard Worker if (count() == 0 || sample >= max_) { 68*d9f75844SAndroid Build Coastguard Worker max_ = sample; 69*d9f75844SAndroid Build Coastguard Worker max_stale_ = false; 70*d9f75844SAndroid Build Coastguard Worker } 71*d9f75844SAndroid Build Coastguard Worker if (count() == 0 || sample <= min_) { 72*d9f75844SAndroid Build Coastguard Worker min_ = sample; 73*d9f75844SAndroid Build Coastguard Worker min_stale_ = false; 74*d9f75844SAndroid Build Coastguard Worker } 75*d9f75844SAndroid Build Coastguard Worker stats_.AddSample(sample); 76*d9f75844SAndroid Build Coastguard Worker // Update next_index_. 77*d9f75844SAndroid Build Coastguard Worker next_index_ = (next_index_ + 1) % max_count(); 78*d9f75844SAndroid Build Coastguard Worker } 79*d9f75844SAndroid Build Coastguard Worker ComputeMean()80*d9f75844SAndroid Build Coastguard Worker double ComputeMean() const { return stats_.GetMean().value_or(0); } 81*d9f75844SAndroid Build Coastguard Worker ComputeMax()82*d9f75844SAndroid Build Coastguard Worker T ComputeMax() const { 83*d9f75844SAndroid Build Coastguard Worker if (max_stale_) { 84*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(count() > 0) 85*d9f75844SAndroid Build Coastguard Worker << "It shouldn't be possible for max_stale_ && count() == 0"; 86*d9f75844SAndroid Build Coastguard Worker max_ = samples_[next_index_]; 87*d9f75844SAndroid Build Coastguard Worker for (size_t i = 1u; i < count(); i++) { 88*d9f75844SAndroid Build Coastguard Worker max_ = std::max(max_, samples_[(next_index_ + i) % max_count()]); 89*d9f75844SAndroid Build Coastguard Worker } 90*d9f75844SAndroid Build Coastguard Worker max_stale_ = false; 91*d9f75844SAndroid Build Coastguard Worker } 92*d9f75844SAndroid Build Coastguard Worker return max_; 93*d9f75844SAndroid Build Coastguard Worker } 94*d9f75844SAndroid Build Coastguard Worker ComputeMin()95*d9f75844SAndroid Build Coastguard Worker T ComputeMin() const { 96*d9f75844SAndroid Build Coastguard Worker if (min_stale_) { 97*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(count() > 0) 98*d9f75844SAndroid Build Coastguard Worker << "It shouldn't be possible for min_stale_ && count() == 0"; 99*d9f75844SAndroid Build Coastguard Worker min_ = samples_[next_index_]; 100*d9f75844SAndroid Build Coastguard Worker for (size_t i = 1u; i < count(); i++) { 101*d9f75844SAndroid Build Coastguard Worker min_ = std::min(min_, samples_[(next_index_ + i) % max_count()]); 102*d9f75844SAndroid Build Coastguard Worker } 103*d9f75844SAndroid Build Coastguard Worker min_stale_ = false; 104*d9f75844SAndroid Build Coastguard Worker } 105*d9f75844SAndroid Build Coastguard Worker return min_; 106*d9f75844SAndroid Build Coastguard Worker } 107*d9f75844SAndroid Build Coastguard Worker 108*d9f75844SAndroid Build Coastguard Worker // O(n) time complexity. 109*d9f75844SAndroid Build Coastguard Worker // Weights nth sample with weight (learning_rate)^n. Learning_rate should be 110*d9f75844SAndroid Build Coastguard Worker // between (0.0, 1.0], otherwise the non-weighted mean is returned. ComputeWeightedMean(double learning_rate)111*d9f75844SAndroid Build Coastguard Worker double ComputeWeightedMean(double learning_rate) const { 112*d9f75844SAndroid Build Coastguard Worker if (count() < 1 || learning_rate <= 0.0 || learning_rate >= 1.0) { 113*d9f75844SAndroid Build Coastguard Worker return ComputeMean(); 114*d9f75844SAndroid Build Coastguard Worker } 115*d9f75844SAndroid Build Coastguard Worker double weighted_mean = 0.0; 116*d9f75844SAndroid Build Coastguard Worker double current_weight = 1.0; 117*d9f75844SAndroid Build Coastguard Worker double weight_sum = 0.0; 118*d9f75844SAndroid Build Coastguard Worker const size_t max_size = max_count(); 119*d9f75844SAndroid Build Coastguard Worker for (size_t i = 0; i < count(); ++i) { 120*d9f75844SAndroid Build Coastguard Worker current_weight *= learning_rate; 121*d9f75844SAndroid Build Coastguard Worker weight_sum += current_weight; 122*d9f75844SAndroid Build Coastguard Worker // Add max_size to prevent underflow. 123*d9f75844SAndroid Build Coastguard Worker size_t index = (next_index_ + max_size - i - 1) % max_size; 124*d9f75844SAndroid Build Coastguard Worker weighted_mean += current_weight * samples_[index]; 125*d9f75844SAndroid Build Coastguard Worker } 126*d9f75844SAndroid Build Coastguard Worker return weighted_mean / weight_sum; 127*d9f75844SAndroid Build Coastguard Worker } 128*d9f75844SAndroid Build Coastguard Worker 129*d9f75844SAndroid Build Coastguard Worker // Compute estimated variance. Estimation is more accurate 130*d9f75844SAndroid Build Coastguard Worker // as the number of samples grows. ComputeVariance()131*d9f75844SAndroid Build Coastguard Worker double ComputeVariance() const { return stats_.GetVariance().value_or(0); } 132*d9f75844SAndroid Build Coastguard Worker 133*d9f75844SAndroid Build Coastguard Worker private: 134*d9f75844SAndroid Build Coastguard Worker webrtc::webrtc_impl::RunningStatistics<T> stats_; 135*d9f75844SAndroid Build Coastguard Worker size_t next_index_; 136*d9f75844SAndroid Build Coastguard Worker mutable T max_; 137*d9f75844SAndroid Build Coastguard Worker mutable bool max_stale_; 138*d9f75844SAndroid Build Coastguard Worker mutable T min_; 139*d9f75844SAndroid Build Coastguard Worker mutable bool min_stale_; 140*d9f75844SAndroid Build Coastguard Worker std::vector<T> samples_; 141*d9f75844SAndroid Build Coastguard Worker }; 142*d9f75844SAndroid Build Coastguard Worker 143*d9f75844SAndroid Build Coastguard Worker } // namespace rtc 144*d9f75844SAndroid Build Coastguard Worker 145*d9f75844SAndroid Build Coastguard Worker #endif // RTC_BASE_ROLLING_ACCUMULATOR_H_ 146