Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(493)

Unified Diff: webrtc/modules/remote_bitrate_estimator/test/metric_recorder.cc

Issue 1202253003: More Simulation Framework features (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Comments addressed Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
new file mode 100644
index 0000000000000000000000000000000000000000..23853cc493e40d38599f5c774bc15008f8a472dd
--- /dev/null
+++ b/webrtc/modules/remote_bitrate_estimator/test/metric_recorder.cc
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 2015 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 "webrtc/modules/remote_bitrate_estimator/test/metric_recorder.h"
+
+#include <algorithm>
+
+namespace webrtc {
+namespace testing {
+namespace bwe {
+
+namespace {
+
+template <typename T>
+T Sum(const std::vector<T>& input) {
+ T total = 0;
+ for (auto it = input.begin(); it != input.end(); ++it) {
+ total += *it;
+ }
+ 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;
+ for (auto it = input.begin(); it != input.end(); ++it) {
+ output.push_back(std::abs(*it));
+ }
+ return output;
+}
+
+template <typename T>
+std::vector<double> Pow(const std::vector<T>& input, double p) {
+ std::vector<double> output;
+ for (auto it = input.begin(); it != input.end(); ++it) {
+ output.push_back(pow(static_cast<double>(*it), 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);
+}
+
+template <typename T>
+std::vector<T> PositiveFilter(const std::vector<T>& input) {
+ std::vector<T> output(input);
+ for (auto it = output.begin(); it != output.end(); ++it) {
stefan-webrtc 2015/07/02 11:03:41 Can't you do this? for (T val : output) val = va
magalhaesc 2015/07/02 17:06:18 Done.
+ (*it) = (*it) > 0 ? (*it) : 0;
+ }
+ return output;
+}
+
+template <typename T>
+std::vector<T> NegativeFilter(const std::vector<T>& input) {
+ std::vector<T> output(input);
+ for (auto it = output.begin(); it != output.end(); ++it) {
+ (*it) = (*it) < 0 ? -(*it) : 0;
+ }
+ return output;
+}
+} // namespace
+
+MetricRecorder::MetricRecorder(const std::string algorithm_name,
+ int flow_id,
+ PacketSender* packet_sender,
+ ChokeFilter* choke_filter)
+ : algorithm_name_(algorithm_name),
+ flow_id_(flow_id),
+ packet_sender_(packet_sender),
+ choke_filter_(choke_filter),
+ now_ms_(0),
+ delays_ms_(),
+ throughput_bytes_(),
+ weighted_estimate_error_(),
+ 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) {
+ std::fill(last_plot_ms_, last_plot_ms_ + kNumMetrics, 0);
+ std::fill(plot_, plot_ + kNumMetrics, true);
+ plot_[kObjective] = false;
+ plot_[kAvailablePerFlow] = false;
+}
+
+void MetricRecorder::PlotLine(int windows_id,
+ const std::string& prefix,
+ int64_t x,
+ int64_t y) {
+ BWE_TEST_LOGGING_PLOT_WITH_NAME(windows_id, prefix, x, y, algorithm_name_);
+}
+
+void MetricRecorder::PlotThroughput(const std::string& prefix,
+ int64_t time_ms,
+ int64_t bitrate_kbps) {
+ if (!plot_[kThroughput])
+ return;
+ static const int kThroughputPlotIntervalMs = 100;
+ if (time_ms - last_plot_ms_[kThroughput] > kThroughputPlotIntervalMs) {
+ PlotLine(0, prefix, time_ms, bitrate_kbps);
+ last_plot_ms_[kThroughput] = time_ms;
+ }
+}
+
+void MetricRecorder::PlotDelay(const std::string& prefix,
+ int64_t time_ms,
+ int64_t delay_ms) {
+ if (!plot_[kDelay])
+ return;
+ static const int kDelayPlotIntervalMs = 100;
+ if (time_ms - last_plot_ms_[kDelay] > kDelayPlotIntervalMs) {
+ PlotLine(1, prefix, time_ms, delay_ms);
+ last_plot_ms_[kDelay] = time_ms;
+ }
+}
+
+void MetricRecorder::PlotLoss(const std::string& prefix,
+ int64_t time_ms,
+ double loss) {
+ if (!plot_[kLoss])
+ return;
+ static const int kPacketLossPlotIntervalMs = 500;
+ if (time_ms - last_plot_ms_[kLoss] > kPacketLossPlotIntervalMs) {
+ PlotLine(2, prefix, time_ms, loss);
+ last_plot_ms_[kLoss] = time_ms;
+ }
+}
+
+void MetricRecorder::PlotObjective(const std::string& prefix, int64_t time_ms) {
+ if (!plot_[kObjective])
+ return;
+ static const int kMetricPlotIntervalMs = 1000;
+ if (time_ms - last_plot_ms_[kObjective] > kMetricPlotIntervalMs) {
+ PlotLine(3, prefix, time_ms, ObjectiveFunction());
+ last_plot_ms_[kObjective] = time_ms;
+ }
+}
+
+void MetricRecorder::PlotTotalAvailableCapacity(const std::string& prefix,
+ int64_t time_ms) {
+ if (!plot_[kTotalAvailable])
+ return;
+ static const int kCapacityPlotIntervalMs = 1000;
+ if (time_ms - last_plot_ms_[kTotalAvailable] > kCapacityPlotIntervalMs) {
+ BWE_TEST_LOGGING_PLOT_WITH_NAME(0, prefix, time_ms, GetTotalAvailableKbps(),
+ "Available");
+ last_plot_ms_[kTotalAvailable] = time_ms;
+ }
+}
+
+void MetricRecorder::PlotAvailableCapacityPerFlow(const std::string& prefix,
+ int64_t time_ms) {
+ if (!plot_[kAvailablePerFlow])
+ return;
+ static const int kCapacityPlotIntervalMs = 1000;
+ if (time_ms - last_plot_ms_[kAvailablePerFlow] > kCapacityPlotIntervalMs) {
+ BWE_TEST_LOGGING_PLOT_WITH_NAME(
+ 0, prefix, time_ms, GetAvailablePerFlowKbps(), "Available_per_flow");
+ last_plot_ms_[kAvailablePerFlow] = time_ms;
+ }
+}
+
+uint32_t MetricRecorder::GetTotalAvailableKbps() {
+ return choke_filter_->TotalAvailableKbps();
+}
+
+uint32_t MetricRecorder::GetAvailablePerFlowKbps() {
+ return choke_filter_->AvailablePerFlowKbps(flow_id_);
+}
+
+uint32_t MetricRecorder::GetSendingEstimateKbps() {
+ return packet_sender_->TargetBitrateKbps();
+}
+
+void MetricRecorder::Update(int64_t time_ms) {
+ now_ms_ = std::max(now_ms_, time_ms);
+ last_available_bitrate_per_flow_kbps_ = GetAvailablePerFlowKbps();
+}
+
+void MetricRecorder::PushDelayMs(int64_t delay_ms, int64_t arrival_time_ms) {
+ if (ShouldRecord(arrival_time_ms)) {
+ delays_ms_.push_back(delay_ms);
+ }
+}
+
+void MetricRecorder::PushThroughputBytes(size_t payload_size,
+ int64_t arrival_time_ms) {
+ if (ShouldRecord(arrival_time_ms)) {
+ throughput_bytes_.push_back(payload_size);
+
+ int64_t current_available_per_flow_kbps =
+ static_cast<int64_t>(GetAvailablePerFlowKbps());
+
+ int64_t current_bitrate_diff_kbps =
+ static_cast<int64_t>(GetSendingEstimateKbps()) -
+ current_available_per_flow_kbps;
+
+ // now_ms_ was still not updated here.
+ weighted_estimate_error_.push_back(
+ ((current_bitrate_diff_kbps + last_unweighted_estimate_error_) *
+ (arrival_time_ms - now_ms_)) /
+ 2);
+
+ optimal_throughput_bits_ += ((current_available_per_flow_kbps +
+ last_available_bitrate_per_flow_kbps_) *
+ (arrival_time_ms - now_ms_)) /
+ 2;
+ }
+}
+
+bool MetricRecorder::ShouldRecord(int64_t arrival_time_ms) {
+ if (arrival_time_ms >= start_computing_metrics_ms_) {
+ if (!started_computing_metrics_) {
+ start_computing_metrics_ms_ = arrival_time_ms;
+ now_ms_ = arrival_time_ms;
+ started_computing_metrics_ = true;
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// 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_;
+}
+
+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 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,
+ int 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));
+
+ // Prevent the error to be too close to zero (plotting issue).
+ double extra_error = average_bitrate_kbps / 500;
+
+ std::string optimum_title =
+ optimum_id.empty() ? "optimal_bitrate" : "optimal_bitrates#" + optimum_id;
+
+ 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",
+ optimal_bitrate_per_flow_kbps, optimum_title, flow_id_);
+
+ // Silencing unused variable compiling error.
+ RTC_UNUSED(p_error);
+ RTC_UNUSED(n_error);
+ RTC_UNUSED(extra_error);
+ RTC_UNUSED(optimal_bitrate_per_flow_kbps);
+}
+
+void MetricRecorder::PlotThroughputHistogram(const std::string& title,
+ const std::string& bwe_name,
+ int num_flows,
+ int64_t extra_offset_ms) {
+ PlotThroughputHistogram(title, bwe_name, num_flows, extra_offset_ms, "");
+}
+
+void MetricRecorder::PlotDelayHistogram(const std::string& title,
+ const std::string& bwe_name,
+ int num_flows) {
+ size_t num_packets_received = delays_ms_.size();
+ double average_delay_ms = Average(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;
+
+ BWE_TEST_LOGGING_LABEL(5, title, "average_delay_(ms)", num_flows)
+ BWE_TEST_LOGGING_ERRORBAR(
+ 5, bwe_name, average_delay_ms, average_delay_ms - tenth_sigma_ms,
+ average_delay_ms + tenth_sigma_ms, "sigma/10", flow_id_);
+
+ // Silencing unused variable compiling error.
+ RTC_UNUSED(tenth_sigma_ms);
+}
+
+void MetricRecorder::PlotLossHistogram(const std::string& title,
+ const std::string& bwe_name,
+ int num_flows,
+ float global_loss_ratio) {
+ 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_);
+}
+
+void MetricRecorder::PlotObjectiveHistogram(const std::string& title,
+ const std::string& bwe_name,
+ int num_flows) {
+ BWE_TEST_LOGGING_LABEL(7, title, "objective_function", num_flows)
+ BWE_TEST_LOGGING_BAR(7, bwe_name, ObjectiveFunction(), flow_id_);
+}
+
+} // namespace bwe
+} // namespace testing
+} // namespace webrtc

Powered by Google App Engine
This is Rietveld 408576698