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:
asapersson 2016-09-09 00:13:35 -07:00 committed by Commit bot
parent 2a5f371df3
commit ce2e13602e
5 changed files with 304 additions and 102 deletions

View File

@ -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,

View File

@ -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;
}

View File

@ -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

View File

@ -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);
};

View File

@ -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