Chromium Code Reviews| Index: webrtc/base/analytics/percentile_filter.h |
| diff --git a/webrtc/modules/video_coding/percentile_filter.cc b/webrtc/base/analytics/percentile_filter.h |
| similarity index 33% |
| rename from webrtc/modules/video_coding/percentile_filter.cc |
| rename to webrtc/base/analytics/percentile_filter.h |
| index 6495567540961c86a721b58de9ba99a51694973e..d730669cf0dcc889aab570d1422c09545296866e 100644 |
| --- a/webrtc/modules/video_coding/percentile_filter.cc |
| +++ b/webrtc/base/analytics/percentile_filter.h |
| @@ -8,25 +8,71 @@ |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| -#include "webrtc/modules/video_coding/percentile_filter.h" |
| +#ifndef WEBRTC_BASE_ANALYTICS_PERCENTILE_FILTER_H_ |
| +#define WEBRTC_BASE_ANALYTICS_PERCENTILE_FILTER_H_ |
| + |
| +#include <stdint.h> |
| #include <iterator> |
| +#include <list> |
| +#include <set> |
| #include "webrtc/base/checks.h" |
| namespace webrtc { |
| -PercentileFilter::PercentileFilter(float percentile) |
| +// Class to efficiently get the percentile value from a group of observations. |
| +// The percentile is the value below which a given percentage of the |
| +// observations fall. |
| +template <typename T> |
| +class PercentileFilter { |
| + public: |
| + // Construct filter. |percentile| should be between 0 and 1. |
| + explicit PercentileFilter(float percentile); |
| + |
| + // Insert one observation. The complexity of this operation is logarithmic in |
| + // the size of the container. |
| + void Push(const T& value); |
| + |
| + // Remove the oldest observation. The complexity of this operation is |
| + // logarithmic in the size of the container. |
| + void Pop(); |
| + |
| + // Get the percentile value. The complexity of this operation is constant. |
| + T GetPercentileValue() const; |
| + |
| + // Returns the number of observations in the container. |
| + size_t Size() const; |
| + |
| + private: |
| + // Update iterator and index to point at target percentile value. |
| + void UpdatePercentileIterator(); |
| + |
| + const float percentile_; |
| + std::list<typename std::multiset<T>::iterator> history_; |
|
magjed_webrtc
2016/11/30 10:58:25
An std::queue seems more appropriate. Any reason f
|
| + std::multiset<T> set_; |
| + // Maintain iterator and index of current target percentile value. |
| + typename std::multiset<T>::iterator percentile_it_; |
| + int64_t percentile_index_; |
| +}; |
| + |
| +template <typename T> |
| +PercentileFilter<T>::PercentileFilter(float percentile) |
| : percentile_(percentile), |
| + history_(), |
| + set_(), |
| percentile_it_(set_.begin()), |
| percentile_index_(0) { |
| RTC_CHECK_GE(percentile, 0.0f); |
| RTC_CHECK_LE(percentile, 1.0f); |
| } |
| -void PercentileFilter::Insert(const int64_t& value) { |
| - // Insert element at the upper bound. |
| - set_.insert(value); |
| +template <typename T> |
| +void PercentileFilter<T>::Push(const T& value) { |
| + // Insert element at the upper bound. This means that each range of equal |
| + // elements will be stored in the same order that they were inserted. |
| + auto it = set_.insert(value); |
| + history_.push_back(it); |
| if (set_.size() == 1u) { |
| // First element inserted - initialize percentile iterator and index. |
| percentile_it_ = set_.begin(); |
| @@ -38,24 +84,31 @@ void PercentileFilter::Insert(const int64_t& value) { |
| UpdatePercentileIterator(); |
| } |
| -void PercentileFilter::Erase(const int64_t& value) { |
| - std::multiset<int64_t>::const_iterator it = set_.lower_bound(value); |
| - // Ignore erase operation if the element is not present in the current set. |
| - if (it == set_.end() || *it != value) |
| - return; |
| +template <typename T> |
| +void PercentileFilter<T>::Pop() { |
| + RTC_DCHECK_GT(history_.size(), 0); |
|
magjed_webrtc
2016/11/30 10:58:25
nit: I think this is cleaner and will give the sam
|
| + RTC_DCHECK_GT(set_.size(), 0); |
| + auto it = history_.front(); |
| + history_.pop_front(); |
| if (it == percentile_it_) { |
| - // If same iterator, update to the following element. Index is not affected. |
| - percentile_it_ = set_.erase(it); |
| + // If removing the percentile value, update iterator to the following |
| + // element. Index is not affected. |
| + percentile_it_ = set_.erase(percentile_it_); |
| } else { |
| - set_.erase(it); |
| - // If erased element was before us, decrement |percentile_index_|. |
| - if (value <= *percentile_it_) |
| + // If erased element is before percentile, decrement |percentile_index_|. |
| + // Note that if the erased element compares equal to the percentile, but |
| + // isn't the same iterator, then it has to be before the |percentile_index_| |
| + // since each range of equal elements is stored in insertion order and we |
| + // are erasing the oldest element. |
| + if (*it <= *percentile_it_) |
| --percentile_index_; |
| + set_.erase(it); |
| } |
| UpdatePercentileIterator(); |
| } |
| -void PercentileFilter::UpdatePercentileIterator() { |
| +template <typename T> |
| +void PercentileFilter<T>::UpdatePercentileIterator() { |
| if (set_.empty()) |
| return; |
| const int64_t index = static_cast<int64_t>(percentile_ * (set_.size() - 1)); |
| @@ -63,8 +116,17 @@ void PercentileFilter::UpdatePercentileIterator() { |
| percentile_index_ = index; |
| } |
| -int64_t PercentileFilter::GetPercentileValue() const { |
| +template <typename T> |
| +T PercentileFilter<T>::GetPercentileValue() const { |
| return set_.empty() ? 0 : *percentile_it_; |
| } |
| +template <typename T> |
| +size_t PercentileFilter<T>::Size() const { |
| + RTC_DCHECK_EQ(set_.size(), history_.size()); |
| + return history_.size(); |
| +} |
| + |
| } // namespace webrtc |
| + |
| +#endif // WEBRTC_BASE_ANALYTICS_PERCENTILE_FILTER_H_ |