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 |