1 /* 2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "webrtc/modules/remote_bitrate_estimator/test/metric_recorder.h" 12 13 #include <algorithm> 14 15 #include "webrtc/modules/remote_bitrate_estimator/test/packet_sender.h" 16 17 namespace webrtc { 18 namespace testing { 19 namespace bwe { 20 21 namespace { 22 // Holder mean, Manhattan distance for p=1, EuclidianNorm/sqrt(n) for p=2. 23 template <typename T> 24 double NormLp(T sum, size_t size, double p) { 25 return pow(sum / size, 1.0 / p); 26 } 27 } 28 29 const double kP = 1.0; // Used for Norm Lp. 30 31 LinkShare::LinkShare(ChokeFilter* choke_filter) 32 : choke_filter_(choke_filter), running_flows_(choke_filter->flow_ids()) { 33 } 34 35 void LinkShare::PauseFlow(int flow_id) { 36 running_flows_.erase(flow_id); 37 } 38 39 void LinkShare::ResumeFlow(int flow_id) { 40 running_flows_.insert(flow_id); 41 } 42 43 uint32_t LinkShare::TotalAvailableKbps() { 44 return choke_filter_->capacity_kbps(); 45 } 46 47 uint32_t LinkShare::AvailablePerFlowKbps(int flow_id) { 48 uint32_t available_capacity_per_flow_kbps = 0; 49 if (running_flows_.find(flow_id) != running_flows_.end()) { 50 available_capacity_per_flow_kbps = 51 TotalAvailableKbps() / static_cast<uint32_t>(running_flows_.size()); 52 } 53 return available_capacity_per_flow_kbps; 54 } 55 56 MetricRecorder::MetricRecorder(const std::string algorithm_name, 57 int flow_id, 58 PacketSender* packet_sender, 59 LinkShare* link_share) 60 : algorithm_name_(algorithm_name), 61 flow_id_(flow_id), 62 link_share_(link_share), 63 now_ms_(0), 64 sum_delays_ms_(0), 65 delay_histogram_ms_(), 66 sum_delays_square_ms2_(0), 67 sum_throughput_bytes_(0), 68 last_unweighted_estimate_error_(0), 69 optimal_throughput_bits_(0), 70 last_available_bitrate_per_flow_kbps_(0), 71 start_computing_metrics_ms_(0), 72 started_computing_metrics_(false), 73 num_packets_received_(0) { 74 std::fill_n(sum_lp_weighted_estimate_error_, 2, 0); 75 if (packet_sender != nullptr) 76 packet_sender->set_metric_recorder(this); 77 } 78 79 void MetricRecorder::SetPlotInformation( 80 const std::vector<std::string>& prefixes, 81 bool plot_delay, 82 bool plot_loss) { 83 assert(prefixes.size() == kNumMetrics); 84 for (size_t i = 0; i < kNumMetrics; ++i) { 85 plot_information_[i].prefix = prefixes[i]; 86 } 87 plot_information_[kThroughput].plot_interval_ms = 100; 88 plot_information_[kSendingEstimate].plot_interval_ms = 100; 89 plot_information_[kDelay].plot_interval_ms = 100; 90 plot_information_[kLoss].plot_interval_ms = 500; 91 plot_information_[kObjective].plot_interval_ms = 1000; 92 plot_information_[kTotalAvailable].plot_interval_ms = 1000; 93 plot_information_[kAvailablePerFlow].plot_interval_ms = 1000; 94 95 for (int i = kThroughput; i < kNumMetrics; ++i) { 96 plot_information_[i].last_plot_ms = 0; 97 switch (i) { 98 case kSendingEstimate: 99 case kObjective: 100 case kAvailablePerFlow: 101 plot_information_[i].plot = false; 102 break; 103 case kLoss: 104 plot_information_[i].plot = plot_loss; 105 break; 106 case kDelay: 107 plot_information_[i].plot = plot_delay; 108 break; 109 default: 110 plot_information_[i].plot = true; 111 } 112 } 113 } 114 115 void MetricRecorder::PlotAllDynamics() { 116 for (int i = kThroughput; i < kNumMetrics; ++i) { 117 if (plot_information_[i].plot && 118 now_ms_ - plot_information_[i].last_plot_ms >= 119 plot_information_[i].plot_interval_ms) { 120 PlotDynamics(i); 121 } 122 } 123 } 124 125 void MetricRecorder::PlotDynamics(int metric) { 126 if (metric == kTotalAvailable) { 127 BWE_TEST_LOGGING_PLOT_WITH_NAME( 128 0, plot_information_[kTotalAvailable].prefix, now_ms_, 129 GetTotalAvailableKbps(), "Available"); 130 } else if (metric == kAvailablePerFlow) { 131 BWE_TEST_LOGGING_PLOT_WITH_NAME( 132 0, plot_information_[kAvailablePerFlow].prefix, now_ms_, 133 GetAvailablePerFlowKbps(), "Available_per_flow"); 134 } else { 135 PlotLine(metric, plot_information_[metric].prefix, 136 plot_information_[metric].time_ms, 137 plot_information_[metric].value); 138 } 139 plot_information_[metric].last_plot_ms = now_ms_; 140 } 141 142 template <typename T> 143 void MetricRecorder::PlotLine(int windows_id, 144 const std::string& prefix, 145 int64_t time_ms, 146 T y) { 147 BWE_TEST_LOGGING_PLOT_WITH_NAME(windows_id, prefix, time_ms, 148 static_cast<double>(y), algorithm_name_); 149 } 150 151 void MetricRecorder::UpdateTimeMs(int64_t time_ms) { 152 now_ms_ = std::max(now_ms_, time_ms); 153 } 154 155 void MetricRecorder::UpdateThroughput(int64_t bitrate_kbps, 156 size_t payload_size) { 157 // Total throughput should be computed before updating the time. 158 PushThroughputBytes(payload_size, now_ms_); 159 plot_information_[kThroughput].Update(now_ms_, bitrate_kbps); 160 } 161 162 void MetricRecorder::UpdateSendingEstimateKbps(int64_t bitrate_kbps) { 163 plot_information_[kSendingEstimate].Update(now_ms_, bitrate_kbps); 164 } 165 166 void MetricRecorder::UpdateDelayMs(int64_t delay_ms) { 167 PushDelayMs(delay_ms, now_ms_); 168 plot_information_[kDelay].Update(now_ms_, delay_ms); 169 } 170 171 void MetricRecorder::UpdateLoss(float loss_ratio) { 172 plot_information_[kLoss].Update(now_ms_, loss_ratio); 173 } 174 175 void MetricRecorder::UpdateObjective() { 176 plot_information_[kObjective].Update(now_ms_, ObjectiveFunction()); 177 } 178 179 uint32_t MetricRecorder::GetTotalAvailableKbps() { 180 if (link_share_ == nullptr) 181 return 0; 182 return link_share_->TotalAvailableKbps(); 183 } 184 185 uint32_t MetricRecorder::GetAvailablePerFlowKbps() { 186 if (link_share_ == nullptr) 187 return 0; 188 return link_share_->AvailablePerFlowKbps(flow_id_); 189 } 190 191 uint32_t MetricRecorder::GetSendingEstimateKbps() { 192 return static_cast<uint32_t>(plot_information_[kSendingEstimate].value); 193 } 194 195 void MetricRecorder::PushDelayMs(int64_t delay_ms, int64_t arrival_time_ms) { 196 if (ShouldRecord(arrival_time_ms)) { 197 sum_delays_ms_ += delay_ms; 198 sum_delays_square_ms2_ += delay_ms * delay_ms; 199 if (delay_histogram_ms_.find(delay_ms) == delay_histogram_ms_.end()) { 200 delay_histogram_ms_[delay_ms] = 0; 201 } 202 ++delay_histogram_ms_[delay_ms]; 203 } 204 } 205 206 void MetricRecorder::UpdateEstimateError(int64_t new_value) { 207 int64_t lp_value = pow(static_cast<double>(std::abs(new_value)), kP); 208 if (new_value < 0) { 209 sum_lp_weighted_estimate_error_[0] += lp_value; 210 } else { 211 sum_lp_weighted_estimate_error_[1] += lp_value; 212 } 213 } 214 215 void MetricRecorder::PushThroughputBytes(size_t payload_size, 216 int64_t arrival_time_ms) { 217 if (ShouldRecord(arrival_time_ms)) { 218 ++num_packets_received_; 219 sum_throughput_bytes_ += payload_size; 220 221 int64_t current_available_per_flow_kbps = 222 static_cast<int64_t>(GetAvailablePerFlowKbps()); 223 224 int64_t current_bitrate_diff_kbps = 225 static_cast<int64_t>(GetSendingEstimateKbps()) - 226 current_available_per_flow_kbps; 227 228 int64_t weighted_estimate_error = 229 (((current_bitrate_diff_kbps + last_unweighted_estimate_error_) * 230 (arrival_time_ms - plot_information_[kThroughput].time_ms)) / 231 2); 232 233 UpdateEstimateError(weighted_estimate_error); 234 235 optimal_throughput_bits_ += 236 ((current_available_per_flow_kbps + 237 last_available_bitrate_per_flow_kbps_) * 238 (arrival_time_ms - plot_information_[kThroughput].time_ms)) / 239 2; 240 241 last_available_bitrate_per_flow_kbps_ = current_available_per_flow_kbps; 242 } 243 } 244 245 bool MetricRecorder::ShouldRecord(int64_t arrival_time_ms) { 246 if (arrival_time_ms >= start_computing_metrics_ms_) { 247 if (!started_computing_metrics_) { 248 start_computing_metrics_ms_ = arrival_time_ms; 249 now_ms_ = arrival_time_ms; 250 started_computing_metrics_ = true; 251 } 252 return true; 253 } else { 254 return false; 255 } 256 } 257 258 void MetricRecorder::PlotThroughputHistogram( 259 const std::string& title, 260 const std::string& bwe_name, 261 size_t num_flows, 262 int64_t extra_offset_ms, 263 const std::string optimum_id) const { 264 double optimal_bitrate_per_flow_kbps = static_cast<double>( 265 optimal_throughput_bits_ / RunDurationMs(extra_offset_ms)); 266 267 double neg_error = Renormalize( 268 NormLp(sum_lp_weighted_estimate_error_[0], num_packets_received_, kP)); 269 double pos_error = Renormalize( 270 NormLp(sum_lp_weighted_estimate_error_[1], num_packets_received_, kP)); 271 272 double average_bitrate_kbps = AverageBitrateKbps(extra_offset_ms); 273 274 // Prevent the error to be too close to zero (plotting issue). 275 double extra_error = average_bitrate_kbps / 500; 276 277 std::string optimum_title = 278 optimum_id.empty() ? "optimal_bitrate" : "optimal_bitrates#" + optimum_id; 279 280 BWE_TEST_LOGGING_LABEL(4, title, "average_bitrate_(kbps)", num_flows); 281 BWE_TEST_LOGGING_LIMITERRORBAR( 282 4, bwe_name, average_bitrate_kbps, 283 average_bitrate_kbps - neg_error - extra_error, 284 average_bitrate_kbps + pos_error + extra_error, "estimate_error", 285 optimal_bitrate_per_flow_kbps, optimum_title, flow_id_); 286 287 BWE_TEST_LOGGING_LOG1("RESULTS >>> " + bwe_name + " Channel utilization : ", 288 "%lf %%", 289 100.0 * static_cast<double>(average_bitrate_kbps) / 290 optimal_bitrate_per_flow_kbps); 291 292 RTC_UNUSED(pos_error); 293 RTC_UNUSED(neg_error); 294 RTC_UNUSED(extra_error); 295 RTC_UNUSED(optimal_bitrate_per_flow_kbps); 296 } 297 298 void MetricRecorder::PlotThroughputHistogram(const std::string& title, 299 const std::string& bwe_name, 300 size_t num_flows, 301 int64_t extra_offset_ms) const { 302 PlotThroughputHistogram(title, bwe_name, num_flows, extra_offset_ms, ""); 303 } 304 305 void MetricRecorder::PlotDelayHistogram(const std::string& title, 306 const std::string& bwe_name, 307 size_t num_flows, 308 int64_t one_way_path_delay_ms) const { 309 double average_delay_ms = 310 static_cast<double>(sum_delays_ms_) / num_packets_received_; 311 312 // Prevent the error to be too close to zero (plotting issue). 313 double extra_error = average_delay_ms / 500; 314 double tenth_sigma_ms = DelayStdDev() / 10.0 + extra_error; 315 int64_t percentile_5_ms = NthDelayPercentile(5); 316 int64_t percentile_95_ms = NthDelayPercentile(95); 317 318 BWE_TEST_LOGGING_LABEL(5, title, "average_delay_(ms)", num_flows) 319 BWE_TEST_LOGGING_ERRORBAR(5, bwe_name, average_delay_ms, percentile_5_ms, 320 percentile_95_ms, "5th and 95th percentiles", 321 flow_id_); 322 323 // Log added latency, disregard baseline path delay. 324 BWE_TEST_LOGGING_LOG1("RESULTS >>> " + bwe_name + " Delay average : ", 325 "%lf ms", average_delay_ms - one_way_path_delay_ms); 326 BWE_TEST_LOGGING_LOG1("RESULTS >>> " + bwe_name + " Delay 5th percentile : ", 327 "%ld ms", percentile_5_ms - one_way_path_delay_ms); 328 BWE_TEST_LOGGING_LOG1("RESULTS >>> " + bwe_name + " Delay 95th percentile : ", 329 "%ld ms", percentile_95_ms - one_way_path_delay_ms); 330 331 RTC_UNUSED(tenth_sigma_ms); 332 RTC_UNUSED(percentile_5_ms); 333 RTC_UNUSED(percentile_95_ms); 334 } 335 336 void MetricRecorder::PlotLossHistogram(const std::string& title, 337 const std::string& bwe_name, 338 size_t num_flows, 339 float global_loss_ratio) const { 340 BWE_TEST_LOGGING_LABEL(6, title, "packet_loss_ratio_(%)", num_flows) 341 BWE_TEST_LOGGING_BAR(6, bwe_name, 100.0f * global_loss_ratio, flow_id_); 342 343 BWE_TEST_LOGGING_LOG1("RESULTS >>> " + bwe_name + " Loss Ratio : ", "%f %%", 344 100.0f * global_loss_ratio); 345 } 346 347 void MetricRecorder::PlotObjectiveHistogram(const std::string& title, 348 const std::string& bwe_name, 349 size_t num_flows) const { 350 BWE_TEST_LOGGING_LABEL(7, title, "objective_function", num_flows) 351 BWE_TEST_LOGGING_BAR(7, bwe_name, ObjectiveFunction(), flow_id_); 352 } 353 354 void MetricRecorder::PlotZero() { 355 for (int i = kThroughput; i <= kLoss; ++i) { 356 if (plot_information_[i].plot) { 357 std::stringstream prefix; 358 prefix << "Receiver_" << flow_id_ << "_" + plot_information_[i].prefix; 359 PlotLine(i, prefix.str(), now_ms_, 0); 360 plot_information_[i].last_plot_ms = now_ms_; 361 } 362 } 363 } 364 365 void MetricRecorder::PauseFlow() { 366 PlotZero(); 367 link_share_->PauseFlow(flow_id_); 368 } 369 370 void MetricRecorder::ResumeFlow(int64_t paused_time_ms) { 371 UpdateTimeMs(now_ms_ + paused_time_ms); 372 PlotZero(); 373 link_share_->ResumeFlow(flow_id_); 374 } 375 376 double MetricRecorder::AverageBitrateKbps(int64_t extra_offset_ms) const { 377 int64_t duration_ms = RunDurationMs(extra_offset_ms); 378 if (duration_ms == 0) 379 return 0.0; 380 return static_cast<double>(8 * sum_throughput_bytes_ / duration_ms); 381 } 382 383 int64_t MetricRecorder::RunDurationMs(int64_t extra_offset_ms) const { 384 return now_ms_ - start_computing_metrics_ms_ - extra_offset_ms; 385 } 386 387 double MetricRecorder::DelayStdDev() const { 388 if (num_packets_received_ == 0) { 389 return 0.0; 390 } 391 double mean = static_cast<double>(sum_delays_ms_) / num_packets_received_; 392 double mean2 = 393 static_cast<double>(sum_delays_square_ms2_) / num_packets_received_; 394 return sqrt(mean2 - pow(mean, 2.0)); 395 } 396 397 // Since delay values are bounded in a subset of [0, 5000] ms, 398 // this function's execution time is O(1), independend of num_packets_received_. 399 int64_t MetricRecorder::NthDelayPercentile(int n) const { 400 if (num_packets_received_ == 0) { 401 return 0; 402 } 403 size_t num_packets_remaining = (n * num_packets_received_) / 100; 404 for (auto hist : delay_histogram_ms_) { 405 if (num_packets_remaining <= hist.second) 406 return static_cast<int64_t>(hist.first); 407 num_packets_remaining -= hist.second; 408 } 409 410 assert(false); 411 return -1; 412 } 413 414 // The weighted_estimate_error_ was weighted based on time windows. 415 // This function scales back the result before plotting. 416 double MetricRecorder::Renormalize(double x) const { 417 return (x * num_packets_received_) / now_ms_; 418 } 419 420 inline double U(int64_t x, double alpha) { 421 if (alpha == 1.0) { 422 return log(static_cast<double>(x)); 423 } 424 return pow(static_cast<double>(x), 1.0 - alpha) / (1.0 - alpha); 425 } 426 427 inline double U(size_t x, double alpha) { 428 return U(static_cast<int64_t>(x), alpha); 429 } 430 431 // TODO(magalhaesc): Update ObjectiveFunction. 432 double MetricRecorder::ObjectiveFunction() const { 433 const double kDelta = 0.15; // Delay penalty factor. 434 const double kAlpha = 1.0; 435 const double kBeta = 1.0; 436 437 double throughput_metric = U(sum_throughput_bytes_, kAlpha); 438 double delay_penalty = kDelta * U(sum_delays_ms_, kBeta); 439 440 return throughput_metric - delay_penalty; 441 } 442 443 } // namespace bwe 444 } // namespace testing 445 } // namespace webrtc 446