Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 /* | |
| 2 * Copyright (c) 2016 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 | |
| 11 #include "webrtc/video/stats_counter.h" | |
| 12 | |
| 13 #include <algorithm> | |
| 14 | |
| 15 #include "webrtc/base/checks.h" | |
| 16 #include "webrtc/base/logging.h" | |
| 17 #include "webrtc/system_wrappers/include/clock.h" | |
| 18 | |
| 19 namespace webrtc { | |
| 20 | |
| 21 namespace { | |
| 22 // Periodic time interval for processing samples. | |
| 23 const int64_t kProcessIntervalMs = 2000; | |
| 24 | |
| 25 // Limit for the maximum number of buckets to use. | |
| 26 const size_t kMaxBuckets = 250; | |
| 27 } // namespace | |
| 28 | |
| 29 StatsCounter::Histogram::Stats::Stats() | |
| 30 : num_samples(0), | |
| 31 min(-1), | |
| 32 max(-1), | |
| 33 average(-1), | |
| 34 percentile10(-1), | |
| 35 percentile50(-1), | |
| 36 percentile90(-1) {} | |
| 37 | |
| 38 // Histogram class. | |
| 39 StatsCounter::Histogram::Histogram(size_t bucket_count, size_t bucket_max) | |
| 40 : bucket_count_(bucket_count), | |
| 41 bucket_max_(bucket_max), | |
| 42 bucket_size1_(bucket_count == bucket_max), | |
|
stefan-webrtc
2016/01/29 15:22:46
I don't think I fully understand what this variabl
| |
| 43 sum_(0) { | |
| 44 RTC_CHECK_GT(kProcessIntervalMs, 0); | |
| 45 RTC_CHECK_GT(bucket_count, 0u); | |
| 46 RTC_CHECK_GT(bucket_max, 0u); | |
| 47 RTC_CHECK_LE(bucket_count, bucket_max); | |
| 48 InitializeBuckets(); | |
| 49 } | |
| 50 | |
| 51 void StatsCounter::Histogram::InitializeBuckets() { | |
| 52 if (bucket_size1_) { | |
| 53 // Buckets created when needed. | |
| 54 return; | |
| 55 } | |
| 56 RTC_CHECK_LE(bucket_count_, kMaxBuckets); | |
| 57 double log_max = log(static_cast<double>(bucket_max_)); | |
| 58 buckets_[0] = 0; // [0, bucket min) | |
| 59 size_t cur = 1; // bucket min | |
| 60 buckets_[cur] = 0; // [bucket min,...) | |
| 61 for (size_t i = 2; i < bucket_count_; ++i) { | |
| 62 // cur * x^(remaining buckets) = max | |
| 63 double log_cur = log(static_cast<double>(cur)); | |
| 64 double log_x = (log_max - log_cur) / (bucket_count_ - i); | |
| 65 double log_next = log_cur + log_x; | |
|
stefan-webrtc
2016/01/29 15:22:46
I'm trying to follow this and I have a bit of a ha
| |
| 66 size_t next = static_cast<size_t>(exp(log_next) + 0.5); | |
| 67 cur = (next > cur) ? next : (cur + 1); | |
| 68 buckets_[cur] = 0; // [cur,...) | |
| 69 } | |
| 70 RTC_DCHECK(buckets_.rbegin()->first == 1 || | |
| 71 buckets_.rbegin()->first == bucket_max_); | |
| 72 } | |
| 73 | |
| 74 void StatsCounter::Histogram::Add(int sample) { | |
| 75 sum_ += sample; | |
| 76 ++stats_.num_samples; | |
| 77 if (stats_.num_samples == 1) { | |
| 78 stats_.min = sample; | |
| 79 stats_.max = sample; | |
| 80 } | |
| 81 stats_.min = std::min(sample, stats_.min); | |
| 82 stats_.max = std::max(sample, stats_.max); | |
| 83 | |
| 84 if (sample < 0) | |
| 85 sample = 0; | |
| 86 | |
| 87 AddToBucket(sample); | |
| 88 } | |
| 89 | |
| 90 void StatsCounter::Histogram::AddToBucket(size_t sample) { | |
| 91 if (sample > bucket_max_) | |
| 92 sample = bucket_max_; | |
| 93 | |
| 94 if (bucket_size1_) { | |
| 95 if (buckets_.size() == kMaxBuckets && | |
| 96 buckets_.find(sample) == buckets_.end()) { | |
| 97 LOG(LS_WARNING) << "Max buckets reached. Sample not added " << sample; | |
| 98 return; | |
| 99 } | |
| 100 ++buckets_[sample]; | |
| 101 return; | |
| 102 } | |
| 103 | |
| 104 // Find bucket. | |
| 105 RTC_DCHECK(buckets_.size() >= 2); | |
| 106 auto it = buckets_.upper_bound(sample); | |
| 107 --it; | |
| 108 it->second++; | |
| 109 } | |
| 110 | |
| 111 StatsCounter::Histogram::Stats StatsCounter::Histogram::stats() { | |
| 112 Compute(); | |
| 113 return stats_; | |
| 114 } | |
| 115 | |
| 116 void StatsCounter::Histogram::Compute() { | |
| 117 if (stats_.num_samples == 0) | |
| 118 return; | |
| 119 | |
| 120 stats_.average = (sum_ + (stats_.num_samples / 2)) / stats_.num_samples; | |
| 121 | |
| 122 size_t sample10 = round(std::max(stats_.num_samples * 0.1f, 1.0f)); | |
| 123 size_t sample50 = round(std::max(stats_.num_samples * 0.5f, 1.0f)); | |
| 124 size_t sample90 = round(std::max(stats_.num_samples * 0.9f, 1.0f)); | |
| 125 | |
| 126 stats_.percentile10 = -1; | |
| 127 stats_.percentile50 = -1; | |
| 128 stats_.percentile90 = -1; | |
| 129 | |
| 130 size_t samples = 0; | |
| 131 int last = 0; | |
| 132 for (const auto bucket : buckets_) { | |
| 133 int cur = static_cast<int>(bucket.first); | |
| 134 samples += bucket.second; | |
| 135 // 10th percentile. | |
| 136 if (!bucket_size1_ && stats_.percentile10 == last) | |
| 137 stats_.percentile10 += ((cur - last) / 2); | |
| 138 if (samples >= sample10 && stats_.percentile10 == -1) | |
| 139 stats_.percentile10 = cur; | |
| 140 // 50th percentile. | |
| 141 if (!bucket_size1_ && stats_.percentile50 == last) | |
| 142 stats_.percentile50 += ((cur - last) / 2); | |
| 143 if (samples >= sample50 && stats_.percentile50 == -1) | |
| 144 stats_.percentile50 = cur; | |
| 145 // 90th percentile. | |
| 146 if (!bucket_size1_ && stats_.percentile90 == last) | |
| 147 stats_.percentile90 += ((cur - last) / 2); | |
| 148 if (samples >= sample90 && stats_.percentile90 == -1) | |
| 149 stats_.percentile90 = cur; | |
| 150 last = cur; | |
| 151 } | |
| 152 | |
| 153 // Limit percentiles by max (mid bucket value used). | |
| 154 if (stats_.max > 0) { | |
| 155 stats_.percentile10 = std::min(stats_.percentile10, stats_.max); | |
| 156 stats_.percentile50 = std::min(stats_.percentile50, stats_.max); | |
| 157 stats_.percentile90 = std::min(stats_.percentile90, stats_.max); | |
| 158 } | |
| 159 } | |
| 160 | |
| 161 // StatsCounter class. | |
| 162 StatsCounter::StatsCounter(Clock* clock, | |
| 163 size_t bucket_count, | |
| 164 size_t bucket_max, | |
| 165 bool include_empty_intervals, | |
| 166 StatsCounterObserver* observer) | |
| 167 : sum_(0), | |
| 168 num_samples_(0), | |
| 169 last_sum_(0), | |
| 170 clock_(clock), | |
| 171 include_empty_intervals_(include_empty_intervals), | |
| 172 observer_(observer), | |
| 173 last_process_time_ms_(-1), | |
| 174 histogram_(bucket_count, bucket_max) {} | |
| 175 | |
| 176 StatsCounter::Histogram::Stats StatsCounter::GetStats() { | |
| 177 return histogram_.stats(); | |
| 178 } | |
| 179 | |
| 180 bool StatsCounter::TimeToProcess() { | |
| 181 int64_t now = clock_->TimeInMilliseconds(); | |
| 182 if (last_process_time_ms_ == -1) | |
| 183 last_process_time_ms_ = now; | |
| 184 | |
| 185 int64_t diff_ms = now - last_process_time_ms_; | |
| 186 if (diff_ms < kProcessIntervalMs) | |
| 187 return false; | |
| 188 | |
| 189 // Advance number of complete kProcessIntervalMs that have passed. | |
| 190 int64_t num_intervals = diff_ms / kProcessIntervalMs; | |
| 191 last_process_time_ms_ += num_intervals * kProcessIntervalMs; | |
| 192 | |
| 193 // Add zero for intervals without samples. | |
| 194 if (include_empty_intervals_) { | |
| 195 for (int64_t i = 0; i < num_intervals - 1; ++i) { | |
| 196 histogram_.Add(0); | |
| 197 if (observer_) | |
| 198 observer_->OnMetricUpdated(0); | |
| 199 } | |
| 200 } | |
| 201 return true; | |
| 202 } | |
| 203 | |
| 204 void StatsCounter::Set(int sample) { | |
| 205 Process(); | |
| 206 ++num_samples_; | |
| 207 sum_ = sample; | |
| 208 } | |
| 209 | |
| 210 void StatsCounter::Add(int sample) { | |
| 211 Process(); | |
| 212 ++num_samples_; | |
| 213 sum_ += sample; | |
| 214 } | |
| 215 | |
| 216 void StatsCounter::Process() { | |
| 217 if (!TimeToProcess()) | |
| 218 return; | |
| 219 | |
| 220 int metric; | |
| 221 if (GetMetric(&metric)) { | |
| 222 histogram_.Add(metric); | |
| 223 if (observer_) | |
| 224 observer_->OnMetricUpdated(metric); | |
| 225 } | |
| 226 last_sum_ = sum_; | |
| 227 sum_ = 0; | |
| 228 num_samples_ = 0; | |
| 229 } | |
| 230 | |
| 231 // StatsCounter sub-classes. | |
| 232 AvgCounter::AvgCounter(Clock* clock, | |
| 233 size_t bucket_count, | |
| 234 size_t bucket_max, | |
| 235 StatsCounterObserver* observer) | |
| 236 : StatsCounter::StatsCounter(clock, | |
| 237 bucket_count, | |
| 238 bucket_max, | |
| 239 false, // include_empty_intervals | |
| 240 observer) {} | |
| 241 | |
| 242 void AvgCounter::Add(int sample) { | |
| 243 StatsCounter::Add(sample); | |
| 244 } | |
| 245 | |
| 246 bool AvgCounter::GetMetric(int* metric) const { | |
| 247 if (num_samples_ == 0) | |
| 248 return false; | |
| 249 *metric = (sum_ + (num_samples_ / 2)) / num_samples_; | |
| 250 return true; | |
| 251 } | |
| 252 | |
| 253 PercentCounter::PercentCounter(Clock* clock, StatsCounterObserver* observer) | |
| 254 : StatsCounter::StatsCounter(clock, | |
| 255 101, // bucket_count | |
| 256 101, // bucket_max | |
| 257 false, // include_empty_intervals | |
| 258 observer) {} | |
| 259 | |
| 260 void PercentCounter::Add(bool sample) { | |
| 261 StatsCounter::Add(sample ? 1 : 0); | |
| 262 } | |
| 263 | |
| 264 bool PercentCounter::GetMetric(int* metric) const { | |
| 265 if (num_samples_ == 0) | |
| 266 return false; | |
| 267 *metric = (sum_ * 100 + (num_samples_ / 2)) / num_samples_; | |
| 268 return true; | |
| 269 } | |
| 270 | |
| 271 PermilleCounter::PermilleCounter(Clock* clock, | |
| 272 size_t bucket_count, | |
| 273 StatsCounterObserver* observer) | |
| 274 : StatsCounter::StatsCounter(clock, | |
| 275 bucket_count, | |
| 276 1001, // bucket_max | |
| 277 false, // include_empty_intervals | |
| 278 observer) {} | |
| 279 | |
| 280 void PermilleCounter::Add(bool sample) { | |
| 281 StatsCounter::Add(sample ? 1 : 0); | |
| 282 } | |
| 283 | |
| 284 bool PermilleCounter::GetMetric(int* metric) const { | |
| 285 if (num_samples_ == 0) | |
| 286 return false; | |
| 287 *metric = (sum_ * 1000 + (num_samples_ / 2)) / num_samples_; | |
| 288 return true; | |
| 289 } | |
| 290 | |
| 291 RateCounter::RateCounter(Clock* clock, | |
| 292 size_t bucket_count, | |
| 293 size_t bucket_max, | |
| 294 bool include_empty_intervals, | |
| 295 StatsCounterObserver* observer) | |
| 296 : StatsCounter::StatsCounter(clock, | |
| 297 bucket_count, | |
| 298 bucket_max, | |
| 299 include_empty_intervals, | |
| 300 observer) {} | |
| 301 | |
| 302 void RateCounter::Add(int sample) { | |
| 303 StatsCounter::Add(sample); | |
| 304 } | |
| 305 | |
| 306 bool RateCounter::GetMetric(int* metric) const { | |
| 307 if (num_samples_ == 0) | |
| 308 return false; | |
| 309 *metric = (sum_ * 1000 + (kProcessIntervalMs / 2)) / kProcessIntervalMs; | |
| 310 return true; | |
| 311 } | |
| 312 | |
| 313 RateAccCounter::RateAccCounter(Clock* clock, | |
| 314 size_t bucket_count, | |
| 315 size_t bucket_max, | |
| 316 bool include_empty_intervals, | |
| 317 StatsCounterObserver* observer) | |
| 318 : StatsCounter::StatsCounter(clock, | |
| 319 bucket_count, | |
| 320 bucket_max, | |
| 321 include_empty_intervals, | |
| 322 observer) {} | |
| 323 | |
| 324 void RateAccCounter::Set(int sample) { | |
| 325 StatsCounter::Set(sample); | |
| 326 } | |
| 327 | |
| 328 bool RateAccCounter::GetMetric(int* metric) const { | |
| 329 if (num_samples_ == 0 || last_sum_ > sum_) | |
| 330 return false; | |
| 331 *metric = ((sum_ - last_sum_) * 1000 + (kProcessIntervalMs / 2)) / | |
| 332 kProcessIntervalMs; | |
| 333 return true; | |
| 334 } | |
| 335 | |
| 336 } // namespace webrtc | |
| OLD | NEW |