diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index 8d93870931..0646fb3d32 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -137,6 +137,8 @@ rtc_source_set("rtc_base_approved_generic") { "flags.h", "format_macros.h", "function_view.h", + "histogram_percentile_counter.cc", + "histogram_percentile_counter.h", "ignore_wundef.h", "location.cc", "location.h", @@ -895,6 +897,7 @@ if (rtc_include_tests) { "event_unittest.cc", "file_unittest.cc", "function_view_unittest.cc", + "histogram_percentile_counter_unittest.cc", "logging_unittest.cc", "md5digest_unittest.cc", "mod_ops_unittest.cc", diff --git a/rtc_base/histogram_percentile_counter.cc b/rtc_base/histogram_percentile_counter.cc new file mode 100644 index 0000000000..959e11d03d --- /dev/null +++ b/rtc_base/histogram_percentile_counter.cc @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/histogram_percentile_counter.h" + +#include +#include + +#include "rtc_base/checks.h" + +namespace rtc { +HistogramPercentileCounter::HistogramPercentileCounter( + uint32_t long_tail_boundary) + : histogram_low_(size_t{long_tail_boundary}), + long_tail_boundary_(long_tail_boundary), + total_elements_(0), + total_elements_low_(0) {} + +HistogramPercentileCounter::~HistogramPercentileCounter() = default; + +void HistogramPercentileCounter::Add(const HistogramPercentileCounter& other) { + for (uint32_t value = 0; value < other.long_tail_boundary_; ++value) { + Add(value, other.histogram_low_[value]); + } + for (const auto& it : histogram_high_) { + Add(it.first, it.second); + } +} + +void HistogramPercentileCounter::Add(uint32_t value, size_t count) { + if (value < long_tail_boundary_) { + histogram_low_[value] += count; + total_elements_low_ += count; + } else { + histogram_high_[value] += count; + } + total_elements_ += count; +} + +void HistogramPercentileCounter::Add(uint32_t value) { + Add(value, 1); +} + +rtc::Optional HistogramPercentileCounter::GetPercentile( + float fraction) { + RTC_CHECK_LE(fraction, 1.0); + RTC_CHECK_GE(fraction, 0.0); + if (total_elements_ == 0) + return rtc::Optional(); + size_t elements_to_skip = static_cast( + std::max(0.0f, std::ceil(total_elements_ * fraction) - 1)); + if (elements_to_skip >= total_elements_) + elements_to_skip = total_elements_ - 1; + if (elements_to_skip < total_elements_low_) { + for (uint32_t value = 0; value < long_tail_boundary_; ++value) { + if (elements_to_skip < histogram_low_[value]) + return rtc::Optional(value); + elements_to_skip -= histogram_low_[value]; + } + } else { + elements_to_skip -= total_elements_low_; + for (const auto& it : histogram_high_) { + if (elements_to_skip < it.second) + return rtc::Optional(it.first); + elements_to_skip -= it.second; + } + } + RTC_NOTREACHED(); + return rtc::Optional(); +} + +} // namespace rtc diff --git a/rtc_base/histogram_percentile_counter.h b/rtc_base/histogram_percentile_counter.h new file mode 100644 index 0000000000..5769fb0c16 --- /dev/null +++ b/rtc_base/histogram_percentile_counter.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_HISTOGRAM_PERCENTILE_COUNTER_H_ +#define RTC_BASE_HISTOGRAM_PERCENTILE_COUNTER_H_ + +#include +#include +#include + +#include "api/optional.h" + +namespace rtc { +// Calculates percentiles on the stream of data. Use |Add| methods to add new +// values. Use |GetPercentile| to get percentile of the currently added values. +class HistogramPercentileCounter { + public: + // Values below |long_tail_boundary| are stored as the histogram in an array. + // Values above - in a map. + explicit HistogramPercentileCounter(uint32_t long_tail_boundary); + ~HistogramPercentileCounter(); + void Add(uint32_t value); + void Add(uint32_t value, size_t count); + void Add(const HistogramPercentileCounter& other); + // Argument should be from 0 to 1. + rtc::Optional GetPercentile(float fraction); + + private: + std::vector histogram_low_; + std::map histogram_high_; + const uint32_t long_tail_boundary_; + size_t total_elements_; + size_t total_elements_low_; +}; +} // namespace rtc +#endif // RTC_BASE_HISTOGRAM_PERCENTILE_COUNTER_H_ diff --git a/rtc_base/histogram_percentile_counter_unittest.cc b/rtc_base/histogram_percentile_counter_unittest.cc new file mode 100644 index 0000000000..494312383b --- /dev/null +++ b/rtc_base/histogram_percentile_counter_unittest.cc @@ -0,0 +1,43 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/histogram_percentile_counter.h" + +#include +#include + +#include "test/gtest.h" + +TEST(HistogramPercentileCounterTest, ReturnsCorrectPercentiles) { + rtc::HistogramPercentileCounter counter(10); + const std::vector kTestValues = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}; + + EXPECT_FALSE(counter.GetPercentile(0.5f)); + // Pairs of {fraction, percentile value} computed by hand + // for |kTestValues|. + const std::vector> kTestPercentiles = { + {0.0f, 1}, {0.01f, 1}, {0.5f, 10}, {0.9f, 18}, + {0.95f, 19}, {0.99f, 20}, {1.0f, 20}}; + for (int value : kTestValues) { + counter.Add(value); + } + for (const auto& test_percentile : kTestPercentiles) { + EXPECT_EQ(test_percentile.second, + counter.GetPercentile(test_percentile.first).value_or(0)); + } +} + +TEST(HistogramPercentileCounterTest, HandlesEmptySequence) { + rtc::HistogramPercentileCounter counter(10); + EXPECT_FALSE(counter.GetPercentile(0.5f)); + counter.Add(1u); + EXPECT_TRUE(counter.GetPercentile(0.5f)); +} diff --git a/video/receive_statistics_proxy.cc b/video/receive_statistics_proxy.cc index 6e61032fad..d4bdc6d274 100644 --- a/video/receive_statistics_proxy.cc +++ b/video/receive_statistics_proxy.cc @@ -821,69 +821,4 @@ void ReceiveStatisticsProxy::ContentSpecificStats::Add( frame_counts.delta_frames += other.frame_counts.delta_frames; interframe_delay_percentiles.Add(other.interframe_delay_percentiles); } - -ReceiveStatisticsProxy::HistogramPercentileCounter::HistogramPercentileCounter( - size_t long_tail_boundary) - : histogram_low_(long_tail_boundary), - long_tail_boundary_(long_tail_boundary), - total_elements_(0), - total_elements_low_(0) {} - -void ReceiveStatisticsProxy::HistogramPercentileCounter::Add( - const HistogramPercentileCounter& other) { - uint32_t value; - for (value = 0; value < other.long_tail_boundary_; ++value) { - Add(value, other.histogram_low_[value]); - } - for (auto it = other.histogram_high_.begin(); - it != other.histogram_high_.end(); ++it) { - Add(it->first, it->second); - } -} - -void ReceiveStatisticsProxy::HistogramPercentileCounter::Add(uint32_t value, - size_t count) { - if (value < long_tail_boundary_) { - histogram_low_[value] += count; - total_elements_low_ += count; - } else { - histogram_high_[value] += count; - } - total_elements_ += count; -} - -void ReceiveStatisticsProxy::HistogramPercentileCounter::Add(uint32_t value) { - Add(value, 1); -} - -rtc::Optional -ReceiveStatisticsProxy::HistogramPercentileCounter::GetPercentile( - float fraction) { - RTC_CHECK_LE(fraction, 1); - RTC_CHECK_GE(fraction, 0); - if (total_elements_ == 0) - return rtc::Optional(); - int elements_to_skip = - static_cast(std::ceil(total_elements_ * fraction)) - 1; - if (elements_to_skip < 0) - elements_to_skip = 0; - if (elements_to_skip >= static_cast(total_elements_)) - elements_to_skip = total_elements_ - 1; - if (elements_to_skip < static_cast(total_elements_low_)) { - for (uint32_t value = 0; value < long_tail_boundary_; ++value) { - elements_to_skip -= histogram_low_[value]; - if (elements_to_skip < 0) - return rtc::Optional(value); - } - } else { - elements_to_skip -= total_elements_low_; - for (auto it = histogram_high_.begin(); it != histogram_high_.end(); ++it) { - elements_to_skip -= it->second; - if (elements_to_skip < 0) - return rtc::Optional(it->first); - } - } - RTC_NOTREACHED(); - return rtc::Optional(); -} } // namespace webrtc diff --git a/video/receive_statistics_proxy.h b/video/receive_statistics_proxy.h index 9d4c231609..e874ddc827 100644 --- a/video/receive_statistics_proxy.h +++ b/video/receive_statistics_proxy.h @@ -21,6 +21,7 @@ #include "common_video/include/frame_callback.h" #include "modules/video_coding/include/video_coding_defines.h" #include "rtc_base/criticalsection.h" +#include "rtc_base/histogram_percentile_counter.h" #include "rtc_base/moving_max_counter.h" #include "rtc_base/rate_statistics.h" #include "rtc_base/ratetracker.h" @@ -95,25 +96,6 @@ class ReceiveStatisticsProxy : public VCMReceiveStatisticsCallback, // Implements CallStatsObserver. void OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) override; - class HistogramPercentileCounter { - public: - // Values below |long_tail_boundary| are stored in the array. - // Values above - in the map. - explicit HistogramPercentileCounter(size_t long_tail_boundary); - void Add(uint32_t value); - void Add(uint32_t value, size_t count); - void Add(const HistogramPercentileCounter& other); - // Argument should be from 0 to 1. - rtc::Optional GetPercentile(float fraction); - - private: - std::vector histogram_low_; - std::map histogram_high_; - const size_t long_tail_boundary_; - size_t total_elements_; - size_t total_elements_low_; - }; - private: struct SampleCounter { SampleCounter() : sum(0), num_samples(0) {} @@ -146,7 +128,7 @@ class ReceiveStatisticsProxy : public VCMReceiveStatisticsCallback, SampleCounter received_height; SampleCounter qp_counter; FrameCounts frame_counts; - HistogramPercentileCounter interframe_delay_percentiles; + rtc::HistogramPercentileCounter interframe_delay_percentiles; }; void UpdateHistograms() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_); diff --git a/video/receive_statistics_proxy_unittest.cc b/video/receive_statistics_proxy_unittest.cc index df4305b812..83dffb7fcd 100644 --- a/video/receive_statistics_proxy_unittest.cc +++ b/video/receive_statistics_proxy_unittest.cc @@ -999,26 +999,4 @@ TEST_P(ReceiveStatisticsProxyTest, StatsAreSlicedOnSimulcastAndExperiment) { "WebRTC.Video.InterframeDelayInMs.ExperimentGroup0")); } } - -TEST_F(ReceiveStatisticsProxyTest, PercentileCounterReturnsCorrectPercentiles) { - ReceiveStatisticsProxy::HistogramPercentileCounter counter(10); - const std::vector kTestValues = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}; - - EXPECT_FALSE(counter.GetPercentile(0.5f)); - // Pairs of {fraction, percentile value} computed by hand - // for |kTestValues|. - const std::vector> kTestPercentiles = { - {0.0f, 1}, {0.01f, 1}, {0.5f, 10}, {0.9f, 18}, - {0.95f, 19}, {0.99f, 20}, {1.0f, 20}}; - size_t i; - for (i = 0; i < kTestValues.size(); ++i) { - counter.Add(kTestValues[i]); - } - for (i = 0; i < kTestPercentiles.size(); ++i) { - EXPECT_EQ(kTestPercentiles[i].second, - counter.GetPercentile(kTestPercentiles[i].first).value_or(0)); - } -} - } // namespace webrtc