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

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: 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
« no previous file with comments | « net/nqe/network_quality_estimator.h ('k') | net/nqe/network_quality_estimator_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 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. Next, it
212 // will be rounded down so it fits within kBitsPerMetric bits. This implies
213 // that if the metric's value is more than 2^(kTrimBits + kBitsPerMetric),
214 // then it will be rounded down.
Not at Google. Contact bengr 2016/06/30 17:49:44 This is confusing. Can you please rephrase it. How
tbansal1 2016/07/01 00:16:00 Done.
215 const int32_t kTrimBits = 5;
216 const int32_t kBitsPerMetric = 7;
Not at Google. Contact bengr 2016/06/30 17:49:44 Add: static_assert(31 > kBitsPerMetric * 4, "4 met
tbansal1 2016/07/01 00:16:00 Done.
217
218 // Trims the |metric| by removing the last kTrimBits, and then rounding down
219 // the |metric| such that the |metric| fits in kBitsPerMetric.
220 int32_t TrimAndRoundDown(int32_t metric) {
Not at Google. Contact bengr 2016/06/30 17:49:44 Rename method to FitInKBitsPerMetricBits?
tbansal1 2016/07/01 00:16:00 Done.
221 // Remove the last kTrimBits. This will allow the metric to fit within
222 // kBitsPerMetric while losing only the least significant bits.
223 metric = metric >> kTrimBits;
224 if (metric >= (1 << kBitsPerMetric)) {
225 // Fit |metric| in kBitsPerMetric by rounding it down.
226 metric = (1 << kBitsPerMetric) - 1;
Not at Google. Contact bengr 2016/06/30 17:49:44 Possible Refactor: int32_t largest_value_using_kBi
tbansal1 2016/07/01 00:16:01 Done.
227 }
228 DCHECK_EQ(0, metric >> kBitsPerMetric);
229 return metric;
230 }
231
191 } // namespace 232 } // namespace
192 233
193 namespace net { 234 namespace net {
194 235
195 NetworkQualityEstimator::NetworkQualityEstimator( 236 NetworkQualityEstimator::NetworkQualityEstimator(
196 std::unique_ptr<ExternalEstimateProvider> external_estimates_provider, 237 std::unique_ptr<ExternalEstimateProvider> external_estimates_provider,
197 const std::map<std::string, std::string>& variation_params) 238 const std::map<std::string, std::string>& variation_params)
198 : NetworkQualityEstimator(std::move(external_estimates_provider), 239 : NetworkQualityEstimator(std::move(external_estimates_provider),
199 variation_params, 240 variation_params,
200 false, 241 false,
(...skipping 22 matching lines...) Expand all
223 effective_connection_type_recomputation_interval_( 264 effective_connection_type_recomputation_interval_(
224 base::TimeDelta::FromSeconds(15)), 265 base::TimeDelta::FromSeconds(15)),
225 last_connection_change_(tick_clock_->NowTicks()), 266 last_connection_change_(tick_clock_->NowTicks()),
226 current_network_id_( 267 current_network_id_(
227 NetworkID(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, 268 NetworkID(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN,
228 std::string())), 269 std::string())),
229 downstream_throughput_kbps_observations_(weight_multiplier_per_second_), 270 downstream_throughput_kbps_observations_(weight_multiplier_per_second_),
230 rtt_observations_(weight_multiplier_per_second_), 271 rtt_observations_(weight_multiplier_per_second_),
231 external_estimate_provider_(std::move(external_estimates_provider)), 272 external_estimate_provider_(std::move(external_estimates_provider)),
232 effective_connection_type_(EFFECTIVE_CONNECTION_TYPE_UNKNOWN), 273 effective_connection_type_(EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
274 correlation_logging_probability_(
275 GetDoubleValueForVariationParamWithDefaultValue(
276 variation_params,
277 "correlation_logging_probability",
278 0.0)),
233 weak_ptr_factory_(this) { 279 weak_ptr_factory_(this) {
234 static_assert(kDefaultHalfLifeSeconds > 0, 280 static_assert(kDefaultHalfLifeSeconds > 0,
235 "Default half life duration must be > 0"); 281 "Default half life duration must be > 0");
236 static_assert(kMaximumNetworkQualityCacheSize > 0, 282 static_assert(kMaximumNetworkQualityCacheSize > 0,
237 "Size of the network quality cache must be > 0"); 283 "Size of the network quality cache must be > 0");
238 // This limit should not be increased unless the logic for removing the 284 // 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. 285 // oldest cache entry is rewritten to use a doubly-linked-list LRU queue.
240 static_assert(kMaximumNetworkQualityCacheSize <= 10, 286 static_assert(kMaximumNetworkQualityCacheSize <= 10,
241 "Size of the network quality cache must <= 10"); 287 "Size of the network quality cache must <= 10");
242 // None of the algorithms can have an empty name. 288 // None of the algorithms can have an empty name.
243 DCHECK(algorithm_name_to_enum_.end() == 289 DCHECK(algorithm_name_to_enum_.end() ==
244 algorithm_name_to_enum_.find(std::string())); 290 algorithm_name_to_enum_.find(std::string()));
245 291
246 DCHECK_EQ(algorithm_name_to_enum_.size(), 292 DCHECK_EQ(algorithm_name_to_enum_.size(),
247 static_cast<size_t>(EffectiveConnectionTypeAlgorithm:: 293 static_cast<size_t>(EffectiveConnectionTypeAlgorithm::
248 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST)); 294 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST));
249 DCHECK_NE(EffectiveConnectionTypeAlgorithm:: 295 DCHECK_NE(EffectiveConnectionTypeAlgorithm::
250 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST, 296 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST,
251 effective_connection_type_algorithm_); 297 effective_connection_type_algorithm_);
298 DCHECK_LE(0.0, correlation_logging_probability_);
299 DCHECK_GE(1.0, correlation_logging_probability_);
252 300
253 ObtainOperatingParams(variation_params); 301 ObtainOperatingParams(variation_params);
254 ObtainEffectiveConnectionTypeModelParams(variation_params); 302 ObtainEffectiveConnectionTypeModelParams(variation_params);
255 NetworkChangeNotifier::AddConnectionTypeObserver(this); 303 NetworkChangeNotifier::AddConnectionTypeObserver(this);
256 if (external_estimate_provider_) { 304 if (external_estimate_provider_) {
257 RecordExternalEstimateProviderMetrics( 305 RecordExternalEstimateProviderMetrics(
258 EXTERNAL_ESTIMATE_PROVIDER_STATUS_AVAILABLE); 306 EXTERNAL_ESTIMATE_PROVIDER_STATUS_AVAILABLE);
259 external_estimate_provider_->SetUpdatedEstimateDelegate(this); 307 external_estimate_provider_->SetUpdatedEstimateDelegate(this);
260 } else { 308 } else {
261 RecordExternalEstimateProviderMetrics( 309 RecordExternalEstimateProviderMetrics(
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after
483 531
484 LoadTimingInfo load_timing_info; 532 LoadTimingInfo load_timing_info;
485 request.GetLoadTimingInfo(&load_timing_info); 533 request.GetLoadTimingInfo(&load_timing_info);
486 534
487 // If the load timing info is unavailable, it probably means that the request 535 // If the load timing info is unavailable, it probably means that the request
488 // did not go over the network. 536 // did not go over the network.
489 if (load_timing_info.send_start.is_null() || 537 if (load_timing_info.send_start.is_null() ||
490 load_timing_info.receive_headers_end.is_null()) { 538 load_timing_info.receive_headers_end.is_null()) {
491 return; 539 return;
492 } 540 }
541 DCHECK(!request.response_info().was_cached);
493 542
494 // Duration between when the resource was requested and when the response 543 // Duration between when the resource was requested and when the response
495 // headers were received. 544 // headers were received.
496 base::TimeDelta observed_http_rtt = 545 base::TimeDelta observed_http_rtt =
497 load_timing_info.receive_headers_end - load_timing_info.send_start; 546 load_timing_info.receive_headers_end - load_timing_info.send_start;
498 DCHECK_GE(observed_http_rtt, base::TimeDelta()); 547 DCHECK_GE(observed_http_rtt, base::TimeDelta());
499 if (observed_http_rtt < peak_network_quality_.http_rtt()) { 548 if (observed_http_rtt < peak_network_quality_.http_rtt()) {
500 peak_network_quality_ = nqe::internal::NetworkQuality( 549 peak_network_quality_ = nqe::internal::NetworkQuality(
501 observed_http_rtt, peak_network_quality_.transport_rtt(), 550 observed_http_rtt, peak_network_quality_.transport_rtt(),
502 peak_network_quality_.downstream_throughput_kbps()); 551 peak_network_quality_.downstream_throughput_kbps());
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
577 void NetworkQualityEstimator::NotifyRequestCompleted( 626 void NetworkQualityEstimator::NotifyRequestCompleted(
578 const URLRequest& request) { 627 const URLRequest& request) {
579 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"), 628 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"),
580 "NetworkQualityEstimator::NotifyRequestCompleted"); 629 "NetworkQualityEstimator::NotifyRequestCompleted");
581 DCHECK(thread_checker_.CalledOnValidThread()); 630 DCHECK(thread_checker_.CalledOnValidThread());
582 631
583 if (!RequestSchemeIsHTTPOrHTTPS(request)) 632 if (!RequestSchemeIsHTTPOrHTTPS(request))
584 return; 633 return;
585 634
586 throughput_analyzer_->NotifyRequestCompleted(request); 635 throughput_analyzer_->NotifyRequestCompleted(request);
636 RecordCorrelationMetric(request);
637 }
638
639 void NetworkQualityEstimator::RecordCorrelationMetric(
640 const URLRequest& request) const {
641 DCHECK(thread_checker_.CalledOnValidThread());
642
643 // The histogram is recorded with probability
644 // |correlation_logging_probability_| to reduce overhead.
645 if (base::RandDouble() >= correlation_logging_probability_)
Not at Google. Contact bengr 2016/06/30 17:49:44 Shouldn't this be base::RandDouble() <= correlatio
tbansal1 2016/07/01 00:16:00 No. Say correlation_logging_probability_ is 1.0. T
Not at Google. Contact bengr 2016/07/01 18:26:26 Misread as condition to execute below. You are rig
646 return;
647
648 if (request.response_info().was_cached)
649 return;
650 LoadTimingInfo load_timing_info;
651 request.GetLoadTimingInfo(&load_timing_info);
652
653 // If the load timing info is unavailable, it probably means that the request
654 // did not go over the network.
Not at Google. Contact bengr 2016/06/30 17:49:44 Could this be replaced with request->response_info
tbansal1 2016/07/01 00:16:01 Done.
655 if (load_timing_info.send_start.is_null() ||
656 load_timing_info.receive_headers_end.is_null()) {
657 return;
658 }
659
660 // Record UMA only for successful requests that have completed.
661 if (!request.status().is_success() || request.status().is_io_pending())
662 return;
663 if (request.GetResponseCode() != HTTP_OK)
664 return;
665 if (load_timing_info.receive_headers_end < last_main_frame_request_)
666 return;
Not at Google. Contact bengr 2016/06/30 17:49:44 Nit: Add new line.
tbansal1 2016/07/01 00:16:00 Done.
667 const base::TimeTicks now = tick_clock_->NowTicks();
668 // Record UMA only for requests that started recently.
669 if (now - last_main_frame_request_ > base::TimeDelta::FromSeconds(15))
Not at Google. Contact bengr 2016/06/30 17:49:44 Why ignore older requests?
tbansal1 2016/07/01 00:16:00 It is possible that network quality was recorded a
Not at Google. Contact bengr 2016/07/01 18:26:26 Acknowledged.
670 return;
671
672 DCHECK_GE(now, load_timing_info.send_start);
Not at Google. Contact bengr 2016/06/30 17:49:44 Not sure this is necessary.
tbansal1 2016/07/01 00:16:01 It checks that we are not adding negative values t
Not at Google. Contact bengr 2016/07/01 18:26:26 Acknowledged.
673
674 // Histogram samples are 32-bit in size with first bit reserved for sign.
675 // Four metrics will be recorded per sample.
676 DCHECK_GE(31, kBitsPerMetric * 4);
Not at Google. Contact bengr 2016/06/30 17:49:44 Prefer replacing with static assert above where kB
tbansal1 2016/07/01 00:16:01 Done.
677
678 const int32_t transport_rtt_milliseconds =
Not at Google. Contact bengr 2016/06/30 17:49:44 Does it help to extract estimated_quality_at_last_
tbansal1 2016/07/01 00:16:00 I am not sure. I wanted to declare it as a const v
679 estimated_quality_at_last_main_frame_.transport_rtt() !=
680 nqe::internal::InvalidRTT()
681 ? TrimAndRoundDown(
682 estimated_quality_at_last_main_frame_.transport_rtt()
683 .InMilliseconds())
684 : 0;
685
686 const int32_t http_rtt_milliseconds =
687 estimated_quality_at_last_main_frame_.http_rtt() !=
688 nqe::internal::InvalidRTT()
689 ? TrimAndRoundDown(estimated_quality_at_last_main_frame_.http_rtt()
690 .InMilliseconds())
691 : 0;
692
693 const int32_t downstream_throughput_kbps =
694 estimated_quality_at_last_main_frame_.downstream_throughput_kbps() !=
695 nqe::internal::kInvalidThroughput
696 ? TrimAndRoundDown(estimated_quality_at_last_main_frame_
697 .downstream_throughput_kbps())
698 : 0;
699
700 const int32_t resource_load_time_milliseconds =
701 TrimAndRoundDown((now - load_timing_info.send_start).InMilliseconds());
702
703 // First 32 - (4* kBitsPerMetric) of the sample are unset. Next
704 // kBitsPerMetric of the sample contain |transport_rtt_milliseconds|.
705 // Next kBitsPerMetric contain |http_rtt_milliseconds|. Next kBitsPerMetric
706 // contain |downstream_throughput_kbps|. And, the last kBitsPerMetric
707 // contain |resource_load_time_milliseconds|.
708 int32_t sample = transport_rtt_milliseconds;
Not at Google. Contact bengr 2016/06/30 17:49:44 Suggest adding DCHECK_EQ(0, transport_rtt_millisec
tbansal1 2016/07/01 00:16:00 Done.
709 sample = (sample << kBitsPerMetric) + http_rtt_milliseconds;
Not at Google. Contact bengr 2016/06/30 17:49:44 Minor: | seems prefereable to +.
tbansal1 2016/07/01 00:16:00 Done.
710 sample = (sample << kBitsPerMetric) + downstream_throughput_kbps;
711 sample = (sample << kBitsPerMetric) + resource_load_time_milliseconds;
712
713 UMA_HISTOGRAM_SPARSE_SLOWLY("NQE.Correlation.ResourceLoadTime", sample);
587 } 714 }
588 715
589 void NetworkQualityEstimator::NotifyURLRequestDestroyed( 716 void NetworkQualityEstimator::NotifyURLRequestDestroyed(
590 const URLRequest& request) { 717 const URLRequest& request) {
591 DCHECK(thread_checker_.CalledOnValidThread()); 718 DCHECK(thread_checker_.CalledOnValidThread());
592 719
593 NotifyRequestCompleted(request); 720 NotifyRequestCompleted(request);
594 } 721 }
595 722
596 void NetworkQualityEstimator::AddRTTObserver(RTTObserver* rtt_observer) { 723 void NetworkQualityEstimator::AddRTTObserver(RTTObserver* rtt_observer) {
(...skipping 692 matching lines...) Expand 10 before | Expand all | Expand 10 after
1289 NotifyObserversOfEffectiveConnectionTypeChanged() { 1416 NotifyObserversOfEffectiveConnectionTypeChanged() {
1290 DCHECK(thread_checker_.CalledOnValidThread()); 1417 DCHECK(thread_checker_.CalledOnValidThread());
1291 1418
1292 // TODO(tbansal): Add hysteresis in the notification. 1419 // TODO(tbansal): Add hysteresis in the notification.
1293 FOR_EACH_OBSERVER( 1420 FOR_EACH_OBSERVER(
1294 EffectiveConnectionTypeObserver, effective_connection_type_observer_list_, 1421 EffectiveConnectionTypeObserver, effective_connection_type_observer_list_,
1295 OnEffectiveConnectionTypeChanged(effective_connection_type_)); 1422 OnEffectiveConnectionTypeChanged(effective_connection_type_));
1296 } 1423 }
1297 1424
1298 } // namespace net 1425 } // namespace net
OLDNEW
« no previous file with comments | « net/nqe/network_quality_estimator.h ('k') | net/nqe/network_quality_estimator_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698