Update AvgCounter to have the ability to include last period metric for subsequent intervals without samples (e.g. for non-periodic updated stats).
Integrate AvgCounter to be used for BWE stats in call. Fixes for stats regression in: WebRTC.Call.EstimatedSendBitrateInKbps WebRTC.Call.PacerBitrateInKbps Example: BWE for a 15 seconds long call (with intervals of 1 sec): |300|400|500|600|600|600|600| 0 | 0 | 0 | 0 | 0 |800|800|800| // 0 - network state down Reported via OnNetworkChanged: |300|400|500|600| x | x | x | 0 | x | x | x | x |800| x | x | // x - empty interval, 0 -> pauses stats Stats: |300|400|500|600|600|600|600| - | - | - | - | - |800|800|800| // x -> last value used (intervals during pause ignored) AvgCounter uses the average of samples within an interval (interval length is 2 sec). BUG=webrtc:6244 Review-Url: https://codereview.webrtc.org/2307913002 Cr-Commit-Position: refs/heads/master@{#14147}
This commit is contained in:
parent
2a5f371df3
commit
ce2e13602e
@ -186,11 +186,10 @@ class Call : public webrtc::Call,
|
||||
// TODO(holmer): Remove this lock once BitrateController no longer calls
|
||||
// OnNetworkChanged from multiple threads.
|
||||
rtc::CriticalSection bitrate_crit_;
|
||||
int64_t estimated_send_bitrate_sum_kbits_ GUARDED_BY(&bitrate_crit_);
|
||||
int64_t pacer_bitrate_sum_kbits_ GUARDED_BY(&bitrate_crit_);
|
||||
uint32_t min_allocated_send_bitrate_bps_ GUARDED_BY(&bitrate_crit_);
|
||||
int64_t num_bitrate_updates_ GUARDED_BY(&bitrate_crit_);
|
||||
uint32_t configured_max_padding_bitrate_bps_ GUARDED_BY(&bitrate_crit_);
|
||||
AvgCounter estimated_send_bitrate_kbps_counter_ GUARDED_BY(&bitrate_crit_);
|
||||
AvgCounter pacer_bitrate_kbps_counter_ GUARDED_BY(&bitrate_crit_);
|
||||
|
||||
std::map<std::string, rtc::NetworkRoute> network_routes_;
|
||||
|
||||
@ -244,11 +243,10 @@ Call::Call(const Call::Config& config)
|
||||
received_audio_bytes_per_second_counter_(clock_, nullptr, true),
|
||||
received_video_bytes_per_second_counter_(clock_, nullptr, true),
|
||||
received_rtcp_bytes_per_second_counter_(clock_, nullptr, true),
|
||||
estimated_send_bitrate_sum_kbits_(0),
|
||||
pacer_bitrate_sum_kbits_(0),
|
||||
min_allocated_send_bitrate_bps_(0),
|
||||
num_bitrate_updates_(0),
|
||||
configured_max_padding_bitrate_bps_(0),
|
||||
estimated_send_bitrate_kbps_counter_(clock_, nullptr, true),
|
||||
pacer_bitrate_kbps_counter_(clock_, nullptr, true),
|
||||
remb_(clock_),
|
||||
congestion_controller_(
|
||||
new CongestionController(clock_, this, &remb_, event_log_.get())),
|
||||
@ -320,22 +318,24 @@ void Call::UpdateHistograms() {
|
||||
}
|
||||
|
||||
void Call::UpdateSendHistograms() {
|
||||
if (num_bitrate_updates_ == 0 || first_packet_sent_ms_ == -1)
|
||||
if (first_packet_sent_ms_ == -1)
|
||||
return;
|
||||
int64_t elapsed_sec =
|
||||
(clock_->TimeInMilliseconds() - first_packet_sent_ms_) / 1000;
|
||||
if (elapsed_sec < metrics::kMinRunTimeInSeconds)
|
||||
return;
|
||||
int send_bitrate_kbps =
|
||||
estimated_send_bitrate_sum_kbits_ / num_bitrate_updates_;
|
||||
int pacer_bitrate_kbps = pacer_bitrate_sum_kbits_ / num_bitrate_updates_;
|
||||
if (send_bitrate_kbps > 0) {
|
||||
const int kMinRequiredPeriodicSamples = 5;
|
||||
AggregatedStats send_bitrate_stats =
|
||||
estimated_send_bitrate_kbps_counter_.ProcessAndGetStats();
|
||||
if (send_bitrate_stats.num_samples > kMinRequiredPeriodicSamples) {
|
||||
RTC_LOGGED_HISTOGRAM_COUNTS_100000("WebRTC.Call.EstimatedSendBitrateInKbps",
|
||||
send_bitrate_kbps);
|
||||
send_bitrate_stats.average);
|
||||
}
|
||||
if (pacer_bitrate_kbps > 0) {
|
||||
AggregatedStats pacer_bitrate_stats =
|
||||
pacer_bitrate_kbps_counter_.ProcessAndGetStats();
|
||||
if (pacer_bitrate_stats.num_samples > kMinRequiredPeriodicSamples) {
|
||||
RTC_LOGGED_HISTOGRAM_COUNTS_100000("WebRTC.Call.PacerBitrateInKbps",
|
||||
pacer_bitrate_kbps);
|
||||
pacer_bitrate_stats.average);
|
||||
}
|
||||
}
|
||||
|
||||
@ -757,26 +757,32 @@ void Call::OnNetworkChanged(uint32_t target_bitrate_bps, uint8_t fraction_loss,
|
||||
bitrate_allocator_->OnNetworkChanged(target_bitrate_bps, fraction_loss,
|
||||
rtt_ms);
|
||||
|
||||
// Ignore updates where the bitrate is zero because the aggregate network
|
||||
// state is down.
|
||||
if (target_bitrate_bps > 0) {
|
||||
{
|
||||
ReadLockScoped read_lock(*send_crit_);
|
||||
// Do not update the stats if we are not sending video.
|
||||
if (video_send_streams_.empty())
|
||||
return;
|
||||
}
|
||||
// Ignore updates if bitrate is zero (the aggregate network state is down).
|
||||
if (target_bitrate_bps == 0) {
|
||||
rtc::CritScope lock(&bitrate_crit_);
|
||||
// We only update these stats if we have send streams, and assume that
|
||||
// OnNetworkChanged is called roughly with a fixed frequency.
|
||||
estimated_send_bitrate_sum_kbits_ += target_bitrate_bps / 1000;
|
||||
// Pacer bitrate might be higher than bitrate estimate if enforcing min
|
||||
// bitrate.
|
||||
uint32_t pacer_bitrate_bps =
|
||||
std::max(target_bitrate_bps, min_allocated_send_bitrate_bps_);
|
||||
pacer_bitrate_sum_kbits_ += pacer_bitrate_bps / 1000;
|
||||
++num_bitrate_updates_;
|
||||
estimated_send_bitrate_kbps_counter_.ProcessAndPause();
|
||||
pacer_bitrate_kbps_counter_.ProcessAndPause();
|
||||
return;
|
||||
}
|
||||
|
||||
bool sending_video;
|
||||
{
|
||||
ReadLockScoped read_lock(*send_crit_);
|
||||
sending_video = !video_send_streams_.empty();
|
||||
}
|
||||
|
||||
rtc::CritScope lock(&bitrate_crit_);
|
||||
if (!sending_video) {
|
||||
// Do not update the stats if we are not sending video.
|
||||
estimated_send_bitrate_kbps_counter_.ProcessAndPause();
|
||||
pacer_bitrate_kbps_counter_.ProcessAndPause();
|
||||
return;
|
||||
}
|
||||
estimated_send_bitrate_kbps_counter_.Add(target_bitrate_bps / 1000);
|
||||
// Pacer bitrate may be higher than bitrate estimate if enforcing min bitrate.
|
||||
uint32_t pacer_bitrate_bps =
|
||||
std::max(target_bitrate_bps, min_allocated_send_bitrate_bps_);
|
||||
pacer_bitrate_kbps_counter_.Add(pacer_bitrate_bps / 1000);
|
||||
}
|
||||
|
||||
void Call::OnAllocationLimitsChanged(uint32_t min_send_bitrate_bps,
|
||||
|
||||
@ -10,6 +10,8 @@
|
||||
|
||||
#include "webrtc/video/send_delay_stats.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "webrtc/base/logging.h"
|
||||
#include "webrtc/system_wrappers/include/metrics.h"
|
||||
|
||||
@ -61,7 +63,7 @@ AvgCounter* SendDelayStats::GetSendDelayCounter(uint32_t ssrc) {
|
||||
if (it != send_delay_counters_.end())
|
||||
return it->second.get();
|
||||
|
||||
AvgCounter* counter = new AvgCounter(clock_, nullptr);
|
||||
AvgCounter* counter = new AvgCounter(clock_, nullptr, false);
|
||||
send_delay_counters_[ssrc].reset(counter);
|
||||
return counter;
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/system_wrappers/include/clock.h"
|
||||
|
||||
namespace webrtc {
|
||||
@ -24,11 +25,12 @@ const int64_t kProcessIntervalMs = 2000;
|
||||
// Class holding periodically computed metrics.
|
||||
class AggregatedCounter {
|
||||
public:
|
||||
AggregatedCounter() : sum_(0) {}
|
||||
AggregatedCounter() : last_sample_(0), sum_samples_(0) {}
|
||||
~AggregatedCounter() {}
|
||||
|
||||
void Add(int sample) {
|
||||
sum_ += sample;
|
||||
last_sample_ = sample;
|
||||
sum_samples_ += sample;
|
||||
++stats_.num_samples;
|
||||
if (stats_.num_samples == 1) {
|
||||
stats_.min = sample;
|
||||
@ -43,14 +45,20 @@ class AggregatedCounter {
|
||||
return stats_;
|
||||
}
|
||||
|
||||
bool Empty() const { return stats_.num_samples == 0; }
|
||||
|
||||
int last_sample() const { return last_sample_; }
|
||||
|
||||
private:
|
||||
void Compute() {
|
||||
if (stats_.num_samples == 0)
|
||||
return;
|
||||
|
||||
stats_.average = (sum_ + stats_.num_samples / 2) / stats_.num_samples;
|
||||
stats_.average =
|
||||
(sum_samples_ + stats_.num_samples / 2) / stats_.num_samples;
|
||||
}
|
||||
int64_t sum_;
|
||||
int last_sample_;
|
||||
int64_t sum_samples_;
|
||||
AggregatedStats stats_;
|
||||
};
|
||||
|
||||
@ -62,11 +70,12 @@ StatsCounter::StatsCounter(Clock* clock,
|
||||
sum_(0),
|
||||
num_samples_(0),
|
||||
last_sum_(0),
|
||||
aggregated_counter_(new AggregatedCounter()),
|
||||
clock_(clock),
|
||||
include_empty_intervals_(include_empty_intervals),
|
||||
observer_(observer),
|
||||
aggregated_counter_(new AggregatedCounter()),
|
||||
last_process_time_ms_(-1) {}
|
||||
last_process_time_ms_(-1),
|
||||
paused_(false) {}
|
||||
|
||||
StatsCounter::~StatsCounter() {}
|
||||
|
||||
@ -74,11 +83,23 @@ AggregatedStats StatsCounter::GetStats() {
|
||||
return aggregated_counter_->ComputeStats();
|
||||
}
|
||||
|
||||
AggregatedStats StatsCounter::ProcessAndGetStats() {
|
||||
if (HasSample())
|
||||
TryProcess();
|
||||
return aggregated_counter_->ComputeStats();
|
||||
}
|
||||
|
||||
void StatsCounter::ProcessAndPause() {
|
||||
if (HasSample())
|
||||
TryProcess();
|
||||
paused_ = true;
|
||||
}
|
||||
|
||||
bool StatsCounter::HasSample() const {
|
||||
return last_process_time_ms_ != -1;
|
||||
}
|
||||
|
||||
bool StatsCounter::TimeToProcess() {
|
||||
bool StatsCounter::TimeToProcess(int* elapsed_intervals) {
|
||||
int64_t now = clock_->TimeInMilliseconds();
|
||||
if (last_process_time_ms_ == -1)
|
||||
last_process_time_ms_ = now;
|
||||
@ -91,14 +112,7 @@ bool StatsCounter::TimeToProcess() {
|
||||
int64_t num_intervals = diff_ms / kProcessIntervalMs;
|
||||
last_process_time_ms_ += num_intervals * kProcessIntervalMs;
|
||||
|
||||
// Add zero for intervals without samples.
|
||||
if (include_empty_intervals_) {
|
||||
for (int64_t i = 0; i < num_intervals - 1; ++i) {
|
||||
aggregated_counter_->Add(0);
|
||||
if (observer_)
|
||||
observer_->OnMetricUpdated(0);
|
||||
}
|
||||
}
|
||||
*elapsed_intervals = num_intervals;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -106,6 +120,7 @@ void StatsCounter::Set(int sample) {
|
||||
TryProcess();
|
||||
++num_samples_;
|
||||
sum_ = sample;
|
||||
paused_ = false;
|
||||
}
|
||||
|
||||
void StatsCounter::Add(int sample) {
|
||||
@ -116,29 +131,57 @@ void StatsCounter::Add(int sample) {
|
||||
if (num_samples_ == 1)
|
||||
max_ = sample;
|
||||
max_ = std::max(sample, max_);
|
||||
paused_ = false;
|
||||
}
|
||||
|
||||
// Reports periodically computed metric.
|
||||
void StatsCounter::ReportMetricToAggregatedCounter(
|
||||
int value,
|
||||
int num_values_to_add) const {
|
||||
for (int i = 0; i < num_values_to_add; ++i) {
|
||||
aggregated_counter_->Add(value);
|
||||
if (observer_)
|
||||
observer_->OnMetricUpdated(value);
|
||||
}
|
||||
}
|
||||
|
||||
void StatsCounter::TryProcess() {
|
||||
if (!TimeToProcess())
|
||||
int elapsed_intervals;
|
||||
if (!TimeToProcess(&elapsed_intervals))
|
||||
return;
|
||||
|
||||
// Get and report periodically computed metric.
|
||||
int metric;
|
||||
if (GetMetric(&metric)) {
|
||||
aggregated_counter_->Add(metric);
|
||||
if (observer_)
|
||||
observer_->OnMetricUpdated(metric);
|
||||
if (GetMetric(&metric))
|
||||
ReportMetricToAggregatedCounter(metric, 1);
|
||||
|
||||
// Report value for elapsed intervals without samples.
|
||||
if (IncludeEmptyIntervals()) {
|
||||
// If there are no samples, all elapsed intervals are empty (otherwise one
|
||||
// interval contains sample(s), discard this interval).
|
||||
int empty_intervals =
|
||||
(num_samples_ == 0) ? elapsed_intervals : (elapsed_intervals - 1);
|
||||
ReportMetricToAggregatedCounter(GetValueForEmptyInterval(),
|
||||
empty_intervals);
|
||||
}
|
||||
last_sum_ = sum_;
|
||||
|
||||
// Reset samples for elapsed interval.
|
||||
if (num_samples_ > 0)
|
||||
last_sum_ = sum_;
|
||||
sum_ = 0;
|
||||
max_ = 0;
|
||||
num_samples_ = 0;
|
||||
}
|
||||
|
||||
bool StatsCounter::IncludeEmptyIntervals() const {
|
||||
return include_empty_intervals_ && !paused_ && !aggregated_counter_->Empty();
|
||||
}
|
||||
|
||||
// StatsCounter sub-classes.
|
||||
AvgCounter::AvgCounter(Clock* clock, StatsCounterObserver* observer)
|
||||
: StatsCounter(clock,
|
||||
false, // |include_empty_intervals|
|
||||
observer) {}
|
||||
AvgCounter::AvgCounter(Clock* clock,
|
||||
StatsCounterObserver* observer,
|
||||
bool include_empty_intervals)
|
||||
: StatsCounter(clock, include_empty_intervals, observer) {}
|
||||
|
||||
void AvgCounter::Add(int sample) {
|
||||
StatsCounter::Add(sample);
|
||||
@ -151,6 +194,10 @@ bool AvgCounter::GetMetric(int* metric) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
int AvgCounter::GetValueForEmptyInterval() const {
|
||||
return aggregated_counter_->last_sample();
|
||||
}
|
||||
|
||||
MaxCounter::MaxCounter(Clock* clock, StatsCounterObserver* observer)
|
||||
: StatsCounter(clock,
|
||||
false, // |include_empty_intervals|
|
||||
@ -167,6 +214,11 @@ bool MaxCounter::GetMetric(int* metric) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
int MaxCounter::GetValueForEmptyInterval() const {
|
||||
RTC_NOTREACHED();
|
||||
return 0;
|
||||
}
|
||||
|
||||
PercentCounter::PercentCounter(Clock* clock, StatsCounterObserver* observer)
|
||||
: StatsCounter(clock,
|
||||
false, // |include_empty_intervals|
|
||||
@ -183,6 +235,11 @@ bool PercentCounter::GetMetric(int* metric) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
int PercentCounter::GetValueForEmptyInterval() const {
|
||||
RTC_NOTREACHED();
|
||||
return 0;
|
||||
}
|
||||
|
||||
PermilleCounter::PermilleCounter(Clock* clock, StatsCounterObserver* observer)
|
||||
: StatsCounter(clock,
|
||||
false, // |include_empty_intervals|
|
||||
@ -199,6 +256,11 @@ bool PermilleCounter::GetMetric(int* metric) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
int PermilleCounter::GetValueForEmptyInterval() const {
|
||||
RTC_NOTREACHED();
|
||||
return 0;
|
||||
}
|
||||
|
||||
RateCounter::RateCounter(Clock* clock,
|
||||
StatsCounterObserver* observer,
|
||||
bool include_empty_intervals)
|
||||
@ -215,6 +277,10 @@ bool RateCounter::GetMetric(int* metric) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
int RateCounter::GetValueForEmptyInterval() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
RateAccCounter::RateAccCounter(Clock* clock,
|
||||
StatsCounterObserver* observer,
|
||||
bool include_empty_intervals)
|
||||
@ -232,4 +298,8 @@ bool RateAccCounter::GetMetric(int* metric) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
int RateAccCounter::GetValueForEmptyInterval() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -78,10 +78,22 @@ class StatsCounter {
|
||||
public:
|
||||
virtual ~StatsCounter();
|
||||
|
||||
// Gets metric within an interval. Returns true on success false otherwise.
|
||||
virtual bool GetMetric(int* metric) const = 0;
|
||||
|
||||
// Gets the value to use for an interval without samples.
|
||||
virtual int GetValueForEmptyInterval() const = 0;
|
||||
|
||||
// Gets aggregated stats (i.e. aggregate of periodically computed metrics).
|
||||
AggregatedStats GetStats();
|
||||
|
||||
// Reports metrics for elapsed intervals to AggregatedCounter and GetStats.
|
||||
AggregatedStats ProcessAndGetStats();
|
||||
|
||||
// Reports metrics for elapsed intervals to AggregatedCounter and pauses stats
|
||||
// (i.e. empty intervals will be discarded until next sample is added).
|
||||
void ProcessAndPause();
|
||||
|
||||
// Checks if a sample has been added (i.e. Add or Set called).
|
||||
bool HasSample() const;
|
||||
|
||||
@ -98,15 +110,19 @@ class StatsCounter {
|
||||
int64_t num_samples_;
|
||||
int64_t last_sum_;
|
||||
|
||||
const std::unique_ptr<AggregatedCounter> aggregated_counter_;
|
||||
|
||||
private:
|
||||
bool TimeToProcess();
|
||||
bool TimeToProcess(int* num_elapsed_intervals);
|
||||
void TryProcess();
|
||||
void ReportMetricToAggregatedCounter(int value, int num_values_to_add) const;
|
||||
bool IncludeEmptyIntervals() const;
|
||||
|
||||
Clock* const clock_;
|
||||
const bool include_empty_intervals_;
|
||||
const std::unique_ptr<StatsCounterObserver> observer_;
|
||||
const std::unique_ptr<AggregatedCounter> aggregated_counter_;
|
||||
int64_t last_process_time_ms_;
|
||||
bool paused_;
|
||||
};
|
||||
|
||||
// AvgCounter: average of samples
|
||||
@ -115,9 +131,15 @@ class StatsCounter {
|
||||
// | Add(5) Add(1) Add(6) | Add(5) Add(5) |
|
||||
// GetMetric | (5 + 1 + 6) / 3 | (5 + 5) / 2 |
|
||||
//
|
||||
// |include_empty_intervals|: If set, intervals without samples will be included
|
||||
// in the stats. The value for an interval is
|
||||
// determined by GetValueForEmptyInterval().
|
||||
//
|
||||
class AvgCounter : public StatsCounter {
|
||||
public:
|
||||
AvgCounter(Clock* clock, StatsCounterObserver* observer);
|
||||
AvgCounter(Clock* clock,
|
||||
StatsCounterObserver* observer,
|
||||
bool include_empty_intervals);
|
||||
~AvgCounter() override {}
|
||||
|
||||
void Add(int sample);
|
||||
@ -125,6 +147,9 @@ class AvgCounter : public StatsCounter {
|
||||
private:
|
||||
bool GetMetric(int* metric) const override;
|
||||
|
||||
// Returns the last computed metric (i.e. from GetMetric).
|
||||
int GetValueForEmptyInterval() const override;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(AvgCounter);
|
||||
};
|
||||
|
||||
@ -143,6 +168,7 @@ class MaxCounter : public StatsCounter {
|
||||
|
||||
private:
|
||||
bool GetMetric(int* metric) const override;
|
||||
int GetValueForEmptyInterval() const override;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(MaxCounter);
|
||||
};
|
||||
@ -162,6 +188,7 @@ class PercentCounter : public StatsCounter {
|
||||
|
||||
private:
|
||||
bool GetMetric(int* metric) const override;
|
||||
int GetValueForEmptyInterval() const override;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(PercentCounter);
|
||||
};
|
||||
@ -181,6 +208,7 @@ class PermilleCounter : public StatsCounter {
|
||||
|
||||
private:
|
||||
bool GetMetric(int* metric) const override;
|
||||
int GetValueForEmptyInterval() const override;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(PermilleCounter);
|
||||
};
|
||||
@ -192,6 +220,10 @@ class PermilleCounter : public StatsCounter {
|
||||
// |<------ 2 sec ------->| |
|
||||
// GetMetric | (5 + 1 + 6) / 2 | (5 + 5) / 2 |
|
||||
//
|
||||
// |include_empty_intervals|: If set, intervals without samples will be included
|
||||
// in the stats. The value for an interval is
|
||||
// determined by GetValueForEmptyInterval().
|
||||
//
|
||||
class RateCounter : public StatsCounter {
|
||||
public:
|
||||
RateCounter(Clock* clock,
|
||||
@ -203,6 +235,7 @@ class RateCounter : public StatsCounter {
|
||||
|
||||
private:
|
||||
bool GetMetric(int* metric) const override;
|
||||
int GetValueForEmptyInterval() const override; // Returns zero.
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(RateCounter);
|
||||
};
|
||||
@ -214,6 +247,10 @@ class RateCounter : public StatsCounter {
|
||||
// |<------ 2 sec ------->| |
|
||||
// GetMetric | 8 / 2 | (13 - 8) / 2 |
|
||||
//
|
||||
// |include_empty_intervals|: If set, intervals without samples will be included
|
||||
// in the stats. The value for an interval is
|
||||
// determined by GetValueForEmptyInterval().
|
||||
//
|
||||
class RateAccCounter : public StatsCounter {
|
||||
public:
|
||||
RateAccCounter(Clock* clock,
|
||||
@ -225,6 +262,7 @@ class RateAccCounter : public StatsCounter {
|
||||
|
||||
private:
|
||||
bool GetMetric(int* metric) const override;
|
||||
int GetValueForEmptyInterval() const override; // Returns zero.
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(RateAccCounter);
|
||||
};
|
||||
|
||||
@ -58,14 +58,14 @@ class StatsCounterTest : public ::testing::Test {
|
||||
};
|
||||
|
||||
TEST_F(StatsCounterTest, NoSamples) {
|
||||
AvgCounter counter(&clock_, nullptr);
|
||||
AvgCounter counter(&clock_, nullptr, false);
|
||||
VerifyStatsIsNotSet(counter.GetStats());
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, TestRegisterObserver) {
|
||||
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
|
||||
const int kSample = 22;
|
||||
AvgCounter counter(&clock_, observer);
|
||||
AvgCounter counter(&clock_, observer, false);
|
||||
AddSampleAndAdvance(kSample, kProcessIntervalMs, &counter);
|
||||
// Trigger process (sample included in next interval).
|
||||
counter.Add(111);
|
||||
@ -73,7 +73,7 @@ TEST_F(StatsCounterTest, TestRegisterObserver) {
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, HasSample) {
|
||||
AvgCounter counter(&clock_, nullptr);
|
||||
AvgCounter counter(&clock_, nullptr, false);
|
||||
EXPECT_FALSE(counter.HasSample());
|
||||
counter.Add(1);
|
||||
EXPECT_TRUE(counter.HasSample());
|
||||
@ -81,7 +81,7 @@ TEST_F(StatsCounterTest, HasSample) {
|
||||
|
||||
TEST_F(StatsCounterTest, VerifyProcessInterval) {
|
||||
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
|
||||
AvgCounter counter(&clock_, observer);
|
||||
AvgCounter counter(&clock_, observer, false);
|
||||
counter.Add(4);
|
||||
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs - 1);
|
||||
// Try trigger process (interval has not passed).
|
||||
@ -101,7 +101,7 @@ TEST_F(StatsCounterTest, VerifyProcessInterval) {
|
||||
|
||||
TEST_F(StatsCounterTest, TestMetric_AvgCounter) {
|
||||
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
|
||||
AvgCounter counter(&clock_, observer);
|
||||
AvgCounter counter(&clock_, observer, false);
|
||||
counter.Add(4);
|
||||
counter.Add(8);
|
||||
counter.Add(9);
|
||||
@ -213,7 +213,7 @@ TEST_F(StatsCounterTest, TestMetric_RateAccCounter) {
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, TestGetStats_MultipleIntervals) {
|
||||
AvgCounter counter(&clock_, nullptr);
|
||||
AvgCounter counter(&clock_, nullptr, false);
|
||||
const int kSample1 = 1;
|
||||
const int kSample2 = 5;
|
||||
const int kSample3 = 8;
|
||||
@ -236,7 +236,7 @@ TEST_F(StatsCounterTest, TestGetStats_MultipleIntervals) {
|
||||
TEST_F(StatsCounterTest, TestGetStatsTwice) {
|
||||
const int kSample1 = 4;
|
||||
const int kSample2 = 7;
|
||||
AvgCounter counter(&clock_, nullptr);
|
||||
AvgCounter counter(&clock_, nullptr, false);
|
||||
AddSampleAndAdvance(kSample1, kProcessIntervalMs, &counter);
|
||||
// Trigger process (sample included in next interval).
|
||||
counter.Add(kSample2);
|
||||
@ -277,29 +277,63 @@ TEST_F(StatsCounterTest, TestRateAccCounter_NegativeRateIgnored) {
|
||||
EXPECT_EQ(200, stats.average);
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, TestAvgCounter_IntervalsWithoutSamplesIgnored) {
|
||||
TEST_F(StatsCounterTest, TestAvgCounter_IntervalsWithoutSamplesIncluded) {
|
||||
// Samples: | 6 | x | x | 8 | // x: empty interval
|
||||
// Stats: | 6 | 6 | 6 | 8 | // x -> last value reported
|
||||
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
|
||||
AvgCounter counter(&clock_, observer);
|
||||
AvgCounter counter(&clock_, observer, true);
|
||||
AddSampleAndAdvance(6, kProcessIntervalMs * 4 - 1, &counter);
|
||||
// Trigger process (sample included in next interval).
|
||||
counter.Add(8);
|
||||
// [6:1], two intervals without samples passed.
|
||||
EXPECT_EQ(1, observer->num_calls_);
|
||||
EXPECT_EQ(6, observer->last_sample_);
|
||||
// Make last interval pass.
|
||||
clock_.AdvanceTimeMilliseconds(1);
|
||||
counter.Add(111); // Trigger process (sample included in next interval).
|
||||
// [6:1],[8:1]
|
||||
EXPECT_EQ(2, observer->num_calls_);
|
||||
EXPECT_EQ(8, observer->last_sample_);
|
||||
// Aggregated stats.
|
||||
AggregatedStats stats = counter.GetStats();
|
||||
EXPECT_EQ(2, stats.num_samples);
|
||||
// [6:3], 3 intervals passed (2 without samples -> last value reported).
|
||||
AggregatedStats stats = counter.ProcessAndGetStats();
|
||||
EXPECT_EQ(3, stats.num_samples);
|
||||
EXPECT_EQ(6, stats.min);
|
||||
EXPECT_EQ(8, stats.max);
|
||||
EXPECT_EQ(6, stats.max);
|
||||
// Make next interval pass and verify stats: [6:3],[8:1]
|
||||
clock_.AdvanceTimeMilliseconds(1);
|
||||
counter.ProcessAndGetStats();
|
||||
EXPECT_EQ(4, observer->num_calls_);
|
||||
EXPECT_EQ(8, observer->last_sample_);
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, TestAvgCounter_WithPause) {
|
||||
// Samples: | 6 | x | x | x | - | 22 | x | // x: empty interval, -: paused
|
||||
// Stats: | 6 | 6 | 6 | 6 | - | 22 | 22 | // x -> last value reported
|
||||
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
|
||||
AvgCounter counter(&clock_, observer, true);
|
||||
// Add sample and advance 3 intervals (2 w/o samples -> last value reported).
|
||||
AddSampleAndAdvance(6, kProcessIntervalMs * 4 - 1, &counter);
|
||||
// Trigger process and verify stats: [6:3]
|
||||
counter.ProcessAndGetStats();
|
||||
EXPECT_EQ(3, observer->num_calls_);
|
||||
EXPECT_EQ(6, observer->last_sample_);
|
||||
// Make next interval pass (1 without samples).
|
||||
// Process and pause. Verify stats: [6:4].
|
||||
clock_.AdvanceTimeMilliseconds(1);
|
||||
counter.ProcessAndPause();
|
||||
EXPECT_EQ(4, observer->num_calls_); // Last value reported.
|
||||
EXPECT_EQ(6, observer->last_sample_);
|
||||
// Make next interval pass (1 without samples -> ignored while paused).
|
||||
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs * 2 - 1);
|
||||
counter.Add(22); // Stops pause.
|
||||
EXPECT_EQ(4, observer->num_calls_);
|
||||
EXPECT_EQ(6, observer->last_sample_);
|
||||
// Make next interval pass, [6:4][22:1]
|
||||
clock_.AdvanceTimeMilliseconds(1);
|
||||
counter.ProcessAndGetStats();
|
||||
EXPECT_EQ(5, observer->num_calls_);
|
||||
EXPECT_EQ(22, observer->last_sample_);
|
||||
// Make 1 interval pass (1 w/o samples -> pause stopped, last value reported).
|
||||
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs);
|
||||
counter.ProcessAndGetStats();
|
||||
EXPECT_EQ(6, observer->num_calls_);
|
||||
EXPECT_EQ(22, observer->last_sample_);
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, TestRateCounter_IntervalsWithoutSamplesIgnored) {
|
||||
// Samples: | 50 | x | 20 | // x: empty interval
|
||||
// Stats: | 25 | x | 10 | // x -> ignored
|
||||
const bool kIncludeEmptyIntervals = false;
|
||||
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
|
||||
const int kSample1 = 50; // 50 / 2 sec
|
||||
@ -309,23 +343,19 @@ TEST_F(StatsCounterTest, TestRateCounter_IntervalsWithoutSamplesIgnored) {
|
||||
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs * 3 - 1);
|
||||
// Trigger process (sample included in next interval).
|
||||
counter.Add(kSample2);
|
||||
// [25:1], one interval without samples passed.
|
||||
// [25:1], 2 intervals passed (1 without samples -> ignored).
|
||||
EXPECT_EQ(1, observer->num_calls_);
|
||||
EXPECT_EQ(25, observer->last_sample_);
|
||||
// Make last interval pass.
|
||||
// Make next interval pass and verify stats: [10:1],[25:1]
|
||||
clock_.AdvanceTimeMilliseconds(1);
|
||||
counter.Add(111); // Trigger process (sample included in next interval).
|
||||
// [10:1],[25:1]
|
||||
counter.ProcessAndGetStats();
|
||||
EXPECT_EQ(2, observer->num_calls_);
|
||||
EXPECT_EQ(10, observer->last_sample_);
|
||||
// Aggregated stats.
|
||||
AggregatedStats stats = counter.GetStats();
|
||||
EXPECT_EQ(2, stats.num_samples);
|
||||
EXPECT_EQ(10, stats.min);
|
||||
EXPECT_EQ(25, stats.max);
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, TestRateCounter_IntervalsWithoutSamplesIncluded) {
|
||||
// Samples: | 50 | x | 20 | // x: empty interval
|
||||
// Stats: | 25 | 0 | 10 | // x -> zero reported
|
||||
const bool kIncludeEmptyIntervals = true;
|
||||
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
|
||||
const int kSample1 = 50; // 50 / 2 sec
|
||||
@ -335,20 +365,76 @@ TEST_F(StatsCounterTest, TestRateCounter_IntervalsWithoutSamplesIncluded) {
|
||||
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs * 3 - 1);
|
||||
// Trigger process (sample included in next interval).
|
||||
counter.Add(kSample2);
|
||||
// [0:1],[25:1], one interval without samples passed.
|
||||
// [0:1],[25:1], 2 intervals passed (1 without samples -> zero reported).
|
||||
EXPECT_EQ(2, observer->num_calls_);
|
||||
EXPECT_EQ(25, observer->last_sample_);
|
||||
// Make last interval pass.
|
||||
EXPECT_EQ(0, observer->last_sample_);
|
||||
// Make last interval pass and verify stats: [0:1],[10:1],[25:1]
|
||||
clock_.AdvanceTimeMilliseconds(1);
|
||||
counter.Add(111); // Trigger process (sample included in next interval).
|
||||
// [0:1],[10:1],[25:1]
|
||||
AggregatedStats stats = counter.ProcessAndGetStats();
|
||||
EXPECT_EQ(25, stats.max);
|
||||
EXPECT_EQ(3, observer->num_calls_);
|
||||
EXPECT_EQ(10, observer->last_sample_);
|
||||
// Aggregated stats.
|
||||
AggregatedStats stats = counter.GetStats();
|
||||
EXPECT_EQ(3, stats.num_samples);
|
||||
EXPECT_EQ(0, stats.min);
|
||||
EXPECT_EQ(25, stats.max);
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, TestRateAccCounter_IntervalsWithoutSamplesIncluded) {
|
||||
// Samples: | 12 | x | x | x | 60 | // x: empty interval
|
||||
// Stats: | 6 | 0 | 0 | 0 | 24 | // x -> zero reported
|
||||
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
|
||||
RateAccCounter counter(&clock_, observer, true);
|
||||
VerifyStatsIsNotSet(counter.ProcessAndGetStats());
|
||||
// Advance one interval and verify stats.
|
||||
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs);
|
||||
VerifyStatsIsNotSet(counter.ProcessAndGetStats());
|
||||
// Add sample and advance 3 intervals (2 w/o samples -> zero reported).
|
||||
counter.Set(12);
|
||||
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs * 4 - 1);
|
||||
// Trigger process and verify stats: [0:2][6:1]
|
||||
counter.ProcessAndGetStats();
|
||||
EXPECT_EQ(3, observer->num_calls_);
|
||||
EXPECT_EQ(0, observer->last_sample_);
|
||||
// Make next interval pass (1 w/o samples -> zero reported), [0:3][6:1]
|
||||
clock_.AdvanceTimeMilliseconds(1);
|
||||
counter.ProcessAndGetStats();
|
||||
EXPECT_EQ(4, observer->num_calls_);
|
||||
EXPECT_EQ(0, observer->last_sample_);
|
||||
// Insert sample and advance non-complete interval, no change, [0:3][6:1]
|
||||
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs - 1);
|
||||
counter.Set(60);
|
||||
EXPECT_EQ(4, observer->num_calls_);
|
||||
// Make next interval pass, [0:3][6:1][24:1]
|
||||
clock_.AdvanceTimeMilliseconds(1);
|
||||
AggregatedStats stats = counter.ProcessAndGetStats();
|
||||
EXPECT_EQ(5, observer->num_calls_);
|
||||
EXPECT_EQ(24, observer->last_sample_);
|
||||
EXPECT_EQ(6, stats.average);
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, TestRateAccCounter_IntervalsWithoutSamplesIgnored) {
|
||||
// Samples: | 12 | x | x | x | 60 | // x: empty interval
|
||||
// Stats: | 6 | x | x | x | 24 | // x -> ignored
|
||||
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
|
||||
RateAccCounter counter(&clock_, observer, false);
|
||||
// Add sample and advance 3 intervals (2 w/o samples -> ignored).
|
||||
counter.Set(12);
|
||||
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs * 4 - 1);
|
||||
// Trigger process and verify stats: [6:1]
|
||||
counter.ProcessAndGetStats();
|
||||
EXPECT_EQ(1, observer->num_calls_);
|
||||
EXPECT_EQ(6, observer->last_sample_);
|
||||
// Make next interval pass (1 w/o samples -> ignored), [6:1]
|
||||
clock_.AdvanceTimeMilliseconds(1);
|
||||
counter.ProcessAndGetStats();
|
||||
EXPECT_EQ(1, observer->num_calls_);
|
||||
// Insert sample and advance non-complete interval, no change, [6:1]
|
||||
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs - 1);
|
||||
counter.Set(60);
|
||||
counter.ProcessAndGetStats();
|
||||
EXPECT_EQ(1, observer->num_calls_);
|
||||
// Make next interval pass, [6:1][24:1]
|
||||
clock_.AdvanceTimeMilliseconds(1);
|
||||
counter.ProcessAndGetStats();
|
||||
EXPECT_EQ(2, observer->num_calls_);
|
||||
EXPECT_EQ(24, observer->last_sample_);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user