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

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
18 namespace { 20 #if defined(OS_ANDROID)
19 21 #include "net/android/network_library.h"
20 // Maximum number of observations that can be held in the ObservationBuffer. 22 #endif // OS_ANDROID
21 const size_t kMaximumObservationsBufferSize = 500;
22
23 } // namespace
24 23
25 namespace net { 24 namespace net {
26 25
26 const size_t NetworkQualityEstimator::kMaximumNetworkQualityCacheSize;
27
28 const size_t NetworkQualityEstimator::kMaximumObservationsBufferSize;
29
27 NetworkQualityEstimator::NetworkQualityEstimator() 30 NetworkQualityEstimator::NetworkQualityEstimator()
28 : NetworkQualityEstimator(false, false) { 31 : NetworkQualityEstimator(false, false) {
29 } 32 }
30 33
31 NetworkQualityEstimator::NetworkQualityEstimator( 34 NetworkQualityEstimator::NetworkQualityEstimator(
32 bool allow_local_host_requests_for_tests, 35 bool allow_local_host_requests_for_tests,
33 bool allow_smaller_responses_for_tests) 36 bool allow_smaller_responses_for_tests)
34 : allow_localhost_requests_(allow_local_host_requests_for_tests), 37 : allow_localhost_requests_(allow_local_host_requests_for_tests),
35 allow_small_responses_(allow_smaller_responses_for_tests), 38 allow_small_responses_(allow_smaller_responses_for_tests),
36 last_connection_change_(base::TimeTicks::Now()), 39 last_connection_change_(base::TimeTicks::Now()),
37 current_connection_type_(NetworkChangeNotifier::GetConnectionType()), 40 current_network_id_(
41 NetworkID(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN,
42 std::string())),
38 fastest_rtt_since_last_connection_change_(base::TimeDelta::Max()), 43 fastest_rtt_since_last_connection_change_(base::TimeDelta::Max()),
39 peak_kbps_since_last_connection_change_(0) { 44 peak_kbps_since_last_connection_change_(0) {
40 static_assert(kMinRequestDurationMicroseconds > 0, 45 static_assert(kMinRequestDurationMicroseconds > 0,
41 "Minimum request duration must be > 0"); 46 "Minimum request duration must be > 0");
47 static_assert(kMaximumNetworkQualityCacheSize > 0,
48 "Size of the network quality cache must be > 0");
49
50 // This limit should not be increased unless the logic for removing the
51 // oldest cache entry is rewritten to use a doubly-linked-list LRU queue.
52 static_assert(kMaximumNetworkQualityCacheSize <= 10,
53 "Size of the network quality cache must <= 10");
42 NetworkChangeNotifier::AddConnectionTypeObserver(this); 54 NetworkChangeNotifier::AddConnectionTypeObserver(this);
55 current_network_id_ = GetCurrentNetworkID();
43 } 56 }
44 57
45 NetworkQualityEstimator::~NetworkQualityEstimator() { 58 NetworkQualityEstimator::~NetworkQualityEstimator() {
46 DCHECK(thread_checker_.CalledOnValidThread()); 59 DCHECK(thread_checker_.CalledOnValidThread());
47 NetworkChangeNotifier::RemoveConnectionTypeObserver(this); 60 NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
48 } 61 }
49 62
50 void NetworkQualityEstimator::NotifyDataReceived( 63 void NetworkQualityEstimator::NotifyDataReceived(
51 const URLRequest& request, 64 const URLRequest& request,
52 int64_t cumulative_prefilter_bytes_read, 65 int64_t cumulative_prefilter_bytes_read,
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
129 142
130 kbps_observations_.AddObservation(Observation(kbps, now)); 143 kbps_observations_.AddObservation(Observation(kbps, now));
131 } 144 }
132 } 145 }
133 } 146 }
134 147
135 void NetworkQualityEstimator::OnConnectionTypeChanged( 148 void NetworkQualityEstimator::OnConnectionTypeChanged(
136 NetworkChangeNotifier::ConnectionType type) { 149 NetworkChangeNotifier::ConnectionType type) {
137 DCHECK(thread_checker_.CalledOnValidThread()); 150 DCHECK(thread_checker_.CalledOnValidThread());
138 if (fastest_rtt_since_last_connection_change_ != base::TimeDelta::Max()) { 151 if (fastest_rtt_since_last_connection_change_ != base::TimeDelta::Max()) {
139 switch (current_connection_type_) { 152 switch (current_network_id_.type) {
140 case NetworkChangeNotifier::CONNECTION_UNKNOWN: 153 case NetworkChangeNotifier::CONNECTION_UNKNOWN:
141 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Unknown", 154 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Unknown",
142 fastest_rtt_since_last_connection_change_); 155 fastest_rtt_since_last_connection_change_);
143 break; 156 break;
144 case NetworkChangeNotifier::CONNECTION_ETHERNET: 157 case NetworkChangeNotifier::CONNECTION_ETHERNET:
145 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Ethernet", 158 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Ethernet",
146 fastest_rtt_since_last_connection_change_); 159 fastest_rtt_since_last_connection_change_);
147 break; 160 break;
148 case NetworkChangeNotifier::CONNECTION_WIFI: 161 case NetworkChangeNotifier::CONNECTION_WIFI:
149 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Wifi", 162 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Wifi",
(...skipping 13 matching lines...) Expand all
163 break; 176 break;
164 case NetworkChangeNotifier::CONNECTION_NONE: 177 case NetworkChangeNotifier::CONNECTION_NONE:
165 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.None", 178 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.None",
166 fastest_rtt_since_last_connection_change_); 179 fastest_rtt_since_last_connection_change_);
167 break; 180 break;
168 case NetworkChangeNotifier::CONNECTION_BLUETOOTH: 181 case NetworkChangeNotifier::CONNECTION_BLUETOOTH:
169 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Bluetooth", 182 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Bluetooth",
170 fastest_rtt_since_last_connection_change_); 183 fastest_rtt_since_last_connection_change_);
171 break; 184 break;
172 default: 185 default:
173 NOTREACHED(); 186 NOTREACHED() << "Unexpected connection type = "
187 << current_network_id_.type;
174 break; 188 break;
175 } 189 }
176 } 190 }
177 191
178 if (peak_kbps_since_last_connection_change_) { 192 if (peak_kbps_since_last_connection_change_) {
179 switch (current_connection_type_) { 193 switch (current_network_id_.type) {
180 case NetworkChangeNotifier::CONNECTION_UNKNOWN: 194 case NetworkChangeNotifier::CONNECTION_UNKNOWN:
181 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Unknown", 195 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Unknown",
182 peak_kbps_since_last_connection_change_); 196 peak_kbps_since_last_connection_change_);
183 break; 197 break;
184 case NetworkChangeNotifier::CONNECTION_ETHERNET: 198 case NetworkChangeNotifier::CONNECTION_ETHERNET:
185 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Ethernet", 199 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Ethernet",
186 peak_kbps_since_last_connection_change_); 200 peak_kbps_since_last_connection_change_);
187 break; 201 break;
188 case NetworkChangeNotifier::CONNECTION_WIFI: 202 case NetworkChangeNotifier::CONNECTION_WIFI:
189 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Wifi", 203 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Wifi",
(...skipping 13 matching lines...) Expand all
203 break; 217 break;
204 case NetworkChangeNotifier::CONNECTION_NONE: 218 case NetworkChangeNotifier::CONNECTION_NONE:
205 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.None", 219 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.None",
206 peak_kbps_since_last_connection_change_); 220 peak_kbps_since_last_connection_change_);
207 break; 221 break;
208 case NetworkChangeNotifier::CONNECTION_BLUETOOTH: 222 case NetworkChangeNotifier::CONNECTION_BLUETOOTH:
209 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Bluetooth", 223 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Bluetooth",
210 peak_kbps_since_last_connection_change_); 224 peak_kbps_since_last_connection_change_);
211 break; 225 break;
212 default: 226 default:
213 NOTREACHED(); 227 NOTREACHED() << "Unexpected connection type = "
228 << current_network_id_.type;
214 break; 229 break;
215 } 230 }
216 } 231 }
217 232
233 // Write the estimates of the previous network to the cache.
234 CacheNetworkQualityEstimate();
235
236 // Clear the local state.
218 last_connection_change_ = base::TimeTicks::Now(); 237 last_connection_change_ = base::TimeTicks::Now();
219 peak_kbps_since_last_connection_change_ = 0; 238 peak_kbps_since_last_connection_change_ = 0;
220 fastest_rtt_since_last_connection_change_ = base::TimeDelta::Max(); 239 fastest_rtt_since_last_connection_change_ = base::TimeDelta::Max();
221 kbps_observations_.Clear(); 240 kbps_observations_.Clear();
222 rtt_msec_observations_.Clear(); 241 rtt_msec_observations_.Clear();
223 current_connection_type_ = type; 242
243 // Update the current network ID.
244 current_network_id_ = GetCurrentNetworkID();
245
246 // Read any cached estimates for the new network.
247 ReadCachedNetworkQualityEstimate();
248 }
249
250 size_t NetworkQualityEstimator::GetNetworkQualityCacheSizeForTests() const {
251 DCHECK(thread_checker_.CalledOnValidThread());
252 return cached_network_qualities_.size();
224 } 253 }
225 254
226 NetworkQuality NetworkQualityEstimator::GetPeakEstimate() const { 255 NetworkQuality NetworkQualityEstimator::GetPeakEstimate() const {
227 DCHECK(thread_checker_.CalledOnValidThread()); 256 DCHECK(thread_checker_.CalledOnValidThread());
228 257
229 return NetworkQuality(fastest_rtt_since_last_connection_change_, 258 return NetworkQuality(fastest_rtt_since_last_connection_change_,
230 peak_kbps_since_last_connection_change_); 259 peak_kbps_since_last_connection_change_);
231 } 260 }
232 261
233 size_t NetworkQualityEstimator::GetMaximumObservationBufferSizeForTests() 262 size_t NetworkQualityEstimator::GetKbpsObservationBufferSizeForTests() const {
234 const { 263 DCHECK(thread_checker_.CalledOnValidThread());
235 return kMaximumObservationsBufferSize; 264 return kbps_observations_.Size();
236 } 265 }
237 266
238 bool NetworkQualityEstimator::VerifyBufferSizeForTests( 267 size_t NetworkQualityEstimator::GetRTTObservationBufferSizeForTests() const {
239 size_t expected_size) const { 268 DCHECK(thread_checker_.CalledOnValidThread());
240 return kbps_observations_.Size() == expected_size && 269 return rtt_msec_observations_.Size();
241 rtt_msec_observations_.Size() == expected_size;
242 } 270 }
243 271
244 NetworkQualityEstimator::Observation::Observation(int32_t value, 272 NetworkQualityEstimator::Observation::Observation(int32_t value,
245 base::TimeTicks timestamp) 273 base::TimeTicks timestamp)
246 : value(value), timestamp(timestamp) { 274 : value(value), timestamp(timestamp) {
247 DCHECK_GE(value, 0); 275 DCHECK_GE(value, 0);
248 DCHECK(!timestamp.is_null()); 276 DCHECK(!timestamp.is_null());
249 } 277 }
250 278
251 NetworkQualityEstimator::Observation::~Observation() { 279 NetworkQualityEstimator::Observation::~Observation() {
252 } 280 }
253 281
254 NetworkQualityEstimator::ObservationBuffer::ObservationBuffer() { 282 NetworkQualityEstimator::ObservationBuffer::ObservationBuffer() {
255 static_assert(kMaximumObservationsBufferSize > 0U, 283 static_assert(kMaximumObservationsBufferSize > 0U,
256 "Minimum size of observation buffer must be > 0"); 284 "Minimum size of observation buffer must be > 0");
257 } 285 }
258 286
259 NetworkQualityEstimator::ObservationBuffer::~ObservationBuffer() { 287 NetworkQualityEstimator::ObservationBuffer::~ObservationBuffer() {
260 } 288 }
261 289
262 void NetworkQualityEstimator::ObservationBuffer::AddObservation( 290 void NetworkQualityEstimator::ObservationBuffer::AddObservation(
263 const Observation& observation) { 291 const Observation& observation) {
264 DCHECK_LE(observations_.size(), kMaximumObservationsBufferSize); 292 DCHECK_LE(observations_.size(),
293 NetworkQualityEstimator::kMaximumObservationsBufferSize);
pauljensen 2015/06/22 18:39:52 Is NetworkQualityEstimator:: really necessary? Dit
tbansal1 2015/06/22 20:28:25 Its not really needed in this file but only in the
265 // Evict the oldest element if the buffer is already full. 294 // Evict the oldest element if the buffer is already full.
266 if (observations_.size() == kMaximumObservationsBufferSize) 295 if (observations_.size() ==
296 NetworkQualityEstimator::kMaximumObservationsBufferSize)
267 observations_.pop_front(); 297 observations_.pop_front();
268 298
269 observations_.push_back(observation); 299 observations_.push_back(observation);
270 DCHECK_LE(observations_.size(), kMaximumObservationsBufferSize); 300 DCHECK_LE(observations_.size(),
301 NetworkQualityEstimator::kMaximumObservationsBufferSize);
271 } 302 }
272 303
273 size_t NetworkQualityEstimator::ObservationBuffer::Size() const { 304 size_t NetworkQualityEstimator::ObservationBuffer::Size() const {
274 return observations_.size(); 305 return observations_.size();
275 } 306 }
276 307
277 void NetworkQualityEstimator::ObservationBuffer::Clear() { 308 void NetworkQualityEstimator::ObservationBuffer::Clear() {
278 observations_.clear(); 309 observations_.clear();
279 DCHECK(observations_.empty()); 310 DCHECK(observations_.empty());
280 } 311 }
281 312
313 NetworkQualityEstimator::NetworkID
314 NetworkQualityEstimator::GetCurrentNetworkID() const {
315 // TODO(tbansal): crbug.com/498068 Add NetworkQualityEstimatorAndroid class
316 // that overrides this method on the Android platform.
317
318 // It is possible that the connection type changed between when
319 // GetConnectionType() was called and when the API to determine the
320 // network name was called. Check if that happened and retry until the
321 // connection type stabilizes. This is an imperfect solution but should
322 // capture majority of cases, and should not significantly affect estimates
323 // (that are approximate to begin with).
324 while (true) {
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();
pauljensen 2015/06/22 18:39:52 We should implement this for windows. It should b
tbansal1 2015/06/22 20:28:25 Filed crbug.com/503235
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
356 if (network_id.type == NetworkChangeNotifier::GetConnectionType())
357 return network_id;
358 }
359 NOTREACHED();
360 }
361
362 bool NetworkQualityEstimator::ReadCachedNetworkQualityEstimate() {
363 DCHECK(thread_checker_.CalledOnValidThread());
364
365 // If the network name is unavailable, caching should not be performed.
366 if (current_network_id_.id.empty())
367 return false;
368
369 CachedNetworkQualities::iterator it =
370 cached_network_qualities_.find(current_network_id_);
371 if (it != cached_network_qualities_.end()) {
372 // TOOD(tbansal): Populate these values back into the median computing
373 // algorithm.
374 // Add UMA to record how frequently matches happen.
pauljensen 2015/06/22 18:39:52 Add a "TODO(tbansal):" here and the next line
tbansal1 2015/06/22 20:28:26 Done.
375 // Ensure that the estimates read are non-zero before populating them into
376 // the median computing algorithm.
377 peak_kbps_since_last_connection_change_ =
378 it->second->network_quality().downstream_throughput_kbps();
379 fastest_rtt_since_last_connection_change_ =
380 it->second->network_quality().rtt();
381 return true;
382 }
383 return false;
384 }
385
386 void NetworkQualityEstimator::CacheNetworkQualityEstimate() {
387 DCHECK(thread_checker_.CalledOnValidThread());
388 DCHECK_LE(cached_network_qualities_.size(), kMaximumNetworkQualityCacheSize);
389
390 // If the network name is unavailable, caching should not be performed.
391 if (current_network_id_.id.empty())
392 return;
393
394 // TODO(tbansal): The following variables should be initialized using the
395 // median values reported by the NetworkQualityEstimator.
396 int median_kbps = peak_kbps_since_last_connection_change_;
397 base::TimeDelta median_rtt = fastest_rtt_since_last_connection_change_;
398
399 // If this network is already in the cache, overwrite that entry.
400 CachedNetworkQualities::iterator it =
401 cached_network_qualities_.find(current_network_id_);
402 if (it != cached_network_qualities_.end()) {
403 (it->second)->UpdateNetworkQuality(median_kbps, median_rtt);
404 return;
405 }
406
407 if (cached_network_qualities_.size() == kMaximumNetworkQualityCacheSize) {
408 // Remove the oldest entry.
409 CachedNetworkQualities::iterator oldest_entry_iterator =
410 cached_network_qualities_.begin();
411
412 for (CachedNetworkQualities::iterator it =
413 cached_network_qualities_.begin();
414 it != cached_network_qualities_.end(); ++it) {
415 if ((it->second)->OlderThan(*(oldest_entry_iterator->second.get())))
416 oldest_entry_iterator = it;
417 }
418 cached_network_qualities_.erase(oldest_entry_iterator);
419 }
420
421 DCHECK_LT(cached_network_qualities_.size(), kMaximumNetworkQualityCacheSize);
422 cached_network_qualities_.insert(std::make_pair(
423 current_network_id_, make_scoped_ptr(new CachedNetworkQuality(
424 NetworkQuality(median_rtt, median_kbps)))));
425 DCHECK_LE(cached_network_qualities_.size(), kMaximumNetworkQualityCacheSize);
426 }
427
428 NetworkQualityEstimator::CachedNetworkQuality::CachedNetworkQuality(
429 const NetworkQuality& network_quality)
430 : last_update_time_(base::TimeTicks::Now()),
431 network_quality_(network_quality) {
432 }
433
434 NetworkQualityEstimator::CachedNetworkQuality::~CachedNetworkQuality() {
435 }
436
437 bool NetworkQualityEstimator::CachedNetworkQuality::OlderThan(
438 const CachedNetworkQuality& cached_network_quality) const {
439 return last_update_time_ < cached_network_quality.last_update_time_;
440 }
441
442 void NetworkQualityEstimator::CachedNetworkQuality::UpdateNetworkQuality(
443 int32_t median_kbps,
444 const base::TimeDelta& median_rtt) {
445 DCHECK_GE(median_kbps, 0);
446 DCHECK_GE(median_rtt, base::TimeDelta());
447 last_update_time_ = base::TimeTicks::Now();
448
449 network_quality_ = NetworkQuality(median_rtt, median_kbps);
450 }
451
282 } // namespace net 452 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698