blob: f6215c5407ae3b44fc7a3b8825bf5b356af0d33a [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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#ifndef RTC_BASE_ROLLINGACCUMULATOR_H_
12#define RTC_BASE_ROLLINGACCUMULATOR_H_
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000013
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020014#include <algorithm>
15#include <vector>
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000016
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "rtc_base/checks.h"
18#include "rtc_base/constructormagic.h"
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020019
20namespace rtc {
21
22// RollingAccumulator stores and reports statistics
23// over N most recent samples.
24//
25// T is assumed to be an int, long, double or float.
Yves Gerey665174f2018-06-19 15:03:05 +020026template <typename T>
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020027class RollingAccumulator {
28 public:
Yves Gerey665174f2018-06-19 15:03:05 +020029 explicit RollingAccumulator(size_t max_count) : samples_(max_count) {
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020030 Reset();
31 }
Yves Gerey665174f2018-06-19 15:03:05 +020032 ~RollingAccumulator() {}
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020033
Yves Gerey665174f2018-06-19 15:03:05 +020034 size_t max_count() const { return samples_.size(); }
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020035
Yves Gerey665174f2018-06-19 15:03:05 +020036 size_t count() const { return count_; }
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020037
38 void Reset() {
39 count_ = 0U;
40 next_index_ = 0U;
41 sum_ = 0.0;
42 sum_2_ = 0.0;
43 max_ = T();
44 max_stale_ = false;
45 min_ = T();
46 min_stale_ = false;
47 }
48
49 void AddSample(T sample) {
50 if (count_ == max_count()) {
51 // Remove oldest sample.
52 T sample_to_remove = samples_[next_index_];
53 sum_ -= sample_to_remove;
54 sum_2_ -= static_cast<double>(sample_to_remove) * sample_to_remove;
55 if (sample_to_remove >= max_) {
56 max_stale_ = true;
57 }
58 if (sample_to_remove <= min_) {
59 min_stale_ = true;
60 }
61 } else {
62 // Increase count of samples.
63 ++count_;
64 }
65 // Add new sample.
66 samples_[next_index_] = sample;
67 sum_ += sample;
68 sum_2_ += static_cast<double>(sample) * sample;
69 if (count_ == 1 || sample >= max_) {
70 max_ = sample;
71 max_stale_ = false;
72 }
73 if (count_ == 1 || sample <= min_) {
74 min_ = sample;
75 min_stale_ = false;
76 }
77 // Update next_index_.
78 next_index_ = (next_index_ + 1) % max_count();
79 }
80
Yves Gerey665174f2018-06-19 15:03:05 +020081 T ComputeSum() const { return static_cast<T>(sum_); }
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020082
83 double ComputeMean() const {
84 if (count_ == 0) {
85 return 0.0;
86 }
87 return sum_ / count_;
88 }
89
90 T ComputeMax() const {
91 if (max_stale_) {
Yves Gerey665174f2018-06-19 15:03:05 +020092 RTC_DCHECK(count_ > 0)
93 << "It shouldn't be possible for max_stale_ && count_ == 0";
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020094 max_ = samples_[next_index_];
95 for (size_t i = 1u; i < count_; i++) {
96 max_ = std::max(max_, samples_[(next_index_ + i) % max_count()]);
97 }
98 max_stale_ = false;
99 }
100 return max_;
101 }
102
103 T ComputeMin() const {
104 if (min_stale_) {
Yves Gerey665174f2018-06-19 15:03:05 +0200105 RTC_DCHECK(count_ > 0)
106 << "It shouldn't be possible for min_stale_ && count_ == 0";
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200107 min_ = samples_[next_index_];
108 for (size_t i = 1u; i < count_; i++) {
109 min_ = std::min(min_, samples_[(next_index_ + i) % max_count()]);
110 }
111 min_stale_ = false;
112 }
113 return min_;
114 }
115
116 // O(n) time complexity.
117 // Weights nth sample with weight (learning_rate)^n. Learning_rate should be
118 // between (0.0, 1.0], otherwise the non-weighted mean is returned.
119 double ComputeWeightedMean(double learning_rate) const {
120 if (count_ < 1 || learning_rate <= 0.0 || learning_rate >= 1.0) {
121 return ComputeMean();
122 }
123 double weighted_mean = 0.0;
124 double current_weight = 1.0;
125 double weight_sum = 0.0;
126 const size_t max_size = max_count();
127 for (size_t i = 0; i < count_; ++i) {
128 current_weight *= learning_rate;
129 weight_sum += current_weight;
130 // Add max_size to prevent underflow.
131 size_t index = (next_index_ + max_size - i - 1) % max_size;
132 weighted_mean += current_weight * samples_[index];
133 }
134 return weighted_mean / weight_sum;
135 }
136
137 // Compute estimated variance. Estimation is more accurate
138 // as the number of samples grows.
139 double ComputeVariance() const {
140 if (count_ == 0) {
141 return 0.0;
142 }
143 // Var = E[x^2] - (E[x])^2
144 double count_inv = 1.0 / count_;
145 double mean_2 = sum_2_ * count_inv;
146 double mean = sum_ * count_inv;
147 return mean_2 - (mean * mean);
148 }
149
150 private:
151 size_t count_;
152 size_t next_index_;
153 double sum_; // Sum(x) - double to avoid overflow
154 double sum_2_; // Sum(x*x) - double to avoid overflow
155 mutable T max_;
156 mutable bool max_stale_;
157 mutable T min_;
158 mutable bool min_stale_;
159 std::vector<T> samples_;
160
161 RTC_DISALLOW_COPY_AND_ASSIGN(RollingAccumulator);
162};
163
164} // namespace rtc
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000165
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200166#endif // RTC_BASE_ROLLINGACCUMULATOR_H_