blob: 5c83796471e52862eb5e57c1bca0a99e5ce1b6a1 [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ång03d9e522020-05-25 12:58:03 +020023RateStatistics::Bucket::Bucket(int64_t timestamp)
24 : sum(0), num_samples(0), timestamp(timestamp) {}
25
Erik Språng51e60302016-06-10 22:13:21 +020026RateStatistics::RateStatistics(int64_t window_size_ms, float scale)
Erik Språng03d9e522020-05-25 12:58:03 +020027 : accumulated_count_(0),
28 first_timestamp_(-1),
Erik Språng51e60302016-06-10 22:13:21 +020029 num_samples_(0),
Erik Språng51e60302016-06-10 22:13:21 +020030 scale_(scale),
31 max_window_size_ms_(window_size_ms),
32 current_window_size_ms_(max_window_size_ms_) {}
sprang@webrtc.org37968a92013-12-03 10:31:59 +000033
Sergey Silkin40b70502018-08-27 10:55:07 +020034RateStatistics::RateStatistics(const RateStatistics& other)
Erik Språng03d9e522020-05-25 12:58:03 +020035 : buckets_(other.buckets_),
36 accumulated_count_(other.accumulated_count_),
37 first_timestamp_(other.first_timestamp_),
Harald Alvestranda846cef2020-01-15 14:02:12 +010038 overflow_(other.overflow_),
Sergey Silkin40b70502018-08-27 10:55:07 +020039 num_samples_(other.num_samples_),
Sergey Silkin40b70502018-08-27 10:55:07 +020040 scale_(other.scale_),
41 max_window_size_ms_(other.max_window_size_ms_),
Erik Språng03d9e522020-05-25 12:58:03 +020042 current_window_size_ms_(other.current_window_size_ms_) {}
Sergey Silkin40b70502018-08-27 10:55:07 +020043
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;
Harald Alvestranda846cef2020-01-15 14:02:12 +010050 overflow_ = false;
Erik Språng51e60302016-06-10 22:13:21 +020051 num_samples_ = 0;
Erik Språng03d9e522020-05-25 12:58:03 +020052 first_timestamp_ = -1;
Erik Språng51e60302016-06-10 22:13:21 +020053 current_window_size_ms_ = max_window_size_ms_;
Erik Språng03d9e522020-05-25 12:58:03 +020054 buckets_.clear();
sprang@webrtc.org37968a92013-12-03 10:31:59 +000055}
56
Harald Alvestranda846cef2020-01-15 14:02:12 +010057void RateStatistics::Update(int64_t count, int64_t now_ms) {
Erik Språng03d9e522020-05-25 12:58:03 +020058 RTC_DCHECK_GE(count, 0);
sprang@webrtc.org37968a92013-12-03 10:31:59 +000059
60 EraseOld(now_ms);
Yun Zhang4774a9f2021-11-23 01:11:20 -080061 if (first_timestamp_ == -1 || num_samples_ == 0) {
Erik Språng03d9e522020-05-25 12:58:03 +020062 first_timestamp_ = now_ms;
63 }
sprang@webrtc.org37968a92013-12-03 10:31:59 +000064
Erik Språng03d9e522020-05-25 12:58:03 +020065 if (buckets_.empty() || now_ms != buckets_.back().timestamp) {
66 if (!buckets_.empty() && now_ms < buckets_.back().timestamp) {
67 RTC_LOG(LS_WARNING) << "Timestamp " << now_ms
68 << " is before the last added "
69 "timestamp in the rate window: "
70 << buckets_.back().timestamp << ", aligning to that.";
71 now_ms = buckets_.back().timestamp;
72 }
73 buckets_.emplace_back(now_ms);
74 }
75 Bucket& last_bucket = buckets_.back();
76 last_bucket.sum += count;
77 ++last_bucket.num_samples;
Erik Språng51e60302016-06-10 22:13:21 +020078
Harald Alvestranda846cef2020-01-15 14:02:12 +010079 if (std::numeric_limits<int64_t>::max() - accumulated_count_ > count) {
80 accumulated_count_ += count;
81 } else {
82 overflow_ = true;
83 }
Erik Språng51e60302016-06-10 22:13:21 +020084 ++num_samples_;
sprang@webrtc.org37968a92013-12-03 10:31:59 +000085}
86
Harald Alvestranda846cef2020-01-15 14:02:12 +010087absl::optional<int64_t> RateStatistics::Rate(int64_t now_ms) const {
sprangcd349d92016-07-13 09:11:28 -070088 // Yeah, this const_cast ain't pretty, but the alternative is to declare most
89 // of the members as mutable...
90 const_cast<RateStatistics*>(this)->EraseOld(now_ms);
Erik Språng51e60302016-06-10 22:13:21 +020091
Erik Språng03d9e522020-05-25 12:58:03 +020092 int active_window_size = 0;
93 if (first_timestamp_ != -1) {
94 if (first_timestamp_ <= now_ms - current_window_size_ms_) {
95 // Count window as full even if no data points currently in view, if the
96 // data stream started before the window.
97 active_window_size = current_window_size_ms_;
98 } else {
99 // Size of a single bucket is 1ms, so even if now_ms == first_timestmap_
100 // the window size should be 1.
101 active_window_size = now_ms - first_timestamp_ + 1;
102 }
103 }
104
Erik Språng51e60302016-06-10 22:13:21 +0200105 // If window is a single bucket or there is only one sample in a data set that
Harald Alvestranda846cef2020-01-15 14:02:12 +0100106 // has not grown to the full window size, or if the accumulator has
107 // overflowed, treat this as rate unavailable.
Erik Språng51e60302016-06-10 22:13:21 +0200108 if (num_samples_ == 0 || active_window_size <= 1 ||
Harald Alvestranda846cef2020-01-15 14:02:12 +0100109 (num_samples_ <= 1 &&
110 rtc::SafeLt(active_window_size, current_window_size_ms_)) ||
111 overflow_) {
Danil Chapovalov0a1d1892018-06-21 11:48:25 +0200112 return absl::nullopt;
Erik Språng51e60302016-06-10 22:13:21 +0200113 }
114
Harald Alvestranda846cef2020-01-15 14:02:12 +0100115 float scale = static_cast<float>(scale_) / active_window_size;
Yves Gereya688d112019-12-31 16:16:51 +0100116 float result = accumulated_count_ * scale + 0.5f;
117
118 // Better return unavailable rate than garbage value (undefined behavior).
Harald Alvestranda846cef2020-01-15 14:02:12 +0100119 if (result > static_cast<float>(std::numeric_limits<int64_t>::max())) {
Yves Gereya688d112019-12-31 16:16:51 +0100120 return absl::nullopt;
121 }
Harald Alvestranda846cef2020-01-15 14:02:12 +0100122 return rtc::dchecked_cast<int64_t>(result);
sprang@webrtc.org37968a92013-12-03 10:31:59 +0000123}
124
125void RateStatistics::EraseOld(int64_t now_ms) {
Erik Språng51e60302016-06-10 22:13:21 +0200126 // New oldest time that is included in data set.
Erik Språng03d9e522020-05-25 12:58:03 +0200127 const int64_t new_oldest_time = now_ms - current_window_size_ms_ + 1;
Erik Språng51e60302016-06-10 22:13:21 +0200128
129 // Loop over buckets and remove too old data points.
Erik Språng03d9e522020-05-25 12:58:03 +0200130 while (!buckets_.empty() && buckets_.front().timestamp < new_oldest_time) {
131 const Bucket& oldest_bucket = buckets_.front();
Erik Språng51e60302016-06-10 22:13:21 +0200132 RTC_DCHECK_GE(accumulated_count_, oldest_bucket.sum);
Erik Språng03d9e522020-05-25 12:58:03 +0200133 RTC_DCHECK_GE(num_samples_, oldest_bucket.num_samples);
Erik Språng51e60302016-06-10 22:13:21 +0200134 accumulated_count_ -= oldest_bucket.sum;
Erik Språng03d9e522020-05-25 12:58:03 +0200135 num_samples_ -= oldest_bucket.num_samples;
136 buckets_.pop_front();
Harald Alvestranda846cef2020-01-15 14:02:12 +0100137 // This does not clear overflow_ even when counter is empty.
138 // TODO(https://bugs.webrtc.org/11247): Consider if overflow_ can be reset.
sprang@webrtc.org37968a92013-12-03 10:31:59 +0000139 }
sprang@webrtc.org37968a92013-12-03 10:31:59 +0000140}
141
Erik Språng51e60302016-06-10 22:13:21 +0200142bool RateStatistics::SetWindowSize(int64_t window_size_ms, int64_t now_ms) {
143 if (window_size_ms <= 0 || window_size_ms > max_window_size_ms_)
144 return false;
Erik Språng03d9e522020-05-25 12:58:03 +0200145 if (first_timestamp_ != -1) {
146 // If the window changes (e.g. decreases - removing data point, then
147 // increases again) we need to update the first timestamp mark as
148 // otherwise it indicates the window coveres a region of zeros, suddenly
149 // under-estimating the rate.
150 first_timestamp_ = std::max(first_timestamp_, now_ms - window_size_ms + 1);
151 }
Erik Språng51e60302016-06-10 22:13:21 +0200152 current_window_size_ms_ = window_size_ms;
153 EraseOld(now_ms);
154 return true;
155}
156
sprang@webrtc.org37968a92013-12-03 10:31:59 +0000157} // namespace webrtc