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

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 bengr 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(
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 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
201 static const char* const kSuffixes[] = { 221 static const char* const kSuffixes[] = {
202 "0_20", "20_60", "60_140", "140_300", "300_620", 222 "0_20", "20_60", "60_140", "140_300", "300_620",
203 "620_1260", "1260_2540", "2540_5100", "5100_Infinity"}; 223 "620_1260", "1260_2540", "2540_5100", "5100_Infinity"};
204 for (size_t i = 0; i < arraysize(kSuffixes) - 1; ++i) { 224 for (size_t i = 0; i < arraysize(kSuffixes) - 1; ++i) {
205 if (observed_throughput_kbps <= static_cast<float>((20 * (2 << i) - 20))) 225 if (observed_throughput_kbps <= static_cast<float>((20 * (2 << i) - 20)))
206 return kSuffixes[i]; 226 return kSuffixes[i];
207 } 227 }
208 return kSuffixes[arraysize(kSuffixes) - 1]; 228 return kSuffixes[arraysize(kSuffixes) - 1];
209 } 229 }
210 230
231 // The least significant kTrimBits of the metric will be discarded. If the
232 // trimmed metric value is greater than what can be fit in kBitsPerMetric bits,
233 // then the largest value that can be represented in kBitsPerMetric bits is
234 // returned.
235 const int32_t kTrimBits = 5;
236 const int32_t kBitsPerMetric = 7;
237
238 static_assert(31 >= kBitsPerMetric * 4,
239 "Four metrics would not fit in a 32-bit int with first bit "
240 "reserved for sign");
241
242 // Trims the |metric| by removing the last kTrimBits, and then rounding down
243 // the |metric| such that the |metric| fits in kBitsPerMetric.
244 int32_t FitInKBitsPerMetricBits(int32_t metric) {
245 // Remove the last kTrimBits. This will allow the metric to fit within
246 // kBitsPerMetric while losing only the least significant bits.
247 metric = metric >> kTrimBits;
248
249 // kLargestValuePossible is the largest value that can be recorded using
250 // kBitsPerMetric.
251 static const int32_t kLargestValuePossible = (1 << kBitsPerMetric) - 1;
252 if (metric > kLargestValuePossible) {
253 // Fit |metric| in kBitsPerMetric by clamping it down.
254 metric = kLargestValuePossible;
255 }
256 DCHECK_EQ(0, metric >> kBitsPerMetric);
257 return metric;
258 }
259
211 } // namespace 260 } // namespace
212 261
213 namespace net { 262 namespace net {
214 263
215 NetworkQualityEstimator::NetworkQualityEstimator( 264 NetworkQualityEstimator::NetworkQualityEstimator(
216 std::unique_ptr<ExternalEstimateProvider> external_estimates_provider, 265 std::unique_ptr<ExternalEstimateProvider> external_estimates_provider,
217 const std::map<std::string, std::string>& variation_params) 266 const std::map<std::string, std::string>& variation_params)
218 : NetworkQualityEstimator(std::move(external_estimates_provider), 267 : NetworkQualityEstimator(std::move(external_estimates_provider),
219 variation_params, 268 variation_params,
220 false, 269 false,
(...skipping 24 matching lines...) Expand all
245 last_connection_change_(tick_clock_->NowTicks()), 294 last_connection_change_(tick_clock_->NowTicks()),
246 current_network_id_( 295 current_network_id_(
247 NetworkID(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, 296 NetworkID(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN,
248 std::string())), 297 std::string())),
249 downstream_throughput_kbps_observations_(weight_multiplier_per_second_), 298 downstream_throughput_kbps_observations_(weight_multiplier_per_second_),
250 rtt_observations_(weight_multiplier_per_second_), 299 rtt_observations_(weight_multiplier_per_second_),
251 effective_connection_type_at_last_main_frame_( 300 effective_connection_type_at_last_main_frame_(
252 EFFECTIVE_CONNECTION_TYPE_UNKNOWN), 301 EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
253 external_estimate_provider_(std::move(external_estimates_provider)), 302 external_estimate_provider_(std::move(external_estimates_provider)),
254 effective_connection_type_(EFFECTIVE_CONNECTION_TYPE_UNKNOWN), 303 effective_connection_type_(EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
304 correlation_uma_logging_probability_(
305 GetDoubleValueForVariationParamWithDefaultValue(
306 variation_params,
307 "correlation_logging_probability",
308 0.0)),
255 weak_ptr_factory_(this) { 309 weak_ptr_factory_(this) {
256 static_assert(kDefaultHalfLifeSeconds > 0, 310 static_assert(kDefaultHalfLifeSeconds > 0,
257 "Default half life duration must be > 0"); 311 "Default half life duration must be > 0");
258 static_assert(kMaximumNetworkQualityCacheSize > 0, 312 static_assert(kMaximumNetworkQualityCacheSize > 0,
259 "Size of the network quality cache must be > 0"); 313 "Size of the network quality cache must be > 0");
260 // This limit should not be increased unless the logic for removing the 314 // This limit should not be increased unless the logic for removing the
261 // oldest cache entry is rewritten to use a doubly-linked-list LRU queue. 315 // oldest cache entry is rewritten to use a doubly-linked-list LRU queue.
262 static_assert(kMaximumNetworkQualityCacheSize <= 10, 316 static_assert(kMaximumNetworkQualityCacheSize <= 10,
263 "Size of the network quality cache must <= 10"); 317 "Size of the network quality cache must <= 10");
264 // None of the algorithms can have an empty name. 318 // None of the algorithms can have an empty name.
265 DCHECK(algorithm_name_to_enum_.end() == 319 DCHECK(algorithm_name_to_enum_.end() ==
266 algorithm_name_to_enum_.find(std::string())); 320 algorithm_name_to_enum_.find(std::string()));
267 321
268 DCHECK_EQ(algorithm_name_to_enum_.size(), 322 DCHECK_EQ(algorithm_name_to_enum_.size(),
269 static_cast<size_t>(EffectiveConnectionTypeAlgorithm:: 323 static_cast<size_t>(EffectiveConnectionTypeAlgorithm::
270 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST)); 324 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST));
271 DCHECK_NE(EffectiveConnectionTypeAlgorithm:: 325 DCHECK_NE(EffectiveConnectionTypeAlgorithm::
272 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST, 326 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST,
273 effective_connection_type_algorithm_); 327 effective_connection_type_algorithm_);
328 DCHECK_LE(0.0, correlation_uma_logging_probability_);
329 DCHECK_GE(1.0, correlation_uma_logging_probability_);
274 330
275 ObtainOperatingParams(variation_params); 331 ObtainOperatingParams(variation_params);
276 ObtainEffectiveConnectionTypeModelParams(variation_params); 332 ObtainEffectiveConnectionTypeModelParams(variation_params);
277 NetworkChangeNotifier::AddConnectionTypeObserver(this); 333 NetworkChangeNotifier::AddConnectionTypeObserver(this);
278 if (external_estimate_provider_) { 334 if (external_estimate_provider_) {
279 RecordExternalEstimateProviderMetrics( 335 RecordExternalEstimateProviderMetrics(
280 EXTERNAL_ESTIMATE_PROVIDER_STATUS_AVAILABLE); 336 EXTERNAL_ESTIMATE_PROVIDER_STATUS_AVAILABLE);
281 external_estimate_provider_->SetUpdatedEstimateDelegate(this); 337 external_estimate_provider_->SetUpdatedEstimateDelegate(this);
282 } else { 338 } else {
283 RecordExternalEstimateProviderMetrics( 339 RecordExternalEstimateProviderMetrics(
(...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after
507 563
508 LoadTimingInfo load_timing_info; 564 LoadTimingInfo load_timing_info;
509 request.GetLoadTimingInfo(&load_timing_info); 565 request.GetLoadTimingInfo(&load_timing_info);
510 566
511 // If the load timing info is unavailable, it probably means that the request 567 // If the load timing info is unavailable, it probably means that the request
512 // did not go over the network. 568 // did not go over the network.
513 if (load_timing_info.send_start.is_null() || 569 if (load_timing_info.send_start.is_null() ||
514 load_timing_info.receive_headers_end.is_null()) { 570 load_timing_info.receive_headers_end.is_null()) {
515 return; 571 return;
516 } 572 }
573 DCHECK(!request.response_info().was_cached);
517 574
518 // Duration between when the resource was requested and when the response 575 // Duration between when the resource was requested and when the response
519 // headers were received. 576 // headers were received.
520 base::TimeDelta observed_http_rtt = 577 base::TimeDelta observed_http_rtt =
521 load_timing_info.receive_headers_end - load_timing_info.send_start; 578 load_timing_info.receive_headers_end - load_timing_info.send_start;
522 DCHECK_GE(observed_http_rtt, base::TimeDelta()); 579 DCHECK_GE(observed_http_rtt, base::TimeDelta());
523 if (observed_http_rtt < peak_network_quality_.http_rtt()) { 580 if (observed_http_rtt < peak_network_quality_.http_rtt()) {
524 peak_network_quality_ = nqe::internal::NetworkQuality( 581 peak_network_quality_ = nqe::internal::NetworkQuality(
525 observed_http_rtt, peak_network_quality_.transport_rtt(), 582 observed_http_rtt, peak_network_quality_.transport_rtt(),
526 peak_network_quality_.downstream_throughput_kbps()); 583 peak_network_quality_.downstream_throughput_kbps());
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
648 void NetworkQualityEstimator::NotifyRequestCompleted( 705 void NetworkQualityEstimator::NotifyRequestCompleted(
649 const URLRequest& request) { 706 const URLRequest& request) {
650 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"), 707 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"),
651 "NetworkQualityEstimator::NotifyRequestCompleted"); 708 "NetworkQualityEstimator::NotifyRequestCompleted");
652 DCHECK(thread_checker_.CalledOnValidThread()); 709 DCHECK(thread_checker_.CalledOnValidThread());
653 710
654 if (!RequestSchemeIsHTTPOrHTTPS(request)) 711 if (!RequestSchemeIsHTTPOrHTTPS(request))
655 return; 712 return;
656 713
657 throughput_analyzer_->NotifyRequestCompleted(request); 714 throughput_analyzer_->NotifyRequestCompleted(request);
715 RecordCorrelationMetric(request);
716 }
717
718 void NetworkQualityEstimator::RecordCorrelationMetric(
719 const URLRequest& request) const {
720 DCHECK(thread_checker_.CalledOnValidThread());
721
722 // The histogram is recorded with probability
723 // |correlation_uma_logging_probability_| to reduce overhead involved with
724 // sparse histograms. Also, recording the correlation on each request is
725 // unnecessary.
726 if (RandDouble() >= correlation_uma_logging_probability_)
727 return;
728
729 if (request.response_info().was_cached ||
730 !request.response_info().network_accessed) {
731 return;
732 }
733
734 LoadTimingInfo load_timing_info;
735 request.GetLoadTimingInfo(&load_timing_info);
736 DCHECK(!load_timing_info.send_start.is_null() &&
737 !load_timing_info.receive_headers_end.is_null());
738
739 // Record UMA only for successful requests that have completed.
740 if (!request.status().is_success() || request.status().is_io_pending())
741 return;
742 if (request.GetResponseCode() != HTTP_OK)
743 return;
744 if (load_timing_info.receive_headers_end < last_main_frame_request_)
745 return;
746
747 const base::TimeTicks now = tick_clock_->NowTicks();
748 // Record UMA only for requests that started recently.
749 if (now - last_main_frame_request_ > base::TimeDelta::FromSeconds(15))
750 return;
751
752 DCHECK_GE(now, load_timing_info.send_start);
753
754 const int32_t transport_rtt_milliseconds =
755 estimated_quality_at_last_main_frame_.transport_rtt() !=
756 nqe::internal::InvalidRTT()
757 ? FitInKBitsPerMetricBits(
758 estimated_quality_at_last_main_frame_.transport_rtt()
759 .InMilliseconds())
760 : 0;
761
762 const int32_t http_rtt_milliseconds =
763 estimated_quality_at_last_main_frame_.http_rtt() !=
764 nqe::internal::InvalidRTT()
765 ? FitInKBitsPerMetricBits(
766 estimated_quality_at_last_main_frame_.http_rtt()
767 .InMilliseconds())
768 : 0;
769
770 const int32_t downstream_throughput_kbps =
771 estimated_quality_at_last_main_frame_.downstream_throughput_kbps() !=
772 nqe::internal::kInvalidThroughput
773 ? FitInKBitsPerMetricBits(estimated_quality_at_last_main_frame_
774 .downstream_throughput_kbps())
775 : 0;
776
777 const int32_t resource_load_time_milliseconds = FitInKBitsPerMetricBits(
778 (now - load_timing_info.send_start).InMilliseconds());
779
780 DCHECK_EQ(0, (transport_rtt_milliseconds | http_rtt_milliseconds |
781 downstream_throughput_kbps | resource_load_time_milliseconds) >>
782 kBitsPerMetric);
783
784 // First 32 - (4* kBitsPerMetric) of the sample are unset. Next
785 // kBitsPerMetric of the sample contain |transport_rtt_milliseconds|.
786 // Next kBitsPerMetric contain |http_rtt_milliseconds|. Next kBitsPerMetric
787 // contain |downstream_throughput_kbps|. And, the last kBitsPerMetric
788 // contain |resource_load_time_milliseconds|.
789 int32_t sample = transport_rtt_milliseconds;
790 sample = (sample << kBitsPerMetric) | http_rtt_milliseconds;
791 sample = (sample << kBitsPerMetric) | downstream_throughput_kbps;
792 sample = (sample << kBitsPerMetric) | resource_load_time_milliseconds;
793
794 UMA_HISTOGRAM_SPARSE_SLOWLY("NQE.Correlation.ResourceLoadTime", sample);
658 } 795 }
659 796
660 void NetworkQualityEstimator::NotifyURLRequestDestroyed( 797 void NetworkQualityEstimator::NotifyURLRequestDestroyed(
661 const URLRequest& request) { 798 const URLRequest& request) {
662 DCHECK(thread_checker_.CalledOnValidThread()); 799 DCHECK(thread_checker_.CalledOnValidThread());
663 800
664 NotifyRequestCompleted(request); 801 NotifyRequestCompleted(request);
665 } 802 }
666 803
667 void NetworkQualityEstimator::AddRTTObserver(RTTObserver* rtt_observer) { 804 void NetworkQualityEstimator::AddRTTObserver(RTTObserver* rtt_observer) {
(...skipping 550 matching lines...) Expand 10 before | Expand all | Expand 10 after
1218 NOTREACHED(); 1355 NOTREACHED();
1219 return EFFECTIVE_CONNECTION_TYPE_UNKNOWN; 1356 return EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
1220 } 1357 }
1221 1358
1222 void NetworkQualityEstimator::SetTickClockForTesting( 1359 void NetworkQualityEstimator::SetTickClockForTesting(
1223 std::unique_ptr<base::TickClock> tick_clock) { 1360 std::unique_ptr<base::TickClock> tick_clock) {
1224 DCHECK(thread_checker_.CalledOnValidThread()); 1361 DCHECK(thread_checker_.CalledOnValidThread());
1225 tick_clock_ = std::move(tick_clock); 1362 tick_clock_ = std::move(tick_clock);
1226 } 1363 }
1227 1364
1365 double NetworkQualityEstimator::RandDouble() const {
1366 return base::RandDouble();
1367 }
1368
1228 void NetworkQualityEstimator::CacheNetworkQualityEstimate() { 1369 void NetworkQualityEstimator::CacheNetworkQualityEstimate() {
1229 DCHECK(thread_checker_.CalledOnValidThread()); 1370 DCHECK(thread_checker_.CalledOnValidThread());
1230 DCHECK_LE(cached_network_qualities_.size(), 1371 DCHECK_LE(cached_network_qualities_.size(),
1231 static_cast<size_t>(kMaximumNetworkQualityCacheSize)); 1372 static_cast<size_t>(kMaximumNetworkQualityCacheSize));
1232 1373
1233 // If the network name is unavailable, caching should not be performed. 1374 // If the network name is unavailable, caching should not be performed.
1234 if (current_network_id_.id.empty()) 1375 if (current_network_id_.id.empty())
1235 return; 1376 return;
1236 1377
1237 base::TimeDelta http_rtt = nqe::internal::InvalidRTT(); 1378 base::TimeDelta http_rtt = nqe::internal::InvalidRTT();
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
1360 NotifyObserversOfEffectiveConnectionTypeChanged() { 1501 NotifyObserversOfEffectiveConnectionTypeChanged() {
1361 DCHECK(thread_checker_.CalledOnValidThread()); 1502 DCHECK(thread_checker_.CalledOnValidThread());
1362 1503
1363 // TODO(tbansal): Add hysteresis in the notification. 1504 // TODO(tbansal): Add hysteresis in the notification.
1364 FOR_EACH_OBSERVER( 1505 FOR_EACH_OBSERVER(
1365 EffectiveConnectionTypeObserver, effective_connection_type_observer_list_, 1506 EffectiveConnectionTypeObserver, effective_connection_type_observer_list_,
1366 OnEffectiveConnectionTypeChanged(effective_connection_type_)); 1507 OnEffectiveConnectionTypeChanged(effective_connection_type_));
1367 } 1508 }
1368 1509
1369 } // namespace net 1510 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698