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

Side by Side Diff: net/nqe/network_quality_estimator.cc

Issue 2107243003: NQE: Record correlation metric in UMA (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed kundaji's comments Created 4 years, 5 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 unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/nqe/network_quality_estimator.h" 5 #include "net/nqe/network_quality_estimator.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <cmath> 8 #include <cmath>
9 #include <limits> 9 #include <limits>
10 #include <utility> 10 #include <utility>
11 #include <vector> 11 #include <vector>
12 12
13 #include "base/bind_helpers.h" 13 #include "base/bind_helpers.h"
14 #include "base/location.h" 14 #include "base/location.h"
15 #include "base/logging.h" 15 #include "base/logging.h"
16 #include "base/metrics/histogram.h" 16 #include "base/metrics/histogram.h"
17 #include "base/metrics/histogram_base.h" 17 #include "base/metrics/histogram_base.h"
18 #include "base/metrics/sparse_histogram.h"
19 #include "base/rand_util.h"
18 #include "base/single_thread_task_runner.h" 20 #include "base/single_thread_task_runner.h"
19 #include "base/strings/string_number_conversions.h" 21 #include "base/strings/string_number_conversions.h"
20 #include "base/threading/thread_task_runner_handle.h" 22 #include "base/threading/thread_task_runner_handle.h"
21 #include "base/time/default_tick_clock.h" 23 #include "base/time/default_tick_clock.h"
22 #include "base/trace_event/trace_event.h" 24 #include "base/trace_event/trace_event.h"
23 #include "build/build_config.h" 25 #include "build/build_config.h"
24 #include "net/base/load_flags.h" 26 #include "net/base/load_flags.h"
25 #include "net/base/load_timing_info.h" 27 #include "net/base/load_timing_info.h"
26 #include "net/base/network_interfaces.h" 28 #include "net/base/network_interfaces.h"
27 #include "net/base/url_util.h" 29 #include "net/base/url_util.h"
30 #include "net/http/http_status_code.h"
28 #include "net/nqe/socket_watcher_factory.h" 31 #include "net/nqe/socket_watcher_factory.h"
29 #include "net/nqe/throughput_analyzer.h" 32 #include "net/nqe/throughput_analyzer.h"
30 #include "net/url_request/url_request.h" 33 #include "net/url_request/url_request.h"
34 #include "net/url_request/url_request_status.h"
31 #include "url/gurl.h" 35 #include "url/gurl.h"
32 36
33 #if defined(OS_ANDROID) 37 #if defined(OS_ANDROID)
34 #include "net/android/network_library.h" 38 #include "net/android/network_library.h"
35 #endif // OS_ANDROID 39 #endif // OS_ANDROID
36 40
37 namespace { 41 namespace {
38 42
39 // Default value of the half life (in seconds) for computing time weighted 43 // Default value of the half life (in seconds) for computing time weighted
40 // percentiles. Every half life, the weight of all observations reduces by 44 // percentiles. Every half life, the weight of all observations reduces by
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
134 138
135 bool GetValueForVariationParam( 139 bool GetValueForVariationParam(
136 const std::map<std::string, std::string>& variation_params, 140 const std::map<std::string, std::string>& variation_params,
137 const std::string& parameter_name, 141 const std::string& parameter_name,
138 int32_t* variations_value) { 142 int32_t* variations_value) {
139 const auto it = variation_params.find(parameter_name); 143 const auto it = variation_params.find(parameter_name);
140 return it != variation_params.end() && 144 return it != variation_params.end() &&
141 base::StringToInt(it->second, variations_value); 145 base::StringToInt(it->second, variations_value);
142 } 146 }
143 147
148 // Returns the variation value for |parameter_name|. If the value is
149 // unavailable, |default_value| is returned.
150 double GetDoubleValueForVariationParamWithDefaultValue(
bengr 2016/07/08 16:49:52 This really should live somewhere in the variation
tbansal1 2016/07/08 17:45:51 I am not sure, but I am planning to add a n_q_e_co
151 const std::map<std::string, std::string>& variation_params,
152 const std::string& parameter_name,
153 double default_value) {
154 const auto it = variation_params.find(parameter_name);
155 if (it == variation_params.end())
156 return default_value;
157
158 double variations_value = default_value;
159 if (!base::StringToDouble(it->second, &variations_value))
160 return default_value;
161 return variations_value;
162 }
163
144 // Returns the algorithm that should be used for computing effective connection 164 // Returns the algorithm that should be used for computing effective connection
145 // type based on field trial params. Returns an empty string if a valid 165 // type based on field trial params. Returns an empty string if a valid
146 // algorithm paramter is not present in the field trial params. 166 // algorithm paramter is not present in the field trial params.
147 std::string GetEffectiveConnectionTypeAlgorithm( 167 std::string GetEffectiveConnectionTypeAlgorithm(
148 const std::map<std::string, std::string>& variation_params) { 168 const std::map<std::string, std::string>& variation_params) {
149 const auto it = variation_params.find("effective_connection_type_algorithm"); 169 const auto it = variation_params.find("effective_connection_type_algorithm");
150 if (it == variation_params.end()) 170 if (it == variation_params.end())
151 return std::string(); 171 return std::string();
152 return it->second; 172 return it->second;
153 } 173 }
(...skipping 27 matching lines...) Expand all
181 static const char* const kSuffixes[] = { 201 static const char* const kSuffixes[] = {
182 "0_20", "20_60", "60_140", "140_300", "300_620", 202 "0_20", "20_60", "60_140", "140_300", "300_620",
183 "620_1260", "1260_2540", "2540_5100", "5100_Infinity"}; 203 "620_1260", "1260_2540", "2540_5100", "5100_Infinity"};
184 for (size_t i = 0; i < arraysize(kSuffixes) - 1; ++i) { 204 for (size_t i = 0; i < arraysize(kSuffixes) - 1; ++i) {
185 if (rtt_milliseconds <= static_cast<float>((20 * (2 << i) - 20))) 205 if (rtt_milliseconds <= static_cast<float>((20 * (2 << i) - 20)))
186 return kSuffixes[i]; 206 return kSuffixes[i];
187 } 207 }
188 return kSuffixes[arraysize(kSuffixes) - 1]; 208 return kSuffixes[arraysize(kSuffixes) - 1];
189 } 209 }
190 210
211 // The least significant kTrimBits of the metric will be discarded. If trimmed
bengr 2016/07/08 16:49:52 If -> If the Also, I don't understand why you tri
tbansal1 2016/07/08 17:45:50 Done.
212 // metric value is greater than what can be fit in kBitsPerMetric bits, then the
213 // largest value that can be represented in kBitsPerMetric bits is returned.
214 const int32_t kTrimBits = 5;
215 const int32_t kBitsPerMetric = 7;
216
217 static_assert(31 >= kBitsPerMetric * 4,
218 "Four metrics would not fit in a 32-bit int with first bit "
219 "reserved for sign");
220
221 // Trims the |metric| by removing the last kTrimBits, and then rounding down
222 // the |metric| such that the |metric| fits in kBitsPerMetric.
223 int32_t FitInKBitsPerMetricBits(int32_t metric) {
bengr 2016/07/08 16:49:52 I don't understand the point of this method. Is th
tbansal1 2016/07/08 17:45:51 Yes, the goal is to record 4 32-bit samples in a s
bengr 2016/07/19 00:32:31 Why do you have to store 4 samples in one UMA samp
tbansal1 2016/07/20 00:28:02 Added comments.
224 // Remove the last kTrimBits. This will allow the metric to fit within
225 // kBitsPerMetric while losing only the least significant bits.
226 metric = metric >> kTrimBits;
227
228 // kLargestValuePossible is the largest value that can be recorded using
229 // kBitsPerMetric.
230 static const int32_t kLargestValuePossible = (1 << kBitsPerMetric) - 1;
Not at Google. Contact bengr 2016/07/01 18:26:26 Suggest moving up with the other constants, instea
tbansal1 2016/07/01 20:11:26 As discussed offline, I would prefer to keep the s
231 if (metric > kLargestValuePossible) {
232 // Fit |metric| in kBitsPerMetric by clamping it down.
233 metric = kLargestValuePossible;
234 }
235 DCHECK_EQ(0, metric >> kBitsPerMetric);
236 return metric;
237 }
238
191 } // namespace 239 } // namespace
192 240
193 namespace net { 241 namespace net {
194 242
195 NetworkQualityEstimator::NetworkQualityEstimator( 243 NetworkQualityEstimator::NetworkQualityEstimator(
196 std::unique_ptr<ExternalEstimateProvider> external_estimates_provider, 244 std::unique_ptr<ExternalEstimateProvider> external_estimates_provider,
197 const std::map<std::string, std::string>& variation_params) 245 const std::map<std::string, std::string>& variation_params)
198 : NetworkQualityEstimator(std::move(external_estimates_provider), 246 : NetworkQualityEstimator(std::move(external_estimates_provider),
199 variation_params, 247 variation_params,
200 false, 248 false,
(...skipping 22 matching lines...) Expand all
223 effective_connection_type_recomputation_interval_( 271 effective_connection_type_recomputation_interval_(
224 base::TimeDelta::FromSeconds(15)), 272 base::TimeDelta::FromSeconds(15)),
225 last_connection_change_(tick_clock_->NowTicks()), 273 last_connection_change_(tick_clock_->NowTicks()),
226 current_network_id_( 274 current_network_id_(
227 NetworkID(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, 275 NetworkID(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN,
228 std::string())), 276 std::string())),
229 downstream_throughput_kbps_observations_(weight_multiplier_per_second_), 277 downstream_throughput_kbps_observations_(weight_multiplier_per_second_),
230 rtt_observations_(weight_multiplier_per_second_), 278 rtt_observations_(weight_multiplier_per_second_),
231 external_estimate_provider_(std::move(external_estimates_provider)), 279 external_estimate_provider_(std::move(external_estimates_provider)),
232 effective_connection_type_(EFFECTIVE_CONNECTION_TYPE_UNKNOWN), 280 effective_connection_type_(EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
281 correlation_logging_probability_(
282 GetDoubleValueForVariationParamWithDefaultValue(
283 variation_params,
284 "correlation_logging_probability",
285 0.0)),
233 weak_ptr_factory_(this) { 286 weak_ptr_factory_(this) {
234 static_assert(kDefaultHalfLifeSeconds > 0, 287 static_assert(kDefaultHalfLifeSeconds > 0,
235 "Default half life duration must be > 0"); 288 "Default half life duration must be > 0");
236 static_assert(kMaximumNetworkQualityCacheSize > 0, 289 static_assert(kMaximumNetworkQualityCacheSize > 0,
237 "Size of the network quality cache must be > 0"); 290 "Size of the network quality cache must be > 0");
238 // This limit should not be increased unless the logic for removing the 291 // This limit should not be increased unless the logic for removing the
239 // oldest cache entry is rewritten to use a doubly-linked-list LRU queue. 292 // oldest cache entry is rewritten to use a doubly-linked-list LRU queue.
240 static_assert(kMaximumNetworkQualityCacheSize <= 10, 293 static_assert(kMaximumNetworkQualityCacheSize <= 10,
241 "Size of the network quality cache must <= 10"); 294 "Size of the network quality cache must <= 10");
242 // None of the algorithms can have an empty name. 295 // None of the algorithms can have an empty name.
243 DCHECK(algorithm_name_to_enum_.end() == 296 DCHECK(algorithm_name_to_enum_.end() ==
244 algorithm_name_to_enum_.find(std::string())); 297 algorithm_name_to_enum_.find(std::string()));
245 298
246 DCHECK_EQ(algorithm_name_to_enum_.size(), 299 DCHECK_EQ(algorithm_name_to_enum_.size(),
247 static_cast<size_t>(EffectiveConnectionTypeAlgorithm:: 300 static_cast<size_t>(EffectiveConnectionTypeAlgorithm::
248 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST)); 301 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST));
249 DCHECK_NE(EffectiveConnectionTypeAlgorithm:: 302 DCHECK_NE(EffectiveConnectionTypeAlgorithm::
250 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST, 303 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST,
251 effective_connection_type_algorithm_); 304 effective_connection_type_algorithm_);
305 DCHECK_LE(0.0, correlation_logging_probability_);
306 DCHECK_GE(1.0, correlation_logging_probability_);
252 307
253 ObtainOperatingParams(variation_params); 308 ObtainOperatingParams(variation_params);
254 ObtainEffectiveConnectionTypeModelParams(variation_params); 309 ObtainEffectiveConnectionTypeModelParams(variation_params);
255 NetworkChangeNotifier::AddConnectionTypeObserver(this); 310 NetworkChangeNotifier::AddConnectionTypeObserver(this);
256 if (external_estimate_provider_) { 311 if (external_estimate_provider_) {
257 RecordExternalEstimateProviderMetrics( 312 RecordExternalEstimateProviderMetrics(
258 EXTERNAL_ESTIMATE_PROVIDER_STATUS_AVAILABLE); 313 EXTERNAL_ESTIMATE_PROVIDER_STATUS_AVAILABLE);
259 external_estimate_provider_->SetUpdatedEstimateDelegate(this); 314 external_estimate_provider_->SetUpdatedEstimateDelegate(this);
260 } else { 315 } else {
261 RecordExternalEstimateProviderMetrics( 316 RecordExternalEstimateProviderMetrics(
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after
483 538
484 LoadTimingInfo load_timing_info; 539 LoadTimingInfo load_timing_info;
485 request.GetLoadTimingInfo(&load_timing_info); 540 request.GetLoadTimingInfo(&load_timing_info);
486 541
487 // If the load timing info is unavailable, it probably means that the request 542 // If the load timing info is unavailable, it probably means that the request
488 // did not go over the network. 543 // did not go over the network.
489 if (load_timing_info.send_start.is_null() || 544 if (load_timing_info.send_start.is_null() ||
490 load_timing_info.receive_headers_end.is_null()) { 545 load_timing_info.receive_headers_end.is_null()) {
491 return; 546 return;
492 } 547 }
548 DCHECK(!request.response_info().was_cached);
493 549
494 // Duration between when the resource was requested and when the response 550 // Duration between when the resource was requested and when the response
495 // headers were received. 551 // headers were received.
496 base::TimeDelta observed_http_rtt = 552 base::TimeDelta observed_http_rtt =
497 load_timing_info.receive_headers_end - load_timing_info.send_start; 553 load_timing_info.receive_headers_end - load_timing_info.send_start;
498 DCHECK_GE(observed_http_rtt, base::TimeDelta()); 554 DCHECK_GE(observed_http_rtt, base::TimeDelta());
499 if (observed_http_rtt < peak_network_quality_.http_rtt()) { 555 if (observed_http_rtt < peak_network_quality_.http_rtt()) {
500 peak_network_quality_ = nqe::internal::NetworkQuality( 556 peak_network_quality_ = nqe::internal::NetworkQuality(
501 observed_http_rtt, peak_network_quality_.transport_rtt(), 557 observed_http_rtt, peak_network_quality_.transport_rtt(),
502 peak_network_quality_.downstream_throughput_kbps()); 558 peak_network_quality_.downstream_throughput_kbps());
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
577 void NetworkQualityEstimator::NotifyRequestCompleted( 633 void NetworkQualityEstimator::NotifyRequestCompleted(
578 const URLRequest& request) { 634 const URLRequest& request) {
579 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"), 635 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"),
580 "NetworkQualityEstimator::NotifyRequestCompleted"); 636 "NetworkQualityEstimator::NotifyRequestCompleted");
581 DCHECK(thread_checker_.CalledOnValidThread()); 637 DCHECK(thread_checker_.CalledOnValidThread());
582 638
583 if (!RequestSchemeIsHTTPOrHTTPS(request)) 639 if (!RequestSchemeIsHTTPOrHTTPS(request))
584 return; 640 return;
585 641
586 throughput_analyzer_->NotifyRequestCompleted(request); 642 throughput_analyzer_->NotifyRequestCompleted(request);
643 RecordCorrelationMetric(request);
644 }
645
646 void NetworkQualityEstimator::RecordCorrelationMetric(
647 const URLRequest& request) const {
648 DCHECK(thread_checker_.CalledOnValidThread());
649
650 // The histogram is recorded with probability
651 // |correlation_logging_probability_| to reduce overhead.
bengr 2016/07/08 16:49:52 What is the overhead?
tbansal1 2016/07/08 17:45:50 Done. Added more comments.
652 if (base::RandDouble() >= correlation_logging_probability_)
653 return;
654
655 if (request.response_info().was_cached ||
656 !request.response_info().network_accessed) {
657 return;
658 }
659
660 LoadTimingInfo load_timing_info;
661 request.GetLoadTimingInfo(&load_timing_info);
662 DCHECK(!load_timing_info.send_start.is_null() &&
663 !load_timing_info.receive_headers_end.is_null());
664
665 // Record UMA only for successful requests that have completed.
666 if (!request.status().is_success() || request.status().is_io_pending())
667 return;
668 if (request.GetResponseCode() != HTTP_OK)
669 return;
670 if (load_timing_info.receive_headers_end < last_main_frame_request_)
671 return;
672
673 const base::TimeTicks now = tick_clock_->NowTicks();
674 // Record UMA only for requests that started recently.
675 if (now - last_main_frame_request_ > base::TimeDelta::FromSeconds(15))
676 return;
677
678 DCHECK_GE(now, load_timing_info.send_start);
679
680 const int32_t transport_rtt_milliseconds =
681 estimated_quality_at_last_main_frame_.transport_rtt() !=
682 nqe::internal::InvalidRTT()
683 ? FitInKBitsPerMetricBits(
684 estimated_quality_at_last_main_frame_.transport_rtt()
685 .InMilliseconds())
686 : 0;
687
688 const int32_t http_rtt_milliseconds =
689 estimated_quality_at_last_main_frame_.http_rtt() !=
690 nqe::internal::InvalidRTT()
691 ? FitInKBitsPerMetricBits(
692 estimated_quality_at_last_main_frame_.http_rtt()
693 .InMilliseconds())
694 : 0;
695
696 const int32_t downstream_throughput_kbps =
697 estimated_quality_at_last_main_frame_.downstream_throughput_kbps() !=
698 nqe::internal::kInvalidThroughput
699 ? FitInKBitsPerMetricBits(estimated_quality_at_last_main_frame_
700 .downstream_throughput_kbps())
701 : 0;
702
703 const int32_t resource_load_time_milliseconds = FitInKBitsPerMetricBits(
704 (now - load_timing_info.send_start).InMilliseconds());
705
706 DCHECK_EQ(0, (transport_rtt_milliseconds | http_rtt_milliseconds |
707 downstream_throughput_kbps | resource_load_time_milliseconds) >>
708 kBitsPerMetric);
709
710 // First 32 - (4* kBitsPerMetric) of the sample are unset. Next
711 // kBitsPerMetric of the sample contain |transport_rtt_milliseconds|.
712 // Next kBitsPerMetric contain |http_rtt_milliseconds|. Next kBitsPerMetric
713 // contain |downstream_throughput_kbps|. And, the last kBitsPerMetric
714 // contain |resource_load_time_milliseconds|.
715 int32_t sample = transport_rtt_milliseconds;
716 sample = (sample << kBitsPerMetric) | http_rtt_milliseconds;
717 sample = (sample << kBitsPerMetric) | downstream_throughput_kbps;
718 sample = (sample << kBitsPerMetric) | resource_load_time_milliseconds;
719
720 UMA_HISTOGRAM_SPARSE_SLOWLY("NQE.Correlation.ResourceLoadTime", sample);
587 } 721 }
588 722
589 void NetworkQualityEstimator::NotifyURLRequestDestroyed( 723 void NetworkQualityEstimator::NotifyURLRequestDestroyed(
590 const URLRequest& request) { 724 const URLRequest& request) {
591 DCHECK(thread_checker_.CalledOnValidThread()); 725 DCHECK(thread_checker_.CalledOnValidThread());
592 726
593 NotifyRequestCompleted(request); 727 NotifyRequestCompleted(request);
594 } 728 }
595 729
596 void NetworkQualityEstimator::AddRTTObserver(RTTObserver* rtt_observer) { 730 void NetworkQualityEstimator::AddRTTObserver(RTTObserver* rtt_observer) {
(...skipping 692 matching lines...) Expand 10 before | Expand all | Expand 10 after
1289 NotifyObserversOfEffectiveConnectionTypeChanged() { 1423 NotifyObserversOfEffectiveConnectionTypeChanged() {
1290 DCHECK(thread_checker_.CalledOnValidThread()); 1424 DCHECK(thread_checker_.CalledOnValidThread());
1291 1425
1292 // TODO(tbansal): Add hysteresis in the notification. 1426 // TODO(tbansal): Add hysteresis in the notification.
1293 FOR_EACH_OBSERVER( 1427 FOR_EACH_OBSERVER(
1294 EffectiveConnectionTypeObserver, effective_connection_type_observer_list_, 1428 EffectiveConnectionTypeObserver, effective_connection_type_observer_list_,
1295 OnEffectiveConnectionTypeChanged(effective_connection_type_)); 1429 OnEffectiveConnectionTypeChanged(effective_connection_type_));
1296 } 1430 }
1297 1431
1298 } // namespace net 1432 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698