OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |