 Chromium Code Reviews
 Chromium Code Reviews Issue 2307913002:
  Update AvgCounter to have the ability to include last period metric for subsequent intervals withou…  (Closed)
    
  
    Issue 2307913002:
  Update AvgCounter to have the ability to include last period metric for subsequent intervals withou…  (Closed) 
  | OLD | NEW | 
|---|---|
| 1 /* | 1 /* | 
| 2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. | 
| 3 * | 3 * | 
| 4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found | 
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may | 
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. | 
| 9 */ | 9 */ | 
| 10 | 10 | 
| 11 #include "webrtc/video/stats_counter.h" | 11 #include "webrtc/video/stats_counter.h" | 
| 12 | 12 | 
| 13 #include <algorithm> | 13 #include <algorithm> | 
| 14 | 14 | 
| 15 #include "webrtc/base/checks.h" | |
| 15 #include "webrtc/system_wrappers/include/clock.h" | 16 #include "webrtc/system_wrappers/include/clock.h" | 
| 16 | 17 | 
| 17 namespace webrtc { | 18 namespace webrtc { | 
| 18 | 19 | 
| 19 namespace { | 20 namespace { | 
| 20 // Periodic time interval for processing samples. | 21 // Periodic time interval for processing samples. | 
| 21 const int64_t kProcessIntervalMs = 2000; | 22 const int64_t kProcessIntervalMs = 2000; | 
| 22 } // namespace | 23 } // namespace | 
| 23 | 24 | 
| 24 // Class holding periodically computed metrics. | 25 // Class holding periodically computed metrics. | 
| 25 class AggregatedCounter { | 26 class AggregatedCounter { | 
| 26 public: | 27 public: | 
| 27 AggregatedCounter() : sum_(0) {} | 28 AggregatedCounter() : last_sample_(0), sum_samples_(0) {} | 
| 28 ~AggregatedCounter() {} | 29 ~AggregatedCounter() {} | 
| 29 | 30 | 
| 30 void Add(int sample) { | 31 void Add(int sample) { | 
| 31 sum_ += sample; | 32 last_sample_ = sample; | 
| 33 sum_samples_ += sample; | |
| 32 ++stats_.num_samples; | 34 ++stats_.num_samples; | 
| 33 if (stats_.num_samples == 1) { | 35 if (stats_.num_samples == 1) { | 
| 34 stats_.min = sample; | 36 stats_.min = sample; | 
| 35 stats_.max = sample; | 37 stats_.max = sample; | 
| 36 } | 38 } | 
| 37 stats_.min = std::min(sample, stats_.min); | 39 stats_.min = std::min(sample, stats_.min); | 
| 38 stats_.max = std::max(sample, stats_.max); | 40 stats_.max = std::max(sample, stats_.max); | 
| 39 } | 41 } | 
| 40 | 42 | 
| 41 AggregatedStats ComputeStats() { | 43 AggregatedStats ComputeStats() { | 
| 42 Compute(); | 44 Compute(); | 
| 43 return stats_; | 45 return stats_; | 
| 44 } | 46 } | 
| 45 | 47 | 
| 48 bool HasSamples() const { return stats_.num_samples > 0; } | |
| 
stefan-webrtc
2016/09/08 07:28:14
Maybe call this Empty() instead to align with inte
 
åsapersson
2016/09/08 13:15:02
Done.
 | |
| 49 | |
| 50 int last_sample() const { return last_sample_; } | |
| 51 | |
| 46 private: | 52 private: | 
| 47 void Compute() { | 53 void Compute() { | 
| 48 if (stats_.num_samples == 0) | 54 if (stats_.num_samples == 0) | 
| 49 return; | 55 return; | 
| 50 | 56 | 
| 51 stats_.average = (sum_ + stats_.num_samples / 2) / stats_.num_samples; | 57 stats_.average = | 
| 58 (sum_samples_ + stats_.num_samples / 2) / stats_.num_samples; | |
| 52 } | 59 } | 
| 53 int64_t sum_; | 60 int last_sample_; | 
| 61 int64_t sum_samples_; | |
| 54 AggregatedStats stats_; | 62 AggregatedStats stats_; | 
| 55 }; | 63 }; | 
| 56 | 64 | 
| 57 // StatsCounter class. | 65 // StatsCounter class. | 
| 58 StatsCounter::StatsCounter(Clock* clock, | 66 StatsCounter::StatsCounter(Clock* clock, | 
| 59 bool include_empty_intervals, | 67 bool include_empty_intervals, | 
| 60 StatsCounterObserver* observer) | 68 StatsCounterObserver* observer) | 
| 61 : max_(0), | 69 : max_(0), | 
| 62 sum_(0), | 70 sum_(0), | 
| 63 num_samples_(0), | 71 num_samples_(0), | 
| 64 last_sum_(0), | 72 last_sum_(0), | 
| 73 aggregated_counter_(new AggregatedCounter()), | |
| 65 clock_(clock), | 74 clock_(clock), | 
| 66 include_empty_intervals_(include_empty_intervals), | 75 include_empty_intervals_(include_empty_intervals), | 
| 67 observer_(observer), | 76 observer_(observer), | 
| 68 aggregated_counter_(new AggregatedCounter()), | 77 last_process_time_ms_(-1), | 
| 69 last_process_time_ms_(-1) {} | 78 paused_(false) {} | 
| 70 | 79 | 
| 71 StatsCounter::~StatsCounter() {} | 80 StatsCounter::~StatsCounter() {} | 
| 72 | 81 | 
| 73 AggregatedStats StatsCounter::GetStats() { | 82 AggregatedStats StatsCounter::GetStats() { | 
| 74 return aggregated_counter_->ComputeStats(); | 83 return aggregated_counter_->ComputeStats(); | 
| 75 } | 84 } | 
| 76 | 85 | 
| 77 bool StatsCounter::TimeToProcess() { | 86 AggregatedStats StatsCounter::ProcessAndGetStats() { | 
| 87 if (last_process_time_ms_ != -1) | |
| 88 TryProcess(); // At least one sample is added. | |
| 89 return aggregated_counter_->ComputeStats(); | |
| 90 } | |
| 91 | |
| 92 void StatsCounter::ProcessAndPause() { | |
| 93 if (last_process_time_ms_ != -1) | |
| 94 TryProcess(); // At least one sample is added. | |
| 95 paused_ = true; | |
| 96 } | |
| 97 | |
| 98 bool StatsCounter::TimeToProcess(int* elapsed_intervals) { | |
| 78 int64_t now = clock_->TimeInMilliseconds(); | 99 int64_t now = clock_->TimeInMilliseconds(); | 
| 79 if (last_process_time_ms_ == -1) | 100 if (last_process_time_ms_ == -1) | 
| 80 last_process_time_ms_ = now; | 101 last_process_time_ms_ = now; | 
| 81 | 102 | 
| 82 int64_t diff_ms = now - last_process_time_ms_; | 103 int64_t diff_ms = now - last_process_time_ms_; | 
| 83 if (diff_ms < kProcessIntervalMs) | 104 if (diff_ms < kProcessIntervalMs) | 
| 84 return false; | 105 return false; | 
| 85 | 106 | 
| 86 // Advance number of complete kProcessIntervalMs that have passed. | 107 // Advance number of complete kProcessIntervalMs that have passed. | 
| 87 int64_t num_intervals = diff_ms / kProcessIntervalMs; | 108 int64_t num_intervals = diff_ms / kProcessIntervalMs; | 
| 88 last_process_time_ms_ += num_intervals * kProcessIntervalMs; | 109 last_process_time_ms_ += num_intervals * kProcessIntervalMs; | 
| 89 | 110 | 
| 90 // Add zero for intervals without samples. | 111 *elapsed_intervals = num_intervals; | 
| 91 if (include_empty_intervals_) { | |
| 92 for (int64_t i = 0; i < num_intervals - 1; ++i) { | |
| 93 aggregated_counter_->Add(0); | |
| 94 if (observer_) | |
| 95 observer_->OnMetricUpdated(0); | |
| 96 } | |
| 97 } | |
| 98 return true; | 112 return true; | 
| 99 } | 113 } | 
| 100 | 114 | 
| 101 void StatsCounter::Set(int sample) { | 115 void StatsCounter::Set(int sample) { | 
| 102 TryProcess(); | 116 TryProcess(); | 
| 103 ++num_samples_; | 117 ++num_samples_; | 
| 104 sum_ = sample; | 118 sum_ = sample; | 
| 119 paused_ = false; | |
| 105 } | 120 } | 
| 106 | 121 | 
| 107 void StatsCounter::Add(int sample) { | 122 void StatsCounter::Add(int sample) { | 
| 108 TryProcess(); | 123 TryProcess(); | 
| 109 ++num_samples_; | 124 ++num_samples_; | 
| 110 sum_ += sample; | 125 sum_ += sample; | 
| 111 | 126 | 
| 112 if (num_samples_ == 1) | 127 if (num_samples_ == 1) | 
| 113 max_ = sample; | 128 max_ = sample; | 
| 114 max_ = std::max(sample, max_); | 129 max_ = std::max(sample, max_); | 
| 130 paused_ = false; | |
| 131 } | |
| 132 | |
| 133 // Reports periodically computed metric. | |
| 134 void StatsCounter::ReportMetricToAggregatedCounter( | |
| 135 int value, | |
| 136 int num_values_to_add) const { | |
| 137 for (int i = 0; i < num_values_to_add; ++i) { | |
| 138 aggregated_counter_->Add(value); | |
| 139 if (observer_) | |
| 140 observer_->OnMetricUpdated(value); | |
| 141 } | |
| 115 } | 142 } | 
| 116 | 143 | 
| 117 void StatsCounter::TryProcess() { | 144 void StatsCounter::TryProcess() { | 
| 118 if (!TimeToProcess()) | 145 int elapsed_intervals; | 
| 146 if (!TimeToProcess(&elapsed_intervals)) | |
| 119 return; | 147 return; | 
| 120 | 148 | 
| 149 // Get and report periodically computed metric. | |
| 121 int metric; | 150 int metric; | 
| 122 if (GetMetric(&metric)) { | 151 if (GetMetric(&metric)) | 
| 123 aggregated_counter_->Add(metric); | 152 ReportMetricToAggregatedCounter(metric, 1); | 
| 124 if (observer_) | 153 | 
| 125 observer_->OnMetricUpdated(metric); | 154 // Report value for elapsed intervals without samples. | 
| 155 if (IncludeEmptyIntervals()) { | |
| 156 int empty_intervals = | |
| 157 (num_samples_ == 0) ? elapsed_intervals : (elapsed_intervals - 1); | |
| 
stefan-webrtc
2016/09/08 07:28:14
I don't follow this, maybe add a comment on why we
 
åsapersson
2016/09/08 13:15:02
Done.
 | |
| 158 ReportMetricToAggregatedCounter(GetValueForEmptyInterval(), | |
| 159 empty_intervals); | |
| 126 } | 160 } | 
| 127 last_sum_ = sum_; | 161 | 
| 162 // Reset samples for elapsed interval. | |
| 163 if (num_samples_ > 0) | |
| 164 last_sum_ = sum_; | |
| 128 sum_ = 0; | 165 sum_ = 0; | 
| 129 max_ = 0; | 166 max_ = 0; | 
| 130 num_samples_ = 0; | 167 num_samples_ = 0; | 
| 131 } | 168 } | 
| 132 | 169 | 
| 170 bool StatsCounter::IncludeEmptyIntervals() const { | |
| 171 return include_empty_intervals_ && !paused_ && | |
| 172 aggregated_counter_->HasSamples(); | |
| 173 } | |
| 174 | |
| 133 // StatsCounter sub-classes. | 175 // StatsCounter sub-classes. | 
| 134 AvgCounter::AvgCounter(Clock* clock, StatsCounterObserver* observer) | 176 AvgCounter::AvgCounter(Clock* clock, | 
| 135 : StatsCounter(clock, | 177 StatsCounterObserver* observer, | 
| 136 false, // |include_empty_intervals| | 178 bool include_empty_intervals) | 
| 137 observer) {} | 179 : StatsCounter(clock, include_empty_intervals, observer) {} | 
| 138 | 180 | 
| 139 void AvgCounter::Add(int sample) { | 181 void AvgCounter::Add(int sample) { | 
| 140 StatsCounter::Add(sample); | 182 StatsCounter::Add(sample); | 
| 141 } | 183 } | 
| 142 | 184 | 
| 143 bool AvgCounter::GetMetric(int* metric) const { | 185 bool AvgCounter::GetMetric(int* metric) const { | 
| 144 if (num_samples_ == 0) | 186 if (num_samples_ == 0) | 
| 145 return false; | 187 return false; | 
| 146 *metric = (sum_ + num_samples_ / 2) / num_samples_; | 188 *metric = (sum_ + num_samples_ / 2) / num_samples_; | 
| 147 return true; | 189 return true; | 
| 148 } | 190 } | 
| 149 | 191 | 
| 192 int AvgCounter::GetValueForEmptyInterval() const { | |
| 193 return aggregated_counter_->last_sample(); | |
| 194 } | |
| 195 | |
| 150 MaxCounter::MaxCounter(Clock* clock, StatsCounterObserver* observer) | 196 MaxCounter::MaxCounter(Clock* clock, StatsCounterObserver* observer) | 
| 151 : StatsCounter(clock, | 197 : StatsCounter(clock, | 
| 152 false, // |include_empty_intervals| | 198 false, // |include_empty_intervals| | 
| 153 observer) {} | 199 observer) {} | 
| 154 | 200 | 
| 155 void MaxCounter::Add(int sample) { | 201 void MaxCounter::Add(int sample) { | 
| 156 StatsCounter::Add(sample); | 202 StatsCounter::Add(sample); | 
| 157 } | 203 } | 
| 158 | 204 | 
| 159 bool MaxCounter::GetMetric(int* metric) const { | 205 bool MaxCounter::GetMetric(int* metric) const { | 
| 160 if (num_samples_ == 0) | 206 if (num_samples_ == 0) | 
| 161 return false; | 207 return false; | 
| 162 *metric = max_; | 208 *metric = max_; | 
| 163 return true; | 209 return true; | 
| 164 } | 210 } | 
| 165 | 211 | 
| 212 int MaxCounter::GetValueForEmptyInterval() const { | |
| 213 RTC_NOTREACHED(); | |
| 214 return 0; | |
| 215 } | |
| 216 | |
| 166 PercentCounter::PercentCounter(Clock* clock, StatsCounterObserver* observer) | 217 PercentCounter::PercentCounter(Clock* clock, StatsCounterObserver* observer) | 
| 167 : StatsCounter(clock, | 218 : StatsCounter(clock, | 
| 168 false, // |include_empty_intervals| | 219 false, // |include_empty_intervals| | 
| 169 observer) {} | 220 observer) {} | 
| 170 | 221 | 
| 171 void PercentCounter::Add(bool sample) { | 222 void PercentCounter::Add(bool sample) { | 
| 172 StatsCounter::Add(sample ? 1 : 0); | 223 StatsCounter::Add(sample ? 1 : 0); | 
| 173 } | 224 } | 
| 174 | 225 | 
| 175 bool PercentCounter::GetMetric(int* metric) const { | 226 bool PercentCounter::GetMetric(int* metric) const { | 
| 176 if (num_samples_ == 0) | 227 if (num_samples_ == 0) | 
| 177 return false; | 228 return false; | 
| 178 *metric = (sum_ * 100 + num_samples_ / 2) / num_samples_; | 229 *metric = (sum_ * 100 + num_samples_ / 2) / num_samples_; | 
| 179 return true; | 230 return true; | 
| 180 } | 231 } | 
| 181 | 232 | 
| 233 int PercentCounter::GetValueForEmptyInterval() const { | |
| 234 RTC_NOTREACHED(); | |
| 235 return 0; | |
| 236 } | |
| 237 | |
| 182 PermilleCounter::PermilleCounter(Clock* clock, StatsCounterObserver* observer) | 238 PermilleCounter::PermilleCounter(Clock* clock, StatsCounterObserver* observer) | 
| 183 : StatsCounter(clock, | 239 : StatsCounter(clock, | 
| 184 false, // |include_empty_intervals| | 240 false, // |include_empty_intervals| | 
| 185 observer) {} | 241 observer) {} | 
| 186 | 242 | 
| 187 void PermilleCounter::Add(bool sample) { | 243 void PermilleCounter::Add(bool sample) { | 
| 188 StatsCounter::Add(sample ? 1 : 0); | 244 StatsCounter::Add(sample ? 1 : 0); | 
| 189 } | 245 } | 
| 190 | 246 | 
| 191 bool PermilleCounter::GetMetric(int* metric) const { | 247 bool PermilleCounter::GetMetric(int* metric) const { | 
| 192 if (num_samples_ == 0) | 248 if (num_samples_ == 0) | 
| 193 return false; | 249 return false; | 
| 194 *metric = (sum_ * 1000 + num_samples_ / 2) / num_samples_; | 250 *metric = (sum_ * 1000 + num_samples_ / 2) / num_samples_; | 
| 195 return true; | 251 return true; | 
| 196 } | 252 } | 
| 197 | 253 | 
| 254 int PermilleCounter::GetValueForEmptyInterval() const { | |
| 255 RTC_NOTREACHED(); | |
| 256 return 0; | |
| 257 } | |
| 258 | |
| 198 RateCounter::RateCounter(Clock* clock, | 259 RateCounter::RateCounter(Clock* clock, | 
| 199 StatsCounterObserver* observer, | 260 StatsCounterObserver* observer, | 
| 200 bool include_empty_intervals) | 261 bool include_empty_intervals) | 
| 201 : StatsCounter(clock, include_empty_intervals, observer) {} | 262 : StatsCounter(clock, include_empty_intervals, observer) {} | 
| 202 | 263 | 
| 203 void RateCounter::Add(int sample) { | 264 void RateCounter::Add(int sample) { | 
| 204 StatsCounter::Add(sample); | 265 StatsCounter::Add(sample); | 
| 205 } | 266 } | 
| 206 | 267 | 
| 207 bool RateCounter::GetMetric(int* metric) const { | 268 bool RateCounter::GetMetric(int* metric) const { | 
| 208 if (num_samples_ == 0) | 269 if (num_samples_ == 0) | 
| 209 return false; | 270 return false; | 
| 210 *metric = (sum_ * 1000 + kProcessIntervalMs / 2) / kProcessIntervalMs; | 271 *metric = (sum_ * 1000 + kProcessIntervalMs / 2) / kProcessIntervalMs; | 
| 211 return true; | 272 return true; | 
| 212 } | 273 } | 
| 213 | 274 | 
| 275 int RateCounter::GetValueForEmptyInterval() const { | |
| 276 return 0; | |
| 277 } | |
| 278 | |
| 214 RateAccCounter::RateAccCounter(Clock* clock, | 279 RateAccCounter::RateAccCounter(Clock* clock, | 
| 215 StatsCounterObserver* observer, | 280 StatsCounterObserver* observer, | 
| 216 bool include_empty_intervals) | 281 bool include_empty_intervals) | 
| 217 : StatsCounter(clock, include_empty_intervals, observer) {} | 282 : StatsCounter(clock, include_empty_intervals, observer) {} | 
| 218 | 283 | 
| 219 void RateAccCounter::Set(int sample) { | 284 void RateAccCounter::Set(int sample) { | 
| 220 StatsCounter::Set(sample); | 285 StatsCounter::Set(sample); | 
| 221 } | 286 } | 
| 222 | 287 | 
| 223 bool RateAccCounter::GetMetric(int* metric) const { | 288 bool RateAccCounter::GetMetric(int* metric) const { | 
| 224 if (num_samples_ == 0 || last_sum_ > sum_) | 289 if (num_samples_ == 0 || last_sum_ > sum_) | 
| 225 return false; | 290 return false; | 
| 226 *metric = | 291 *metric = | 
| 227 ((sum_ - last_sum_) * 1000 + kProcessIntervalMs / 2) / kProcessIntervalMs; | 292 ((sum_ - last_sum_) * 1000 + kProcessIntervalMs / 2) / kProcessIntervalMs; | 
| 228 return true; | 293 return true; | 
| 229 } | 294 } | 
| 230 | 295 | 
| 296 int RateAccCounter::GetValueForEmptyInterval() const { | |
| 297 return 0; | |
| 298 } | |
| 299 | |
| 231 } // namespace webrtc | 300 } // namespace webrtc | 
| OLD | NEW |