blob: c4c2e78581b82f9bd137f74832b7059d774ece82 [file] [log] [blame]
sprang@webrtc.org37968a92013-12-03 10:31:59 +00001/*
2 * Copyright (c) 2013 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#include "rtc_base/rate_statistics.h"
sprang@webrtc.org37968a92013-12-03 10:31:59 +000012
Stefan Holmerfb8fc532016-04-22 15:48:23 +020013#include <algorithm>
Yves Gereya688d112019-12-31 16:16:51 +010014#include <limits>
Mirko Bonadei317a1f02019-09-17 17:06:18 +020015#include <memory>
Stefan Holmerfb8fc532016-04-22 15:48:23 +020016
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "rtc_base/checks.h"
Harald Alvestranda846cef2020-01-15 14:02:12 +010018#include "rtc_base/logging.h"
19#include "rtc_base/numerics/safe_conversions.h"
henrike@webrtc.orgf2aafe42014-04-29 17:54:17 +000020
sprang@webrtc.org37968a92013-12-03 10:31:59 +000021namespace webrtc {
22
Erik Språng51e60302016-06-10 22:13:21 +020023RateStatistics::RateStatistics(int64_t window_size_ms, float scale)
24 : buckets_(new Bucket[window_size_ms]()),
sprang@webrtc.org37968a92013-12-03 10:31:59 +000025 accumulated_count_(0),
Erik Språng51e60302016-06-10 22:13:21 +020026 num_samples_(0),
27 oldest_time_(-window_size_ms),
sprang@webrtc.org37968a92013-12-03 10:31:59 +000028 oldest_index_(0),
Erik Språng51e60302016-06-10 22:13:21 +020029 scale_(scale),
30 max_window_size_ms_(window_size_ms),
31 current_window_size_ms_(max_window_size_ms_) {}
sprang@webrtc.org37968a92013-12-03 10:31:59 +000032
Sergey Silkin40b70502018-08-27 10:55:07 +020033RateStatistics::RateStatistics(const RateStatistics& other)
34 : accumulated_count_(other.accumulated_count_),
Harald Alvestranda846cef2020-01-15 14:02:12 +010035 overflow_(other.overflow_),
Sergey Silkin40b70502018-08-27 10:55:07 +020036 num_samples_(other.num_samples_),
37 oldest_time_(other.oldest_time_),
38 oldest_index_(other.oldest_index_),
39 scale_(other.scale_),
40 max_window_size_ms_(other.max_window_size_ms_),
41 current_window_size_ms_(other.current_window_size_ms_) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +020042 buckets_ = std::make_unique<Bucket[]>(other.max_window_size_ms_);
Sergey Silkin40b70502018-08-27 10:55:07 +020043 std::copy(other.buckets_.get(),
44 other.buckets_.get() + other.max_window_size_ms_, buckets_.get());
45}
46
47RateStatistics::RateStatistics(RateStatistics&& other) = default;
48
tkchinf75d0082016-02-23 22:49:42 -080049RateStatistics::~RateStatistics() {}
sprang@webrtc.org37968a92013-12-03 10:31:59 +000050
51void RateStatistics::Reset() {
52 accumulated_count_ = 0;
Harald Alvestranda846cef2020-01-15 14:02:12 +010053 overflow_ = false;
Erik Språng51e60302016-06-10 22:13:21 +020054 num_samples_ = 0;
55 oldest_time_ = -max_window_size_ms_;
sprang@webrtc.org37968a92013-12-03 10:31:59 +000056 oldest_index_ = 0;
Erik Språng51e60302016-06-10 22:13:21 +020057 current_window_size_ms_ = max_window_size_ms_;
58 for (int64_t i = 0; i < max_window_size_ms_; i++)
59 buckets_[i] = Bucket();
sprang@webrtc.org37968a92013-12-03 10:31:59 +000060}
61
Harald Alvestranda846cef2020-01-15 14:02:12 +010062void RateStatistics::Update(int64_t count, int64_t now_ms) {
63 RTC_DCHECK_LE(0, count);
sprang@webrtc.org37968a92013-12-03 10:31:59 +000064 if (now_ms < oldest_time_) {
65 // Too old data is ignored.
66 return;
67 }
68
69 EraseOld(now_ms);
70
Erik Språng51e60302016-06-10 22:13:21 +020071 // First ever sample, reset window to start now.
72 if (!IsInitialized())
73 oldest_time_ = now_ms;
74
Harald Alvestranda846cef2020-01-15 14:02:12 +010075 uint32_t now_offset = rtc::dchecked_cast<uint32_t>(now_ms - oldest_time_);
Erik Språng51e60302016-06-10 22:13:21 +020076 RTC_DCHECK_LT(now_offset, max_window_size_ms_);
77 uint32_t index = oldest_index_ + now_offset;
78 if (index >= max_window_size_ms_)
79 index -= max_window_size_ms_;
80 buckets_[index].sum += count;
81 ++buckets_[index].samples;
Harald Alvestranda846cef2020-01-15 14:02:12 +010082 if (std::numeric_limits<int64_t>::max() - accumulated_count_ > count) {
83 accumulated_count_ += count;
84 } else {
85 overflow_ = true;
86 }
Erik Språng51e60302016-06-10 22:13:21 +020087 ++num_samples_;
sprang@webrtc.org37968a92013-12-03 10:31:59 +000088}
89
Harald Alvestranda846cef2020-01-15 14:02:12 +010090absl::optional<int64_t> RateStatistics::Rate(int64_t now_ms) const {
sprangcd349d92016-07-13 09:11:28 -070091 // Yeah, this const_cast ain't pretty, but the alternative is to declare most
92 // of the members as mutable...
93 const_cast<RateStatistics*>(this)->EraseOld(now_ms);
Erik Språng51e60302016-06-10 22:13:21 +020094
95 // If window is a single bucket or there is only one sample in a data set that
Harald Alvestranda846cef2020-01-15 14:02:12 +010096 // has not grown to the full window size, or if the accumulator has
97 // overflowed, treat this as rate unavailable.
98 int active_window_size = now_ms - oldest_time_ + 1;
Erik Språng51e60302016-06-10 22:13:21 +020099 if (num_samples_ == 0 || active_window_size <= 1 ||
Harald Alvestranda846cef2020-01-15 14:02:12 +0100100 (num_samples_ <= 1 &&
101 rtc::SafeLt(active_window_size, current_window_size_ms_)) ||
102 overflow_) {
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200103 return absl::nullopt;
Erik Språng51e60302016-06-10 22:13:21 +0200104 }
105
Harald Alvestranda846cef2020-01-15 14:02:12 +0100106 float scale = static_cast<float>(scale_) / active_window_size;
Yves Gereya688d112019-12-31 16:16:51 +0100107 float result = accumulated_count_ * scale + 0.5f;
108
109 // Better return unavailable rate than garbage value (undefined behavior).
Harald Alvestranda846cef2020-01-15 14:02:12 +0100110 if (result > static_cast<float>(std::numeric_limits<int64_t>::max())) {
Yves Gereya688d112019-12-31 16:16:51 +0100111 return absl::nullopt;
112 }
Harald Alvestranda846cef2020-01-15 14:02:12 +0100113 return rtc::dchecked_cast<int64_t>(result);
sprang@webrtc.org37968a92013-12-03 10:31:59 +0000114}
115
116void RateStatistics::EraseOld(int64_t now_ms) {
Erik Språng51e60302016-06-10 22:13:21 +0200117 if (!IsInitialized())
sprang@webrtc.org37968a92013-12-03 10:31:59 +0000118 return;
Erik Språng51e60302016-06-10 22:13:21 +0200119
120 // New oldest time that is included in data set.
121 int64_t new_oldest_time = now_ms - current_window_size_ms_ + 1;
122
123 // New oldest time is older than the current one, no need to cull data.
124 if (new_oldest_time <= oldest_time_)
125 return;
126
127 // Loop over buckets and remove too old data points.
128 while (num_samples_ > 0 && oldest_time_ < new_oldest_time) {
129 const Bucket& oldest_bucket = buckets_[oldest_index_];
130 RTC_DCHECK_GE(accumulated_count_, oldest_bucket.sum);
131 RTC_DCHECK_GE(num_samples_, oldest_bucket.samples);
132 accumulated_count_ -= oldest_bucket.sum;
133 num_samples_ -= oldest_bucket.samples;
134 buckets_[oldest_index_] = Bucket();
135 if (++oldest_index_ >= max_window_size_ms_)
sprang@webrtc.org37968a92013-12-03 10:31:59 +0000136 oldest_index_ = 0;
sprang@webrtc.org37968a92013-12-03 10:31:59 +0000137 ++oldest_time_;
Harald Alvestranda846cef2020-01-15 14:02:12 +0100138 // This does not clear overflow_ even when counter is empty.
139 // TODO(https://bugs.webrtc.org/11247): Consider if overflow_ can be reset.
sprang@webrtc.org37968a92013-12-03 10:31:59 +0000140 }
141 oldest_time_ = new_oldest_time;
142}
143
Erik Språng51e60302016-06-10 22:13:21 +0200144bool RateStatistics::SetWindowSize(int64_t window_size_ms, int64_t now_ms) {
145 if (window_size_ms <= 0 || window_size_ms > max_window_size_ms_)
146 return false;
Erik Språng51e60302016-06-10 22:13:21 +0200147 current_window_size_ms_ = window_size_ms;
148 EraseOld(now_ms);
149 return true;
150}
151
sprangcd349d92016-07-13 09:11:28 -0700152bool RateStatistics::IsInitialized() const {
Erik Språng51e60302016-06-10 22:13:21 +0200153 return oldest_time_ != -max_window_size_ms_;
154}
155
sprang@webrtc.org37968a92013-12-03 10:31:59 +0000156} // namespace webrtc