diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index 2de8c4f22d..2b10d1e07c 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -577,6 +577,8 @@ rtc_library("rtc_numerics") { sources = [ "numerics/event_based_exponential_moving_average.cc", "numerics/event_based_exponential_moving_average.h", + "numerics/event_rate_counter.cc", + "numerics/event_rate_counter.h", "numerics/exp_filter.cc", "numerics/exp_filter.h", "numerics/math_utils.h", @@ -585,6 +587,8 @@ rtc_library("rtc_numerics") { "numerics/moving_median_filter.h", "numerics/percentile_filter.h", "numerics/running_statistics.h", + "numerics/sample_stats.cc", + "numerics/sample_stats.h", "numerics/samples_stats_counter.cc", "numerics/samples_stats_counter.h", "numerics/sequence_number_util.h", @@ -594,6 +598,8 @@ rtc_library("rtc_numerics") { ":rtc_base_approved", ":safe_compare", "../api:array_view", + "../api/units:data_rate", + "../api/units:time_delta", "../api/units:timestamp", "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/types:optional", diff --git a/rtc_base/numerics/event_rate_counter.cc b/rtc_base/numerics/event_rate_counter.cc new file mode 100644 index 0000000000..d7b7293918 --- /dev/null +++ b/rtc_base/numerics/event_rate_counter.cc @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019 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/numerics/event_rate_counter.h" + +#include + +namespace webrtc { + +void EventRateCounter::AddEvent(Timestamp event_time) { + if (first_time_.IsFinite()) + interval_.AddSample(event_time - last_time_); + first_time_ = std::min(first_time_, event_time); + last_time_ = std::max(last_time_, event_time); + event_count_++; +} + +void EventRateCounter::AddEvents(EventRateCounter other) { + first_time_ = std::min(first_time_, other.first_time_); + last_time_ = std::max(last_time_, other.last_time_); + event_count_ += other.event_count_; + interval_.AddSamples(other.interval_); +} + +bool EventRateCounter::IsEmpty() const { + return first_time_ == last_time_; +} + +double EventRateCounter::Rate() const { + if (event_count_ == 0) + return 0; + if (event_count_ == 1) + return NAN; + return (event_count_ - 1) / (last_time_ - first_time_).seconds(); +} + +TimeDelta EventRateCounter::TotalDuration() const { + if (first_time_.IsInfinite()) { + return TimeDelta::Zero(); + } + return last_time_ - first_time_; +} +} // namespace webrtc diff --git a/rtc_base/numerics/event_rate_counter.h b/rtc_base/numerics/event_rate_counter.h new file mode 100644 index 0000000000..ebeac873b8 --- /dev/null +++ b/rtc_base/numerics/event_rate_counter.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019 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_NUMERICS_EVENT_RATE_COUNTER_H_ +#define RTC_BASE_NUMERICS_EVENT_RATE_COUNTER_H_ + +#include "rtc_base/numerics/sample_stats.h" + +namespace webrtc { + +// Calculates statistics based on events. For example for computing frame rates. +// Note that it doesn't provide any running statistics or reset funcitonality, +// so it's mostly useful for end of call statistics. +class EventRateCounter { + public: + // Adds an event based on it's |event_time| for correct updates of the + // interval statistics, each event must be added past the previous events. + void AddEvent(Timestamp event_time); + // Adds the events from |other|. Note that the interval stats won't be + // recalculated, only merged, so this is not equivalent to if the events would + // have been added to the same counter from the start. + void AddEvents(EventRateCounter other); + bool IsEmpty() const; + // Average number of events per second. Defaults to 0 for no events and NAN + // for one event. + double Rate() const; + SampleStats& interval() { return interval_; } + TimeDelta TotalDuration() const; + int Count() const { return event_count_; } + + private: + Timestamp first_time_ = Timestamp::PlusInfinity(); + Timestamp last_time_ = Timestamp::MinusInfinity(); + int64_t event_count_ = 0; + SampleStats interval_; +}; +} // namespace webrtc +#endif // RTC_BASE_NUMERICS_EVENT_RATE_COUNTER_H_ diff --git a/rtc_base/numerics/sample_stats.cc b/rtc_base/numerics/sample_stats.cc new file mode 100644 index 0000000000..7a6f01e3d4 --- /dev/null +++ b/rtc_base/numerics/sample_stats.cc @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2019 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/numerics/sample_stats.h" + +namespace webrtc { + +double SampleStats::Max() { + if (IsEmpty()) + return INFINITY; + return GetMax(); +} + +double SampleStats::Mean() { + if (IsEmpty()) + return 0; + return GetAverage(); +} + +double SampleStats::Median() { + return Quantile(0.5); +} + +double SampleStats::Quantile(double quantile) { + if (IsEmpty()) + return 0; + return GetPercentile(quantile); +} + +double SampleStats::Min() { + if (IsEmpty()) + return -INFINITY; + return GetMin(); +} + +double SampleStats::Variance() { + if (IsEmpty()) + return 0; + return GetVariance(); +} + +double SampleStats::StandardDeviation() { + return sqrt(Variance()); +} + +int SampleStats::Count() { + return static_cast(GetSamples().size()); +} + +void SampleStats::AddSample(TimeDelta delta) { + RTC_DCHECK(delta.IsFinite()); + stats_.AddSample(delta.seconds()); +} + +void SampleStats::AddSampleMs(double delta_ms) { + AddSample(TimeDelta::ms(delta_ms)); +} +void SampleStats::AddSamples(const SampleStats& other) { + stats_.AddSamples(other.stats_); +} + +bool SampleStats::IsEmpty() { + return stats_.IsEmpty(); +} + +TimeDelta SampleStats::Max() { + return TimeDelta::seconds(stats_.Max()); +} + +TimeDelta SampleStats::Mean() { + return TimeDelta::seconds(stats_.Mean()); +} + +TimeDelta SampleStats::Median() { + return Quantile(0.5); +} + +TimeDelta SampleStats::Quantile(double quantile) { + return TimeDelta::seconds(stats_.Quantile(quantile)); +} + +TimeDelta SampleStats::Min() { + return TimeDelta::seconds(stats_.Min()); +} + +TimeDelta SampleStats::Variance() { + return TimeDelta::seconds(stats_.Variance()); +} + +TimeDelta SampleStats::StandardDeviation() { + return TimeDelta::seconds(stats_.StandardDeviation()); +} + +int SampleStats::Count() { + return stats_.Count(); +} + +void SampleStats::AddSample(DataRate sample) { + stats_.AddSample(sample.bps()); +} + +void SampleStats::AddSampleBps(double rate_bps) { + stats_.AddSample(rate_bps); +} + +void SampleStats::AddSamples(const SampleStats& other) { + stats_.AddSamples(other.stats_); +} + +bool SampleStats::IsEmpty() { + return stats_.IsEmpty(); +} + +DataRate SampleStats::Max() { + return DataRate::bps(stats_.Max()); +} + +DataRate SampleStats::Mean() { + return DataRate::bps(stats_.Mean()); +} + +DataRate SampleStats::Median() { + return Quantile(0.5); +} + +DataRate SampleStats::Quantile(double quantile) { + return DataRate::bps(stats_.Quantile(quantile)); +} + +DataRate SampleStats::Min() { + return DataRate::bps(stats_.Min()); +} + +DataRate SampleStats::Variance() { + return DataRate::bps(stats_.Variance()); +} + +DataRate SampleStats::StandardDeviation() { + return DataRate::bps(stats_.StandardDeviation()); +} + +int SampleStats::Count() { + return stats_.Count(); +} + +} // namespace webrtc diff --git a/rtc_base/numerics/sample_stats.h b/rtc_base/numerics/sample_stats.h new file mode 100644 index 0000000000..f6347414b0 --- /dev/null +++ b/rtc_base/numerics/sample_stats.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2019 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_NUMERICS_SAMPLE_STATS_H_ +#define RTC_BASE_NUMERICS_SAMPLE_STATS_H_ + +#include "api/units/data_rate.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "rtc_base/numerics/samples_stats_counter.h" + +namespace webrtc { +template +class SampleStats; + +// TODO(srte): Merge this implementation with SamplesStatsCounter. +template <> +class SampleStats : public SamplesStatsCounter { + public: + double Max(); + double Mean(); + double Median(); + double Quantile(double quantile); + double Min(); + double Variance(); + double StandardDeviation(); + int Count(); +}; + +template <> +class SampleStats { + public: + void AddSample(TimeDelta delta); + void AddSampleMs(double delta_ms); + void AddSamples(const SampleStats& other); + bool IsEmpty(); + TimeDelta Max(); + TimeDelta Mean(); + TimeDelta Median(); + TimeDelta Quantile(double quantile); + TimeDelta Min(); + TimeDelta Variance(); + TimeDelta StandardDeviation(); + int Count(); + + private: + SampleStats stats_; +}; + +template <> +class SampleStats { + public: + void AddSample(DataRate rate); + void AddSampleBps(double rate_bps); + void AddSamples(const SampleStats& other); + bool IsEmpty(); + DataRate Max(); + DataRate Mean(); + DataRate Median(); + DataRate Quantile(double quantile); + DataRate Min(); + DataRate Variance(); + DataRate StandardDeviation(); + int Count(); + + private: + SampleStats stats_; +}; +} // namespace webrtc + +#endif // RTC_BASE_NUMERICS_SAMPLE_STATS_H_ diff --git a/test/scenario/performance_stats.cc b/test/scenario/performance_stats.cc index 5f5b506c1f..e12be8a003 100644 --- a/test/scenario/performance_stats.cc +++ b/test/scenario/performance_stats.cc @@ -13,178 +13,6 @@ namespace webrtc { namespace test { -void EventRateCounter::AddEvent(Timestamp event_time) { - if (first_time_.IsFinite()) - interval_.AddSample(event_time - last_time_); - first_time_ = std::min(first_time_, event_time); - last_time_ = std::max(last_time_, event_time); - event_count_++; -} - -void EventRateCounter::AddEvents(EventRateCounter other) { - first_time_ = std::min(first_time_, other.first_time_); - last_time_ = std::max(last_time_, other.last_time_); - event_count_ += other.event_count_; - interval_.AddSamples(other.interval_); -} - -bool EventRateCounter::IsEmpty() const { - return first_time_ == last_time_; -} - -double EventRateCounter::Rate() const { - if (event_count_ == 0) - return 0; - if (event_count_ == 1) - return NAN; - return (event_count_ - 1) / (last_time_ - first_time_).seconds(); -} - -TimeDelta EventRateCounter::TotalDuration() const { - if (first_time_.IsInfinite()) { - return TimeDelta::Zero(); - } - return last_time_ - first_time_; -} - -double SampleStats::Max() { - if (IsEmpty()) - return INFINITY; - return GetMax(); -} - -double SampleStats::Mean() { - if (IsEmpty()) - return 0; - return GetAverage(); -} - -double SampleStats::Median() { - return Quantile(0.5); -} - -double SampleStats::Quantile(double quantile) { - if (IsEmpty()) - return 0; - return GetPercentile(quantile); -} - -double SampleStats::Min() { - if (IsEmpty()) - return -INFINITY; - return GetMin(); -} - -double SampleStats::Variance() { - if (IsEmpty()) - return 0; - return GetVariance(); -} - -double SampleStats::StandardDeviation() { - return sqrt(Variance()); -} - -int SampleStats::Count() { - return static_cast(GetSamples().size()); -} - -void SampleStats::AddSample(TimeDelta delta) { - RTC_DCHECK(delta.IsFinite()); - stats_.AddSample(delta.seconds()); -} - -void SampleStats::AddSampleMs(double delta_ms) { - AddSample(TimeDelta::ms(delta_ms)); -} -void SampleStats::AddSamples(const SampleStats& other) { - stats_.AddSamples(other.stats_); -} - -bool SampleStats::IsEmpty() { - return stats_.IsEmpty(); -} - -TimeDelta SampleStats::Max() { - return TimeDelta::seconds(stats_.Max()); -} - -TimeDelta SampleStats::Mean() { - return TimeDelta::seconds(stats_.Mean()); -} - -TimeDelta SampleStats::Median() { - return Quantile(0.5); -} - -TimeDelta SampleStats::Quantile(double quantile) { - return TimeDelta::seconds(stats_.Quantile(quantile)); -} - -TimeDelta SampleStats::Min() { - return TimeDelta::seconds(stats_.Min()); -} - -TimeDelta SampleStats::Variance() { - return TimeDelta::seconds(stats_.Variance()); -} - -TimeDelta SampleStats::StandardDeviation() { - return TimeDelta::seconds(stats_.StandardDeviation()); -} - -int SampleStats::Count() { - return stats_.Count(); -} - -void SampleStats::AddSample(DataRate sample) { - stats_.AddSample(sample.bps()); -} - -void SampleStats::AddSampleBps(double rate_bps) { - stats_.AddSample(rate_bps); -} - -void SampleStats::AddSamples(const SampleStats& other) { - stats_.AddSamples(other.stats_); -} - -bool SampleStats::IsEmpty() { - return stats_.IsEmpty(); -} - -DataRate SampleStats::Max() { - return DataRate::bps(stats_.Max()); -} - -DataRate SampleStats::Mean() { - return DataRate::bps(stats_.Mean()); -} - -DataRate SampleStats::Median() { - return Quantile(0.5); -} - -DataRate SampleStats::Quantile(double quantile) { - return DataRate::bps(stats_.Quantile(quantile)); -} - -DataRate SampleStats::Min() { - return DataRate::bps(stats_.Min()); -} - -DataRate SampleStats::Variance() { - return DataRate::bps(stats_.Variance()); -} - -DataRate SampleStats::StandardDeviation() { - return DataRate::bps(stats_.StandardDeviation()); -} - -int SampleStats::Count() { - return stats_.Count(); -} - void VideoFramesStats::AddFrameInfo(const VideoFrameBuffer& frame, Timestamp at_time) { ++count; diff --git a/test/scenario/performance_stats.h b/test/scenario/performance_stats.h index 310ee8d7f9..6974ab6d22 100644 --- a/test/scenario/performance_stats.h +++ b/test/scenario/performance_stats.h @@ -14,7 +14,8 @@ #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "api/video/video_frame_buffer.h" -#include "rtc_base/numerics/samples_stats_counter.h" +#include "rtc_base/numerics/event_rate_counter.h" +#include "rtc_base/numerics/sample_stats.h" namespace webrtc { namespace test { @@ -36,78 +37,6 @@ struct VideoFramePair { int repeated = 0; }; -template -class SampleStats; - -template <> -class SampleStats : public SamplesStatsCounter { - public: - double Max(); - double Mean(); - double Median(); - double Quantile(double quantile); - double Min(); - double Variance(); - double StandardDeviation(); - int Count(); -}; - -template <> -class SampleStats { - public: - void AddSample(TimeDelta delta); - void AddSampleMs(double delta_ms); - void AddSamples(const SampleStats& other); - bool IsEmpty(); - TimeDelta Max(); - TimeDelta Mean(); - TimeDelta Median(); - TimeDelta Quantile(double quantile); - TimeDelta Min(); - TimeDelta Variance(); - TimeDelta StandardDeviation(); - int Count(); - - private: - SampleStats stats_; -}; - -template <> -class SampleStats { - public: - void AddSample(DataRate rate); - void AddSampleBps(double rate_bps); - void AddSamples(const SampleStats& other); - bool IsEmpty(); - DataRate Max(); - DataRate Mean(); - DataRate Median(); - DataRate Quantile(double quantile); - DataRate Min(); - DataRate Variance(); - DataRate StandardDeviation(); - int Count(); - - private: - SampleStats stats_; -}; - -class EventRateCounter { - public: - void AddEvent(Timestamp event_time); - void AddEvents(EventRateCounter other); - bool IsEmpty() const; - double Rate() const; - SampleStats& interval() { return interval_; } - TimeDelta TotalDuration() const; - int Count() const { return event_count_; } - - private: - Timestamp first_time_ = Timestamp::PlusInfinity(); - Timestamp last_time_ = Timestamp::MinusInfinity(); - int64_t event_count_ = 0; - SampleStats interval_; -}; struct VideoFramesStats { int count = 0;