blob: 89f7e54a6809476874c5c0fe7671858c2b478b34 [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"
henrike@webrtc.orgf2aafe42014-04-29 17:54:17 +000018
sprang@webrtc.org37968a92013-12-03 10:31:59 +000019namespace webrtc {
20
Erik Språng51e60302016-06-10 22:13:21 +020021RateStatistics::RateStatistics(int64_t window_size_ms, float scale)
22 : buckets_(new Bucket[window_size_ms]()),
sprang@webrtc.org37968a92013-12-03 10:31:59 +000023 accumulated_count_(0),
Erik Språng51e60302016-06-10 22:13:21 +020024 num_samples_(0),
25 oldest_time_(-window_size_ms),
sprang@webrtc.org37968a92013-12-03 10:31:59 +000026 oldest_index_(0),
Erik Språng51e60302016-06-10 22:13:21 +020027 scale_(scale),
28 max_window_size_ms_(window_size_ms),
29 current_window_size_ms_(max_window_size_ms_) {}
sprang@webrtc.org37968a92013-12-03 10:31:59 +000030
Sergey Silkin40b70502018-08-27 10:55:07 +020031RateStatistics::RateStatistics(const RateStatistics& other)
32 : accumulated_count_(other.accumulated_count_),
33 num_samples_(other.num_samples_),
34 oldest_time_(other.oldest_time_),
35 oldest_index_(other.oldest_index_),
36 scale_(other.scale_),
37 max_window_size_ms_(other.max_window_size_ms_),
38 current_window_size_ms_(other.current_window_size_ms_) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +020039 buckets_ = std::make_unique<Bucket[]>(other.max_window_size_ms_);
Sergey Silkin40b70502018-08-27 10:55:07 +020040 std::copy(other.buckets_.get(),
41 other.buckets_.get() + other.max_window_size_ms_, buckets_.get());
42}
43
44RateStatistics::RateStatistics(RateStatistics&& other) = default;
45
tkchinf75d0082016-02-23 22:49:42 -080046RateStatistics::~RateStatistics() {}
sprang@webrtc.org37968a92013-12-03 10:31:59 +000047
48void RateStatistics::Reset() {
49 accumulated_count_ = 0;
Erik Språng51e60302016-06-10 22:13:21 +020050 num_samples_ = 0;
51 oldest_time_ = -max_window_size_ms_;
sprang@webrtc.org37968a92013-12-03 10:31:59 +000052 oldest_index_ = 0;
Erik Språng51e60302016-06-10 22:13:21 +020053 current_window_size_ms_ = max_window_size_ms_;
54 for (int64_t i = 0; i < max_window_size_ms_; i++)
55 buckets_[i] = Bucket();
sprang@webrtc.org37968a92013-12-03 10:31:59 +000056}
57
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +000058void RateStatistics::Update(size_t count, int64_t now_ms) {
sprang@webrtc.org37968a92013-12-03 10:31:59 +000059 if (now_ms < oldest_time_) {
60 // Too old data is ignored.
61 return;
62 }
63
64 EraseOld(now_ms);
65
Erik Språng51e60302016-06-10 22:13:21 +020066 // First ever sample, reset window to start now.
67 if (!IsInitialized())
68 oldest_time_ = now_ms;
69
70 uint32_t now_offset = static_cast<uint32_t>(now_ms - oldest_time_);
71 RTC_DCHECK_LT(now_offset, max_window_size_ms_);
72 uint32_t index = oldest_index_ + now_offset;
73 if (index >= max_window_size_ms_)
74 index -= max_window_size_ms_;
75 buckets_[index].sum += count;
76 ++buckets_[index].samples;
sprang@webrtc.org37968a92013-12-03 10:31:59 +000077 accumulated_count_ += count;
Erik Språng51e60302016-06-10 22:13:21 +020078 ++num_samples_;
sprang@webrtc.org37968a92013-12-03 10:31:59 +000079}
80
Danil Chapovalov0a1d1892018-06-21 11:48:25 +020081absl::optional<uint32_t> RateStatistics::Rate(int64_t now_ms) const {
sprangcd349d92016-07-13 09:11:28 -070082 // Yeah, this const_cast ain't pretty, but the alternative is to declare most
83 // of the members as mutable...
84 const_cast<RateStatistics*>(this)->EraseOld(now_ms);
Erik Språng51e60302016-06-10 22:13:21 +020085
86 // If window is a single bucket or there is only one sample in a data set that
87 // has not grown to the full window size, treat this as rate unavailable.
88 int64_t active_window_size = now_ms - oldest_time_ + 1;
89 if (num_samples_ == 0 || active_window_size <= 1 ||
90 (num_samples_ <= 1 && active_window_size < current_window_size_ms_)) {
Danil Chapovalov0a1d1892018-06-21 11:48:25 +020091 return absl::nullopt;
Erik Språng51e60302016-06-10 22:13:21 +020092 }
93
94 float scale = scale_ / active_window_size;
Yves Gereya688d112019-12-31 16:16:51 +010095 float result = accumulated_count_ * scale + 0.5f;
96
97 // Better return unavailable rate than garbage value (undefined behavior).
98 if (result > std::numeric_limits<uint32_t>::max()) {
99 return absl::nullopt;
100 }
101 return static_cast<uint32_t>(result);
sprang@webrtc.org37968a92013-12-03 10:31:59 +0000102}
103
104void RateStatistics::EraseOld(int64_t now_ms) {
Erik Språng51e60302016-06-10 22:13:21 +0200105 if (!IsInitialized())
sprang@webrtc.org37968a92013-12-03 10:31:59 +0000106 return;
Erik Språng51e60302016-06-10 22:13:21 +0200107
108 // New oldest time that is included in data set.
109 int64_t new_oldest_time = now_ms - current_window_size_ms_ + 1;
110
111 // New oldest time is older than the current one, no need to cull data.
112 if (new_oldest_time <= oldest_time_)
113 return;
114
115 // Loop over buckets and remove too old data points.
116 while (num_samples_ > 0 && oldest_time_ < new_oldest_time) {
117 const Bucket& oldest_bucket = buckets_[oldest_index_];
118 RTC_DCHECK_GE(accumulated_count_, oldest_bucket.sum);
119 RTC_DCHECK_GE(num_samples_, oldest_bucket.samples);
120 accumulated_count_ -= oldest_bucket.sum;
121 num_samples_ -= oldest_bucket.samples;
122 buckets_[oldest_index_] = Bucket();
123 if (++oldest_index_ >= max_window_size_ms_)
sprang@webrtc.org37968a92013-12-03 10:31:59 +0000124 oldest_index_ = 0;
sprang@webrtc.org37968a92013-12-03 10:31:59 +0000125 ++oldest_time_;
sprang@webrtc.org37968a92013-12-03 10:31:59 +0000126 }
127 oldest_time_ = new_oldest_time;
128}
129
Erik Språng51e60302016-06-10 22:13:21 +0200130bool RateStatistics::SetWindowSize(int64_t window_size_ms, int64_t now_ms) {
131 if (window_size_ms <= 0 || window_size_ms > max_window_size_ms_)
132 return false;
133
134 current_window_size_ms_ = window_size_ms;
135 EraseOld(now_ms);
136 return true;
137}
138
sprangcd349d92016-07-13 09:11:28 -0700139bool RateStatistics::IsInitialized() const {
Erik Språng51e60302016-06-10 22:13:21 +0200140 return oldest_time_ != -max_window_size_ms_;
141}
142
sprang@webrtc.org37968a92013-12-03 10:31:59 +0000143} // namespace webrtc