blob: dacceffe2d24be54ba0ccf3062ccd0a345e89752 [file] [log] [blame]
henrike@webrtc.orgf0488722014-05-13 18:00:26 +00001/*
2 * Copyright 2011 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Steve Anton10542f22019-01-11 09:11:00 -080011#ifndef RTC_BASE_ROLLING_ACCUMULATOR_H_
12#define RTC_BASE_ROLLING_ACCUMULATOR_H_
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000013
Yves Gerey3e707812018-11-28 16:47:49 +010014#include <stddef.h>
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020015#include <algorithm>
16#include <vector>
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000017
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "rtc_base/checks.h"
Steve Anton10542f22019-01-11 09:11:00 -080019#include "rtc_base/constructor_magic.h"
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020020
21namespace rtc {
22
23// RollingAccumulator stores and reports statistics
24// over N most recent samples.
25//
26// T is assumed to be an int, long, double or float.
Yves Gerey665174f2018-06-19 15:03:05 +020027template <typename T>
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020028class RollingAccumulator {
29 public:
Yves Gerey665174f2018-06-19 15:03:05 +020030 explicit RollingAccumulator(size_t max_count) : samples_(max_count) {
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020031 Reset();
32 }
Yves Gerey665174f2018-06-19 15:03:05 +020033 ~RollingAccumulator() {}
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020034
Yves Gerey665174f2018-06-19 15:03:05 +020035 size_t max_count() const { return samples_.size(); }
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020036
Yves Gerey665174f2018-06-19 15:03:05 +020037 size_t count() const { return count_; }
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020038
39 void Reset() {
40 count_ = 0U;
41 next_index_ = 0U;
42 sum_ = 0.0;
43 sum_2_ = 0.0;
44 max_ = T();
45 max_stale_ = false;
46 min_ = T();
47 min_stale_ = false;
48 }
49
50 void AddSample(T sample) {
51 if (count_ == max_count()) {
52 // Remove oldest sample.
53 T sample_to_remove = samples_[next_index_];
54 sum_ -= sample_to_remove;
55 sum_2_ -= static_cast<double>(sample_to_remove) * sample_to_remove;
56 if (sample_to_remove >= max_) {
57 max_stale_ = true;
58 }
59 if (sample_to_remove <= min_) {
60 min_stale_ = true;
61 }
62 } else {
63 // Increase count of samples.
64 ++count_;
65 }
66 // Add new sample.
67 samples_[next_index_] = sample;
68 sum_ += sample;
69 sum_2_ += static_cast<double>(sample) * sample;
70 if (count_ == 1 || sample >= max_) {
71 max_ = sample;
72 max_stale_ = false;
73 }
74 if (count_ == 1 || sample <= min_) {
75 min_ = sample;
76 min_stale_ = false;
77 }
78 // Update next_index_.
79 next_index_ = (next_index_ + 1) % max_count();
80 }
81
Yves Gerey665174f2018-06-19 15:03:05 +020082 T ComputeSum() const { return static_cast<T>(sum_); }
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020083
84 double ComputeMean() const {
85 if (count_ == 0) {
86 return 0.0;
87 }
88 return sum_ / count_;
89 }
90
91 T ComputeMax() const {
92 if (max_stale_) {
Yves Gerey665174f2018-06-19 15:03:05 +020093 RTC_DCHECK(count_ > 0)
94 << "It shouldn't be possible for max_stale_ && count_ == 0";
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020095 max_ = samples_[next_index_];
96 for (size_t i = 1u; i < count_; i++) {
97 max_ = std::max(max_, samples_[(next_index_ + i) % max_count()]);
98 }
99 max_stale_ = false;
100 }
101 return max_;
102 }
103
104 T ComputeMin() const {
105 if (min_stale_) {
Yves Gerey665174f2018-06-19 15:03:05 +0200106 RTC_DCHECK(count_ > 0)
107 << "It shouldn't be possible for min_stale_ && count_ == 0";
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200108 min_ = samples_[next_index_];
109 for (size_t i = 1u; i < count_; i++) {
110 min_ = std::min(min_, samples_[(next_index_ + i) % max_count()]);
111 }
112 min_stale_ = false;
113 }
114 return min_;
115 }
116
117 // O(n) time complexity.
118 // Weights nth sample with weight (learning_rate)^n. Learning_rate should be
119 // between (0.0, 1.0], otherwise the non-weighted mean is returned.
120 double ComputeWeightedMean(double learning_rate) const {
121 if (count_ < 1 || learning_rate <= 0.0 || learning_rate >= 1.0) {
122 return ComputeMean();
123 }
124 double weighted_mean = 0.0;
125 double current_weight = 1.0;
126 double weight_sum = 0.0;
127 const size_t max_size = max_count();
128 for (size_t i = 0; i < count_; ++i) {
129 current_weight *= learning_rate;
130 weight_sum += current_weight;
131 // Add max_size to prevent underflow.
132 size_t index = (next_index_ + max_size - i - 1) % max_size;
133 weighted_mean += current_weight * samples_[index];
134 }
135 return weighted_mean / weight_sum;
136 }
137
138 // Compute estimated variance. Estimation is more accurate
139 // as the number of samples grows.
140 double ComputeVariance() const {
141 if (count_ == 0) {
142 return 0.0;
143 }
144 // Var = E[x^2] - (E[x])^2
145 double count_inv = 1.0 / count_;
146 double mean_2 = sum_2_ * count_inv;
147 double mean = sum_ * count_inv;
148 return mean_2 - (mean * mean);
149 }
150
151 private:
152 size_t count_;
153 size_t next_index_;
154 double sum_; // Sum(x) - double to avoid overflow
155 double sum_2_; // Sum(x*x) - double to avoid overflow
156 mutable T max_;
157 mutable bool max_stale_;
158 mutable T min_;
159 mutable bool min_stale_;
160 std::vector<T> samples_;
161
162 RTC_DISALLOW_COPY_AND_ASSIGN(RollingAccumulator);
163};
164
165} // namespace rtc
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000166
Steve Anton10542f22019-01-11 09:11:00 -0800167#endif // RTC_BASE_ROLLING_ACCUMULATOR_H_