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

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

Issue 1144163008: Add in-memory caching of network quality estimates across network changes. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed comments Created 5 years, 6 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/base/network_quality_estimator.h" 5 #include "net/base/network_quality_estimator.h"
6 6
7 #include <limits> 7 #include <limits>
8 #include <string> 8 #include <string>
9 9
10 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h"
11 #include "base/metrics/histogram.h" 12 #include "base/metrics/histogram.h"
13 #include "build/build_config.h"
12 #include "net/base/load_timing_info.h" 14 #include "net/base/load_timing_info.h"
13 #include "net/base/net_util.h" 15 #include "net/base/net_util.h"
14 #include "net/base/network_quality.h" 16 #include "net/base/network_interfaces.h"
15 #include "net/url_request/url_request.h" 17 #include "net/url_request/url_request.h"
16 #include "url/gurl.h" 18 #include "url/gurl.h"
17 19
20 #if defined(OS_ANDROID)
21 #include "net/android/network_library.h"
22 #endif // OS_ANDROID
23
18 namespace { 24 namespace {
19 25
20 // Maximum number of observations that can be held in the ObservationBuffer. 26 // Maximum number of observations that can be held in the ObservationBuffer.
21 const size_t kMaximumObservationsBufferSize = 500; 27 const size_t kMaximumObservationsBufferSize = 500;
22 28
23 } // namespace 29 } // namespace
24 30
25 namespace net { 31 namespace net {
26 32
33 const size_t NetworkQualityEstimator::kMaximumNetworkQualityCacheSize = 10;
34
27 NetworkQualityEstimator::NetworkQualityEstimator() 35 NetworkQualityEstimator::NetworkQualityEstimator()
28 : NetworkQualityEstimator(false, false) { 36 : NetworkQualityEstimator(false, false) {
29 } 37 }
30 38
31 NetworkQualityEstimator::NetworkQualityEstimator( 39 NetworkQualityEstimator::NetworkQualityEstimator(
32 bool allow_local_host_requests_for_tests, 40 bool allow_local_host_requests_for_tests,
33 bool allow_smaller_responses_for_tests) 41 bool allow_smaller_responses_for_tests)
34 : allow_localhost_requests_(allow_local_host_requests_for_tests), 42 : allow_localhost_requests_(allow_local_host_requests_for_tests),
35 allow_small_responses_(allow_smaller_responses_for_tests), 43 allow_small_responses_(allow_smaller_responses_for_tests),
36 last_connection_change_(base::TimeTicks::Now()), 44 last_connection_change_(base::TimeTicks::Now()),
37 current_connection_type_(NetworkChangeNotifier::GetConnectionType()), 45 current_network_id_(
46 NetworkID(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN,
47 std::string())),
38 fastest_rtt_since_last_connection_change_(base::TimeDelta::Max()), 48 fastest_rtt_since_last_connection_change_(base::TimeDelta::Max()),
39 peak_kbps_since_last_connection_change_(0) { 49 peak_kbps_since_last_connection_change_(0) {
40 static_assert(kMinRequestDurationMicroseconds > 0, 50 static_assert(kMinRequestDurationMicroseconds > 0,
41 "Minimum request duration must be > 0"); 51 "Minimum request duration must be > 0");
52 static_assert(kMaximumNetworkQualityCacheSize > 0,
53 "Size of the network quality cache must be > 0");
54
55 // This limit should not be increased unless the logic for removing the
56 // oldest cache entry is rewritten to use a doubly-linked-list LRU queue.
57 static_assert(kMaximumNetworkQualityCacheSize <= 10,
58 "Size of the network quality cache must <= 10");
42 NetworkChangeNotifier::AddConnectionTypeObserver(this); 59 NetworkChangeNotifier::AddConnectionTypeObserver(this);
60 current_network_id_ = GetCurrentNetworkID();
43 } 61 }
44 62
45 NetworkQualityEstimator::~NetworkQualityEstimator() { 63 NetworkQualityEstimator::~NetworkQualityEstimator() {
46 DCHECK(thread_checker_.CalledOnValidThread()); 64 DCHECK(thread_checker_.CalledOnValidThread());
47 NetworkChangeNotifier::RemoveConnectionTypeObserver(this); 65 NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
48 } 66 }
49 67
50 void NetworkQualityEstimator::NotifyDataReceived( 68 void NetworkQualityEstimator::NotifyDataReceived(
51 const URLRequest& request, 69 const URLRequest& request,
52 int64_t cumulative_prefilter_bytes_read, 70 int64_t cumulative_prefilter_bytes_read,
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
129 147
130 kbps_observations_.AddObservation(Observation(kbps, now)); 148 kbps_observations_.AddObservation(Observation(kbps, now));
131 } 149 }
132 } 150 }
133 } 151 }
134 152
135 void NetworkQualityEstimator::OnConnectionTypeChanged( 153 void NetworkQualityEstimator::OnConnectionTypeChanged(
136 NetworkChangeNotifier::ConnectionType type) { 154 NetworkChangeNotifier::ConnectionType type) {
137 DCHECK(thread_checker_.CalledOnValidThread()); 155 DCHECK(thread_checker_.CalledOnValidThread());
138 if (fastest_rtt_since_last_connection_change_ != base::TimeDelta::Max()) { 156 if (fastest_rtt_since_last_connection_change_ != base::TimeDelta::Max()) {
139 switch (current_connection_type_) { 157 switch (current_network_id_.type) {
140 case NetworkChangeNotifier::CONNECTION_UNKNOWN: 158 case NetworkChangeNotifier::CONNECTION_UNKNOWN:
141 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Unknown", 159 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Unknown",
142 fastest_rtt_since_last_connection_change_); 160 fastest_rtt_since_last_connection_change_);
143 break; 161 break;
144 case NetworkChangeNotifier::CONNECTION_ETHERNET: 162 case NetworkChangeNotifier::CONNECTION_ETHERNET:
145 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Ethernet", 163 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Ethernet",
146 fastest_rtt_since_last_connection_change_); 164 fastest_rtt_since_last_connection_change_);
147 break; 165 break;
148 case NetworkChangeNotifier::CONNECTION_WIFI: 166 case NetworkChangeNotifier::CONNECTION_WIFI:
149 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Wifi", 167 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Wifi",
(...skipping 13 matching lines...) Expand all
163 break; 181 break;
164 case NetworkChangeNotifier::CONNECTION_NONE: 182 case NetworkChangeNotifier::CONNECTION_NONE:
165 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.None", 183 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.None",
166 fastest_rtt_since_last_connection_change_); 184 fastest_rtt_since_last_connection_change_);
167 break; 185 break;
168 case NetworkChangeNotifier::CONNECTION_BLUETOOTH: 186 case NetworkChangeNotifier::CONNECTION_BLUETOOTH:
169 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Bluetooth", 187 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Bluetooth",
170 fastest_rtt_since_last_connection_change_); 188 fastest_rtt_since_last_connection_change_);
171 break; 189 break;
172 default: 190 default:
173 NOTREACHED(); 191 NOTREACHED() << "Unexpected connection type = "
192 << current_network_id_.type;
174 break; 193 break;
175 } 194 }
176 } 195 }
177 196
178 if (peak_kbps_since_last_connection_change_) { 197 if (peak_kbps_since_last_connection_change_) {
179 switch (current_connection_type_) { 198 switch (current_network_id_.type) {
180 case NetworkChangeNotifier::CONNECTION_UNKNOWN: 199 case NetworkChangeNotifier::CONNECTION_UNKNOWN:
181 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Unknown", 200 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Unknown",
182 peak_kbps_since_last_connection_change_); 201 peak_kbps_since_last_connection_change_);
183 break; 202 break;
184 case NetworkChangeNotifier::CONNECTION_ETHERNET: 203 case NetworkChangeNotifier::CONNECTION_ETHERNET:
185 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Ethernet", 204 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Ethernet",
186 peak_kbps_since_last_connection_change_); 205 peak_kbps_since_last_connection_change_);
187 break; 206 break;
188 case NetworkChangeNotifier::CONNECTION_WIFI: 207 case NetworkChangeNotifier::CONNECTION_WIFI:
189 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Wifi", 208 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Wifi",
(...skipping 13 matching lines...) Expand all
203 break; 222 break;
204 case NetworkChangeNotifier::CONNECTION_NONE: 223 case NetworkChangeNotifier::CONNECTION_NONE:
205 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.None", 224 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.None",
206 peak_kbps_since_last_connection_change_); 225 peak_kbps_since_last_connection_change_);
207 break; 226 break;
208 case NetworkChangeNotifier::CONNECTION_BLUETOOTH: 227 case NetworkChangeNotifier::CONNECTION_BLUETOOTH:
209 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Bluetooth", 228 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Bluetooth",
210 peak_kbps_since_last_connection_change_); 229 peak_kbps_since_last_connection_change_);
211 break; 230 break;
212 default: 231 default:
213 NOTREACHED(); 232 NOTREACHED() << "Unexpected connection type = "
233 << current_network_id_.type;
214 break; 234 break;
215 } 235 }
216 } 236 }
217 237
238 // Write the estimates of the previous network to the cache.
239 CacheNetworkQualityEstimate();
240
241 // Clear the local state.
218 last_connection_change_ = base::TimeTicks::Now(); 242 last_connection_change_ = base::TimeTicks::Now();
219 peak_kbps_since_last_connection_change_ = 0; 243 peak_kbps_since_last_connection_change_ = 0;
220 fastest_rtt_since_last_connection_change_ = base::TimeDelta::Max(); 244 fastest_rtt_since_last_connection_change_ = base::TimeDelta::Max();
221 kbps_observations_.Clear(); 245 kbps_observations_.Clear();
222 rtt_msec_observations_.Clear(); 246 rtt_msec_observations_.Clear();
223 current_connection_type_ = type; 247
248 // Update the current network ID.
249 current_network_id_ = GetCurrentNetworkID();
250
251 // Read any cached estimates for the new network.
252 ReadCachedNetworkQualityEstimate();
253 }
254
255 size_t NetworkQualityEstimator::GetNetworkQualityCacheSizeForTests() const {
256 DCHECK(thread_checker_.CalledOnValidThread());
257 return cached_network_qualities_.size();
224 } 258 }
225 259
226 NetworkQuality NetworkQualityEstimator::GetPeakEstimate() const { 260 NetworkQuality NetworkQualityEstimator::GetPeakEstimate() const {
227 DCHECK(thread_checker_.CalledOnValidThread()); 261 DCHECK(thread_checker_.CalledOnValidThread());
228 262
229 return NetworkQuality(fastest_rtt_since_last_connection_change_, 263 return NetworkQuality(fastest_rtt_since_last_connection_change_,
230 peak_kbps_since_last_connection_change_); 264 peak_kbps_since_last_connection_change_);
231 } 265 }
232 266
233 size_t NetworkQualityEstimator::GetMaximumObservationBufferSizeForTests() 267 size_t NetworkQualityEstimator::GetMaximumObservationBufferSizeForTests()
234 const { 268 const {
269 DCHECK(thread_checker_.CalledOnValidThread());
235 return kMaximumObservationsBufferSize; 270 return kMaximumObservationsBufferSize;
236 } 271 }
237 272
238 bool NetworkQualityEstimator::VerifyBufferSizeForTests( 273 size_t NetworkQualityEstimator::GetKbpsObservationBufferSizeForTests() const {
239 size_t expected_size) const { 274 DCHECK(thread_checker_.CalledOnValidThread());
240 return kbps_observations_.Size() == expected_size && 275 return kbps_observations_.Size();
241 rtt_msec_observations_.Size() == expected_size; 276 }
277
278 size_t NetworkQualityEstimator::GetRTTObservationBufferSizeForTests() const {
279 DCHECK(thread_checker_.CalledOnValidThread());
280 return rtt_msec_observations_.Size();
242 } 281 }
243 282
244 NetworkQualityEstimator::Observation::Observation(int32_t value, 283 NetworkQualityEstimator::Observation::Observation(int32_t value,
245 base::TimeTicks timestamp) 284 base::TimeTicks timestamp)
246 : value(value), timestamp(timestamp) { 285 : value(value), timestamp(timestamp) {
247 DCHECK_GE(value, 0); 286 DCHECK_GE(value, 0);
248 DCHECK(!timestamp.is_null()); 287 DCHECK(!timestamp.is_null());
249 } 288 }
250 289
251 NetworkQualityEstimator::Observation::~Observation() { 290 NetworkQualityEstimator::Observation::~Observation() {
(...skipping 20 matching lines...) Expand all
272 311
273 size_t NetworkQualityEstimator::ObservationBuffer::Size() const { 312 size_t NetworkQualityEstimator::ObservationBuffer::Size() const {
274 return observations_.size(); 313 return observations_.size();
275 } 314 }
276 315
277 void NetworkQualityEstimator::ObservationBuffer::Clear() { 316 void NetworkQualityEstimator::ObservationBuffer::Clear() {
278 observations_.clear(); 317 observations_.clear();
279 DCHECK(observations_.empty()); 318 DCHECK(observations_.empty());
280 } 319 }
281 320
321 NetworkQualityEstimator::NetworkID
322 NetworkQualityEstimator::GetCurrentNetworkID() const {
323 // TODO(tbansal): crbug.com/498068 Add NetworkQualityEstimatorAndroid class
324 // that overrides this method on the Android platform.
325 NetworkQualityEstimator::NetworkID network_id(
326 NetworkChangeNotifier::GetConnectionType(), std::string());
327
328 DCHECK(thread_checker_.CalledOnValidThread());
329
330 switch (network_id.type) {
331 case NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN:
332 case NetworkChangeNotifier::ConnectionType::CONNECTION_NONE:
333 case NetworkChangeNotifier::ConnectionType::CONNECTION_BLUETOOTH:
334 case NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET:
335 break;
336 case NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI:
337 #if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS)
338 network_id.id = GetWifiSSID();
339 break;
340 #else
341 break;
342 #endif
343 case NetworkChangeNotifier::ConnectionType::CONNECTION_2G:
344 case NetworkChangeNotifier::ConnectionType::CONNECTION_3G:
345 case NetworkChangeNotifier::ConnectionType::CONNECTION_4G:
346 #if defined(OS_ANDROID)
347 network_id.id = android::GetTelephonyNetworkOperator();
348 break;
349 #else
350 break;
351 #endif
352 default:
353 NOTREACHED() << "Unexpected connection type = " << network_id.type;
354 }
355 // Due to race, it is possible that the connection type changed between when
pauljensen 2015/06/18 16:20:26 Remove "Due to race, "
tbansal1 2015/06/18 20:57:40 Done.
356 // GetCurrentConnectionType() was read and when network name was read.
pauljensen 2015/06/18 16:20:25 network->the network
pauljensen 2015/06/18 16:20:26 read->called
tbansal1 2015/06/18 20:57:40 Done.
tbansal1 2015/06/18 20:57:40 Done.
357 // Check if that happened and retry.
pauljensen 2015/06/18 16:20:26 Mention that this isn't perfect but should catch t
tbansal1 2015/06/18 20:57:40 Done.
358 if (network_id.type != NetworkChangeNotifier::GetConnectionType())
359 return GetCurrentNetworkID();
pauljensen 2015/06/18 16:20:26 Please use a loop rather than recursion. Waiting
tbansal1 2015/06/18 20:57:41 Using while(true) now, which I found to be slightl
pauljensen 2015/06/19 03:10:07 SGTM
360 return network_id;
361 }
362
363 bool NetworkQualityEstimator::ReadCachedNetworkQualityEstimate() {
364 DCHECK(thread_checker_.CalledOnValidThread());
365
366 CachedNetworkQualities::iterator it =
367 cached_network_qualities_.find(current_network_id_);
368 if (it != cached_network_qualities_.end()) {
369 // TOOD(tbansal): Populate these values back into the median computing
370 // algorithm.
371 // Add UMA to record how frequently matches happen.
372 // Ensure that the estimates read are non-zero before populating them into
373 // the median computing algorithm.
374 peak_kbps_since_last_connection_change_ =
375 it->second->network_quality().downstream_throughput_kbps();
376 fastest_rtt_since_last_connection_change_ =
377 it->second->network_quality().rtt();
378 return true;
379 }
380 return false;
381 }
382
383 void NetworkQualityEstimator::CacheNetworkQualityEstimate() {
384 DCHECK(thread_checker_.CalledOnValidThread());
385 DCHECK_LE(cached_network_qualities_.size(), kMaximumNetworkQualityCacheSize);
386
387 // TODO(tbansal): The following variables should be initialized using the
388 // median values reported by the NetworkQualityEstimator.
389 int median_kbps = peak_kbps_since_last_connection_change_;
390 base::TimeDelta median_rtt = fastest_rtt_since_last_connection_change_;
391
392 // If this network is already in the cache, overwrite that entry.
393 CachedNetworkQualities::iterator it =
394 cached_network_qualities_.find(current_network_id_);
395 if (it != cached_network_qualities_.end()) {
396 (it->second)->UpdateNetworkQuality(median_kbps, median_rtt);
pauljensen 2015/06/19 03:10:07 This in-place update seems odd. Why not just: c
tbansal1 2015/06/19 23:34:40 For this, I will need to overload = operator for C
pauljensen 2015/06/20 01:30:45 SGTM
pauljensen 2015/06/22 18:39:52 You never implemented this change.
tbansal1 2015/06/22 20:28:25 Took a slightly different approach which allowed m
397 return;
398 }
399
400 if (cached_network_qualities_.size() == kMaximumNetworkQualityCacheSize) {
401 // Remove the oldest entry.
402 CachedNetworkQualities::iterator oldest_entry_iterator =
403 cached_network_qualities_.begin();
404
405 for (CachedNetworkQualities::iterator it =
406 cached_network_qualities_.begin();
407 it != cached_network_qualities_.end(); ++it) {
408 if ((it->second)->OlderThan(*(oldest_entry_iterator->second.get())))
409 oldest_entry_iterator = it;
410 }
411 cached_network_qualities_.erase(oldest_entry_iterator);
412 }
413
414 DCHECK_LT(cached_network_qualities_.size(), kMaximumNetworkQualityCacheSize);
415 cached_network_qualities_.insert(std::make_pair(
416 current_network_id_, make_scoped_ptr(new CachedNetworkQuality(
417 NetworkQuality(median_rtt, median_kbps)))));
418 DCHECK_LE(cached_network_qualities_.size(), kMaximumNetworkQualityCacheSize);
419 }
420
421 NetworkQualityEstimator::CachedNetworkQuality::CachedNetworkQuality(
422 const NetworkQuality& network_quality)
423 : last_update_time_(base::TimeTicks::Now()),
424 network_quality_(network_quality) {
425 }
426
427 NetworkQualityEstimator::CachedNetworkQuality::~CachedNetworkQuality() {
428 }
429
430 bool NetworkQualityEstimator::CachedNetworkQuality::OlderThan(
431 const CachedNetworkQuality& cached_network_quality) const {
432 return last_update_time_ < cached_network_quality.last_update_time_;
433 }
434
435 void NetworkQualityEstimator::CachedNetworkQuality::UpdateNetworkQuality(
436 int32_t median_kbps,
437 const base::TimeDelta& median_rtt) {
438 DCHECK_GE(median_kbps, 0);
439 DCHECK_GE(median_rtt, base::TimeDelta());
440 last_update_time_ = base::TimeTicks::Now();
441
442 network_quality_ = NetworkQuality(median_rtt, median_kbps);
443 }
444
282 } // namespace net 445 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698