| Index: webrtc/modules/remote_bitrate_estimator/test/metric_recorder.cc
|
| diff --git a/webrtc/modules/remote_bitrate_estimator/test/metric_recorder.cc b/webrtc/modules/remote_bitrate_estimator/test/metric_recorder.cc
|
| index 25e8989f5c5b8bd90012520d731195b3c4745f2a..eb068e5657bff848e2c0754e8e6dbf42ea3f8fa8 100644
|
| --- a/webrtc/modules/remote_bitrate_estimator/test/metric_recorder.cc
|
| +++ b/webrtc/modules/remote_bitrate_estimator/test/metric_recorder.cc
|
| @@ -17,73 +17,14 @@ namespace testing {
|
| namespace bwe {
|
|
|
| namespace {
|
| -
|
| -template <typename T>
|
| -T Sum(const std::vector<T>& input) {
|
| - T total = 0;
|
| - for (T val : input) {
|
| - total += val;
|
| - }
|
| - return total;
|
| -}
|
| -
|
| -template <typename T>
|
| -double Average(const std::vector<T>& array, size_t size) {
|
| - return static_cast<double>(Sum(array)) / size;
|
| -}
|
| -
|
| -template <typename T>
|
| -std::vector<T> Abs(const std::vector<T>& input) {
|
| - std::vector<T> output(input);
|
| - for (T val : output) {
|
| - val = std::abs(val);
|
| - }
|
| - return output;
|
| -}
|
| -
|
| -template <typename T>
|
| -std::vector<double> Pow(const std::vector<T>& input, double p) {
|
| - std::vector<double> output;
|
| - for (T val : input) {
|
| - output.push_back(pow(static_cast<double>(val), p));
|
| - }
|
| - return output;
|
| -}
|
| -
|
| -template <typename T>
|
| -double StandardDeviation(const std::vector<T>& array, size_t size) {
|
| - double mean = Average(array, size);
|
| - std::vector<double> square_values = Pow(array, 2.0);
|
| - double var = Average(square_values, size) - mean * mean;
|
| - return sqrt(var);
|
| -}
|
| -
|
| // Holder mean, Manhattan distance for p=1, EuclidianNorm/sqrt(n) for p=2.
|
| template <typename T>
|
| -double NormLp(const std::vector<T>& array, size_t size, double p) {
|
| - std::vector<T> abs_values = Abs(array);
|
| - std::vector<double> pow_values = Pow(abs_values, p);
|
| - return pow(Sum(pow_values) / size, 1.0 / p);
|
| +double NormLp(T sum, size_t size, double p) {
|
| + return pow(sum / size, 1.0 / p);
|
| }
|
| -
|
| -template <typename T>
|
| -std::vector<T> PositiveFilter(const std::vector<T>& input) {
|
| - std::vector<T> output(input);
|
| - for (T val : output) {
|
| - val = val > 0 ? val : 0;
|
| - }
|
| - return output;
|
| }
|
|
|
| -template <typename T>
|
| -std::vector<T> NegativeFilter(const std::vector<T>& input) {
|
| - std::vector<T> output(input);
|
| - for (T val : output) {
|
| - val = val < 0 ? -val : 0;
|
| - }
|
| - return output;
|
| -}
|
| -} // namespace
|
| +const double kP = 1.0; // Used for Norm Lp.
|
|
|
| LinkShare::LinkShare(ChokeFilter* choke_filter)
|
| : choke_filter_(choke_filter), running_flows_(choke_filter->flow_ids()) {
|
| @@ -119,14 +60,17 @@ MetricRecorder::MetricRecorder(const std::string algorithm_name,
|
| packet_sender_(packet_sender),
|
| link_share_(link_share),
|
| now_ms_(0),
|
| - delays_ms_(),
|
| - throughput_bytes_(),
|
| - weighted_estimate_error_(),
|
| + sum_delays_ms_(0),
|
| + delay_histogram_ms_(),
|
| + sum_delays_square_ms2_(0),
|
| + sum_throughput_bytes_(0),
|
| last_unweighted_estimate_error_(0),
|
| optimal_throughput_bits_(0),
|
| last_available_bitrate_per_flow_kbps_(0),
|
| start_computing_metrics_ms_(0),
|
| - started_computing_metrics_(false) {
|
| + started_computing_metrics_(false),
|
| + num_packets_received_(0) {
|
| + std::fill_n(sum_lp_weighted_estimate_error_, 2, 0);
|
| }
|
|
|
| void MetricRecorder::SetPlotInformation(
|
| @@ -224,27 +168,48 @@ void MetricRecorder::UpdateObjective() {
|
| }
|
|
|
| uint32_t MetricRecorder::GetTotalAvailableKbps() {
|
| + if (link_share_ == nullptr)
|
| + return 0;
|
| return link_share_->TotalAvailableKbps();
|
| }
|
|
|
| uint32_t MetricRecorder::GetAvailablePerFlowKbps() {
|
| + if (link_share_ == nullptr)
|
| + return 0;
|
| return link_share_->AvailablePerFlowKbps(flow_id_);
|
| }
|
|
|
| uint32_t MetricRecorder::GetSendingEstimateKbps() {
|
| + if (packet_sender_ == nullptr)
|
| + return 0;
|
| return packet_sender_->TargetBitrateKbps();
|
| }
|
|
|
| void MetricRecorder::PushDelayMs(int64_t delay_ms, int64_t arrival_time_ms) {
|
| if (ShouldRecord(arrival_time_ms)) {
|
| - delays_ms_.push_back(delay_ms);
|
| + sum_delays_ms_ += delay_ms;
|
| + sum_delays_square_ms2_ += delay_ms * delay_ms;
|
| + if (delay_histogram_ms_.find(delay_ms) == delay_histogram_ms_.end()) {
|
| + delay_histogram_ms_[delay_ms] = 0;
|
| + }
|
| + ++delay_histogram_ms_[delay_ms];
|
| + }
|
| +}
|
| +
|
| +void MetricRecorder::UpdateEstimateError(int64_t new_value) {
|
| + int64_t lp_value = pow(static_cast<double>(std::abs(new_value)), kP);
|
| + if (new_value < 0) {
|
| + sum_lp_weighted_estimate_error_[0] += lp_value;
|
| + } else {
|
| + sum_lp_weighted_estimate_error_[1] += lp_value;
|
| }
|
| }
|
|
|
| void MetricRecorder::PushThroughputBytes(size_t payload_size,
|
| int64_t arrival_time_ms) {
|
| if (ShouldRecord(arrival_time_ms)) {
|
| - throughput_bytes_.push_back(payload_size);
|
| + ++num_packets_received_;
|
| + sum_throughput_bytes_ += payload_size;
|
|
|
| int64_t current_available_per_flow_kbps =
|
| static_cast<int64_t>(GetAvailablePerFlowKbps());
|
| @@ -253,10 +218,12 @@ void MetricRecorder::PushThroughputBytes(size_t payload_size,
|
| static_cast<int64_t>(GetSendingEstimateKbps()) -
|
| current_available_per_flow_kbps;
|
|
|
| - weighted_estimate_error_.push_back(
|
| - ((current_bitrate_diff_kbps + last_unweighted_estimate_error_) *
|
| - (arrival_time_ms - plot_information_[kThroughput].time_ms)) /
|
| - 2);
|
| + int64_t weighted_estimate_error =
|
| + (((current_bitrate_diff_kbps + last_unweighted_estimate_error_) *
|
| + (arrival_time_ms - plot_information_[kThroughput].time_ms)) /
|
| + 2);
|
| +
|
| + UpdateEstimateError(weighted_estimate_error);
|
|
|
| optimal_throughput_bits_ +=
|
| ((current_available_per_flow_kbps +
|
| @@ -281,56 +248,21 @@ bool MetricRecorder::ShouldRecord(int64_t arrival_time_ms) {
|
| }
|
| }
|
|
|
| -// The weighted_estimate_error_ was weighted based on time windows.
|
| -// This function scales back the result before plotting.
|
| -double MetricRecorder::Renormalize(double x) {
|
| - size_t num_packets_received = delays_ms_.size();
|
| - return (x * num_packets_received) / now_ms_;
|
| -}
|
| +void MetricRecorder::PlotThroughputHistogram(
|
| + const std::string& title,
|
| + const std::string& bwe_name,
|
| + size_t num_flows,
|
| + int64_t extra_offset_ms,
|
| + const std::string optimum_id) const {
|
| + double optimal_bitrate_per_flow_kbps = static_cast<double>(
|
| + optimal_throughput_bits_ / RunDurationMs(extra_offset_ms));
|
|
|
| -inline double U(int64_t x, double alpha) {
|
| - if (alpha == 1.0) {
|
| - return log(static_cast<double>(x));
|
| - }
|
| - return pow(static_cast<double>(x), 1.0 - alpha) / (1.0 - alpha);
|
| -}
|
| -
|
| -inline double U(size_t x, double alpha) {
|
| - return U(static_cast<int64_t>(x), alpha);
|
| -}
|
| + double neg_error = Renormalize(
|
| + NormLp(sum_lp_weighted_estimate_error_[0], num_packets_received_, kP));
|
| + double pos_error = Renormalize(
|
| + NormLp(sum_lp_weighted_estimate_error_[1], num_packets_received_, kP));
|
|
|
| -// TODO(magalhaesc): Update ObjectiveFunction.
|
| -double MetricRecorder::ObjectiveFunction() {
|
| - const double kDelta = 0.15; // Delay penalty factor.
|
| - const double kAlpha = 1.0;
|
| - const double kBeta = 1.0;
|
| -
|
| - double throughput_metric = U(Sum(throughput_bytes_), kAlpha);
|
| - double delay_penalty = kDelta * U(Sum(delays_ms_), kBeta);
|
| -
|
| - return throughput_metric - delay_penalty;
|
| -}
|
| -
|
| -void MetricRecorder::PlotThroughputHistogram(const std::string& title,
|
| - const std::string& bwe_name,
|
| - size_t num_flows,
|
| - int64_t extra_offset_ms,
|
| - const std::string optimum_id) {
|
| - size_t num_packets_received = delays_ms_.size();
|
| -
|
| - int64_t duration_ms = now_ms_ - start_computing_metrics_ms_ - extra_offset_ms;
|
| -
|
| - double average_bitrate_kbps =
|
| - static_cast<double>(8 * Sum(throughput_bytes_) / duration_ms);
|
| -
|
| - double optimal_bitrate_per_flow_kbps =
|
| - static_cast<double>(optimal_throughput_bits_ / duration_ms);
|
| -
|
| - std::vector<int64_t> positive = PositiveFilter(weighted_estimate_error_);
|
| - std::vector<int64_t> negative = NegativeFilter(weighted_estimate_error_);
|
| -
|
| - double p_error = Renormalize(NormLp(positive, num_packets_received, 1.0));
|
| - double n_error = Renormalize(NormLp(negative, num_packets_received, 1.0));
|
| + double average_bitrate_kbps = AverageBitrateKbps(extra_offset_ms);
|
|
|
| // Prevent the error to be too close to zero (plotting issue).
|
| double extra_error = average_bitrate_kbps / 500;
|
| @@ -341,8 +273,8 @@ void MetricRecorder::PlotThroughputHistogram(const std::string& title,
|
| BWE_TEST_LOGGING_LABEL(4, title, "average_bitrate_(kbps)", num_flows);
|
| BWE_TEST_LOGGING_LIMITERRORBAR(
|
| 4, bwe_name, average_bitrate_kbps,
|
| - average_bitrate_kbps - n_error - extra_error,
|
| - average_bitrate_kbps + p_error + extra_error, "estimate_error",
|
| + average_bitrate_kbps - neg_error - extra_error,
|
| + average_bitrate_kbps + pos_error + extra_error, "estimate_error",
|
| optimal_bitrate_per_flow_kbps, optimum_title, flow_id_);
|
|
|
| BWE_TEST_LOGGING_LOG1("RESULTS >>> " + bwe_name + " Channel utilization : ",
|
| @@ -350,8 +282,8 @@ void MetricRecorder::PlotThroughputHistogram(const std::string& title,
|
| 100.0 * static_cast<double>(average_bitrate_kbps) /
|
| optimal_bitrate_per_flow_kbps);
|
|
|
| - RTC_UNUSED(p_error);
|
| - RTC_UNUSED(n_error);
|
| + RTC_UNUSED(pos_error);
|
| + RTC_UNUSED(neg_error);
|
| RTC_UNUSED(extra_error);
|
| RTC_UNUSED(optimal_bitrate_per_flow_kbps);
|
| }
|
| @@ -359,32 +291,22 @@ void MetricRecorder::PlotThroughputHistogram(const std::string& title,
|
| void MetricRecorder::PlotThroughputHistogram(const std::string& title,
|
| const std::string& bwe_name,
|
| size_t num_flows,
|
| - int64_t extra_offset_ms) {
|
| + int64_t extra_offset_ms) const {
|
| PlotThroughputHistogram(title, bwe_name, num_flows, extra_offset_ms, "");
|
| }
|
|
|
| void MetricRecorder::PlotDelayHistogram(const std::string& title,
|
| const std::string& bwe_name,
|
| size_t num_flows,
|
| - int64_t one_way_path_delay_ms) {
|
| - size_t num_packets_received = delays_ms_.size();
|
| - double average_delay_ms = Average(delays_ms_, num_packets_received);
|
| + int64_t one_way_path_delay_ms) const {
|
| + double average_delay_ms =
|
| + static_cast<double>(sum_delays_ms_) / num_packets_received_;
|
|
|
| // Prevent the error to be too close to zero (plotting issue).
|
| double extra_error = average_delay_ms / 500;
|
| -
|
| - double tenth_sigma_ms =
|
| - StandardDeviation(delays_ms_, num_packets_received) / 10.0 + extra_error;
|
| -
|
| - size_t per_5_index = (num_packets_received - 1) / 20;
|
| - std::nth_element(delays_ms_.begin(), delays_ms_.begin() + per_5_index,
|
| - delays_ms_.end());
|
| - int64_t percentile_5_ms = delays_ms_[per_5_index];
|
| -
|
| - size_t per_95_index = num_packets_received - 1 - per_5_index;
|
| - std::nth_element(delays_ms_.begin(), delays_ms_.begin() + per_95_index,
|
| - delays_ms_.end());
|
| - int64_t percentile_95_ms = delays_ms_[per_95_index];
|
| + double tenth_sigma_ms = DelayStdDev() / 10.0 + extra_error;
|
| + int64_t percentile_5_ms = NthDelayPercentile(5);
|
| + int64_t percentile_95_ms = NthDelayPercentile(95);
|
|
|
| BWE_TEST_LOGGING_LABEL(5, title, "average_delay_(ms)", num_flows)
|
| BWE_TEST_LOGGING_ERRORBAR(5, bwe_name, average_delay_ms, percentile_5_ms,
|
| @@ -407,7 +329,7 @@ void MetricRecorder::PlotDelayHistogram(const std::string& title,
|
| void MetricRecorder::PlotLossHistogram(const std::string& title,
|
| const std::string& bwe_name,
|
| size_t num_flows,
|
| - float global_loss_ratio) {
|
| + float global_loss_ratio) const {
|
| BWE_TEST_LOGGING_LABEL(6, title, "packet_loss_ratio_(%)", num_flows)
|
| BWE_TEST_LOGGING_BAR(6, bwe_name, 100.0f * global_loss_ratio, flow_id_);
|
|
|
| @@ -417,7 +339,7 @@ void MetricRecorder::PlotLossHistogram(const std::string& title,
|
|
|
| void MetricRecorder::PlotObjectiveHistogram(const std::string& title,
|
| const std::string& bwe_name,
|
| - size_t num_flows) {
|
| + size_t num_flows) const {
|
| BWE_TEST_LOGGING_LABEL(7, title, "objective_function", num_flows)
|
| BWE_TEST_LOGGING_BAR(7, bwe_name, ObjectiveFunction(), flow_id_);
|
| }
|
| @@ -444,6 +366,73 @@ void MetricRecorder::ResumeFlow(int64_t paused_time_ms) {
|
| link_share_->ResumeFlow(flow_id_);
|
| }
|
|
|
| +double MetricRecorder::AverageBitrateKbps(int64_t extra_offset_ms) const {
|
| + int64_t duration_ms = RunDurationMs(extra_offset_ms);
|
| + if (duration_ms == 0)
|
| + return 0.0;
|
| + return static_cast<double>(8 * sum_throughput_bytes_ / duration_ms);
|
| +}
|
| +
|
| +int64_t MetricRecorder::RunDurationMs(int64_t extra_offset_ms) const {
|
| + return now_ms_ - start_computing_metrics_ms_ - extra_offset_ms;
|
| +}
|
| +
|
| +double MetricRecorder::DelayStdDev() const {
|
| + if (num_packets_received_ == 0) {
|
| + return 0.0;
|
| + }
|
| + double mean = static_cast<double>(sum_delays_ms_) / num_packets_received_;
|
| + double mean2 =
|
| + static_cast<double>(sum_delays_square_ms2_) / num_packets_received_;
|
| + return sqrt(mean2 - pow(mean, 2.0));
|
| +}
|
| +
|
| +// Since delay values are bounded in a subset of [0, 5000] ms,
|
| +// this function's execution time is O(1), independend of num_packets_received_.
|
| +int64_t MetricRecorder::NthDelayPercentile(int n) const {
|
| + if (num_packets_received_ == 0) {
|
| + return 0;
|
| + }
|
| + size_t num_packets_remaining = (n * num_packets_received_) / 100;
|
| + for (auto hist : delay_histogram_ms_) {
|
| + if (num_packets_remaining <= hist.second)
|
| + return static_cast<int64_t>(hist.first);
|
| + num_packets_remaining -= hist.second;
|
| + }
|
| +
|
| + assert(false);
|
| + return -1;
|
| +}
|
| +
|
| +// The weighted_estimate_error_ was weighted based on time windows.
|
| +// This function scales back the result before plotting.
|
| +double MetricRecorder::Renormalize(double x) const {
|
| + return (x * num_packets_received_) / now_ms_;
|
| +}
|
| +
|
| +inline double U(int64_t x, double alpha) {
|
| + if (alpha == 1.0) {
|
| + return log(static_cast<double>(x));
|
| + }
|
| + return pow(static_cast<double>(x), 1.0 - alpha) / (1.0 - alpha);
|
| +}
|
| +
|
| +inline double U(size_t x, double alpha) {
|
| + return U(static_cast<int64_t>(x), alpha);
|
| +}
|
| +
|
| +// TODO(magalhaesc): Update ObjectiveFunction.
|
| +double MetricRecorder::ObjectiveFunction() const {
|
| + const double kDelta = 0.15; // Delay penalty factor.
|
| + const double kAlpha = 1.0;
|
| + const double kBeta = 1.0;
|
| +
|
| + double throughput_metric = U(sum_throughput_bytes_, kAlpha);
|
| + double delay_penalty = kDelta * U(sum_delays_ms_, kBeta);
|
| +
|
| + return throughput_metric - delay_penalty;
|
| +}
|
| +
|
| } // namespace bwe
|
| } // namespace testing
|
| } // namespace webrtc
|
|
|