OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "chrome/browser/net/predictor.h" | 5 #include "chrome/browser/net/predictor.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cmath> | 8 #include <cmath> |
9 #include <set> | 9 #include <set> |
10 #include <sstream> | 10 #include <sstream> |
11 | 11 |
12 #include "base/basictypes.h" | |
12 #include "base/bind.h" | 13 #include "base/bind.h" |
13 #include "base/command_line.h" | 14 #include "base/command_line.h" |
14 #include "base/compiler_specific.h" | 15 #include "base/compiler_specific.h" |
16 #include "base/containers/mru_cache.h" | |
15 #include "base/metrics/histogram.h" | 17 #include "base/metrics/histogram.h" |
16 #include "base/prefs/pref_service.h" | 18 #include "base/prefs/pref_service.h" |
17 #include "base/stl_util.h" | 19 #include "base/stl_util.h" |
18 #include "base/strings/string_split.h" | 20 #include "base/strings/string_split.h" |
19 #include "base/strings/string_util.h" | 21 #include "base/strings/string_util.h" |
20 #include "base/strings/stringprintf.h" | 22 #include "base/strings/stringprintf.h" |
21 #include "base/synchronization/waitable_event.h" | 23 #include "base/synchronization/waitable_event.h" |
22 #include "base/threading/thread_restrictions.h" | 24 #include "base/threading/thread_restrictions.h" |
23 #include "base/time/time.h" | 25 #include "base/time/time.h" |
24 #include "base/values.h" | 26 #include "base/values.h" |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
118 | 120 |
119 Predictor* predictor_; // The predictor which started us. | 121 Predictor* predictor_; // The predictor which started us. |
120 | 122 |
121 const GURL url_; // Hostname to resolve. | 123 const GURL url_; // Hostname to resolve. |
122 net::SingleRequestHostResolver resolver_; | 124 net::SingleRequestHostResolver resolver_; |
123 net::AddressList addresses_; | 125 net::AddressList addresses_; |
124 | 126 |
125 DISALLOW_COPY_AND_ASSIGN(LookupRequest); | 127 DISALLOW_COPY_AND_ASSIGN(LookupRequest); |
126 }; | 128 }; |
127 | 129 |
130 // This records UMAs for preconnect usage based on navigation URLs to | |
jar (doing other things)
2013/08/01 22:56:47
FWIW: The long standing bug in this algorithm is t
kouhei (in TOK)
2013/08/02 06:18:03
My understanding is that per-socket utilization co
| |
131 // gather precision/recall for user-event based preconnect triggers. | |
132 // Stats are gathered via a LRU cach that remembers all preconnect within the | |
jar (doing other things)
2013/08/01 22:56:47
nit: cache-->cache
kouhei (in TOK)
2013/08/02 06:18:03
Done.
| |
133 // last N seconds. | |
134 // A preconnect trigger is considered as used iff a navigation including | |
135 // access to the preconnected host occurs within a time period specified by | |
jar (doing other things)
2013/08/01 22:56:47
Per comment: We count access (still), and not simu
| |
136 // kMaxUnusedSocketLifetimeSecondsWithoutAGet. | |
137 class Predictor::PreconnectUsage { | |
138 public: | |
139 PreconnectUsage(); | |
140 ~PreconnectUsage(); | |
141 | |
142 // Record a preconnect trigger to |url|. | |
143 void ObservePreconnect(const GURL& url); | |
144 | |
145 // Record a user navigation with its redirect history, |url_chain|. | |
146 // We are uncertain if this is actually a link navigation. | |
147 void ObserveNavigationChain(const std::vector<GURL>& url_chain, | |
148 bool is_subresource); | |
149 | |
150 // Record a user link navigation to |final_url|. | |
151 // We are certain that this is a user-triggered link navigation. | |
152 void ObserveLinkNavigation(const GURL& final_url); | |
153 | |
154 private: | |
155 // This tracks whether a preconnect was used in some navigation or not | |
156 class PreconnectPrecisionStat { | |
157 public: | |
158 PreconnectPrecisionStat() | |
159 : timestamp_(base::TimeTicks::Now()), | |
160 was_used_(false) { | |
161 } | |
162 | |
163 const base::TimeTicks& timestamp() { return timestamp_; } | |
164 | |
165 void set_was_used() { was_used_ = true; } | |
166 bool was_used() const { return was_used_; } | |
167 | |
168 private: | |
169 base::TimeTicks timestamp_; | |
170 bool was_used_; | |
171 }; | |
172 | |
173 typedef base::MRUCache<GURL, PreconnectPrecisionStat> MRUPreconnects; | |
174 MRUPreconnects mru_preconnects_; | |
175 | |
176 // The longest time an entry can persist in mru_preconnect_ | |
177 const base::TimeDelta max_duration_; | |
178 | |
179 std::vector<GURL> recent_navigation_chain_; | |
180 | |
181 DISALLOW_COPY_AND_ASSIGN(PreconnectUsage); | |
182 }; | |
183 | |
184 Predictor::PreconnectUsage::PreconnectUsage() | |
185 : mru_preconnects_(MRUPreconnects::NO_AUTO_EVICT), | |
186 max_duration_(base::TimeDelta::FromSeconds( | |
187 Predictor::kMaxUnusedSocketLifetimeSecondsWithoutAGet)) { | |
188 } | |
189 | |
190 Predictor::PreconnectUsage::~PreconnectUsage() {} | |
191 | |
192 void Predictor::PreconnectUsage::ObservePreconnect(const GURL& url) { | |
193 // Evict any overly old entries and record stats | |
jar (doing other things)
2013/08/01 22:56:47
nit: Period at end of sentences.
kouhei (in TOK)
2013/08/02 06:18:03
Done.
| |
194 base::TimeTicks now = base::TimeTicks::Now(); | |
195 | |
196 MRUPreconnects::reverse_iterator eldest_preconnect = | |
197 mru_preconnects_.rbegin(); | |
198 while (!mru_preconnects_.empty()) { | |
199 DCHECK(eldest_preconnect == mru_preconnects_.rbegin()); | |
200 if (now - eldest_preconnect->second.timestamp() < max_duration_) | |
201 break; | |
202 | |
203 UMA_HISTOGRAM_BOOLEAN("Net.PreconnectTriggerUsed", | |
204 eldest_preconnect->second.was_used()); | |
205 eldest_preconnect = mru_preconnects_.Erase(eldest_preconnect); | |
206 } | |
207 | |
208 // Add new entry | |
jar (doing other things)
2013/08/01 22:56:47
nit: period
kouhei (in TOK)
2013/08/02 06:18:03
Done.
| |
209 GURL canonical_url(Predictor::CanonicalizeUrl(url)); | |
210 mru_preconnects_.Put(canonical_url, PreconnectPrecisionStat()); | |
211 } | |
212 | |
213 void Predictor::PreconnectUsage::ObserveNavigationChain( | |
214 const std::vector<GURL>& url_chain, bool is_subresource) { | |
jar (doing other things)
2013/08/01 22:56:47
nit: one arg per line.
kouhei (in TOK)
2013/08/02 06:18:03
Done.
| |
215 if (url_chain.empty()) | |
216 return; | |
217 | |
218 if (!is_subresource) | |
219 recent_navigation_chain_ = url_chain; | |
220 | |
221 GURL canonical_url(Predictor::CanonicalizeUrl(url_chain.back())); | |
222 | |
223 // Record the preconnect trigger for the url as used if exist | |
224 MRUPreconnects::iterator itPreconnect = mru_preconnects_.Peek(canonical_url); | |
225 bool was_preconnected = (itPreconnect != mru_preconnects_.end()); | |
226 if (was_preconnected) | |
227 itPreconnect->second.set_was_used(); | |
228 | |
229 // This is an UMA which was named incorrectly. This actually measures the | |
230 // ratio of URLRequests which have used a preconnected session. | |
231 UMA_HISTOGRAM_BOOLEAN("Net.PreconnectedNavigation", was_preconnected); | |
232 } | |
233 | |
234 void Predictor::PreconnectUsage::ObserveLinkNavigation(const GURL& url) { | |
235 if (recent_navigation_chain_.empty() || | |
236 url != recent_navigation_chain_.back()) { | |
237 // The navigation chain is not available for this navigation. | |
238 recent_navigation_chain_.clear(); | |
239 recent_navigation_chain_.push_back(url); | |
240 } | |
241 | |
242 // See if the link navigation involved preconnected session. | |
243 bool did_use_preconnect = false; | |
244 for (std::vector<GURL>::const_iterator it = recent_navigation_chain_.begin(); | |
245 it != recent_navigation_chain_.end(); | |
246 ++it) { | |
247 GURL canonical_url(Predictor::CanonicalizeUrl(*it)); | |
248 | |
249 // Record the preconnect trigger for the url as used if exist | |
250 MRUPreconnects::iterator itPreconnect = | |
251 mru_preconnects_.Peek(canonical_url); | |
252 bool was_preconnected = (itPreconnect != mru_preconnects_.end()); | |
253 if (was_preconnected) | |
254 did_use_preconnect = true; | |
255 } | |
256 | |
257 UMA_HISTOGRAM_BOOLEAN("Net.PreconnectedLinkNavigations", did_use_preconnect); | |
258 } | |
259 | |
128 Predictor::Predictor(bool preconnect_enabled) | 260 Predictor::Predictor(bool preconnect_enabled) |
129 : url_request_context_getter_(NULL), | 261 : url_request_context_getter_(NULL), |
130 predictor_enabled_(true), | 262 predictor_enabled_(true), |
131 peak_pending_lookups_(0), | 263 peak_pending_lookups_(0), |
132 shutdown_(false), | 264 shutdown_(false), |
133 max_concurrent_dns_lookups_(g_max_parallel_resolves), | 265 max_concurrent_dns_lookups_(g_max_parallel_resolves), |
134 max_dns_queue_delay_( | 266 max_dns_queue_delay_( |
135 TimeDelta::FromMilliseconds(g_max_queueing_delay_ms)), | 267 TimeDelta::FromMilliseconds(g_max_queueing_delay_ms)), |
136 host_resolver_(NULL), | 268 host_resolver_(NULL), |
137 preconnect_enabled_(preconnect_enabled), | 269 preconnect_enabled_(preconnect_enabled), |
138 consecutive_omnibox_preconnect_count_(0), | 270 consecutive_omnibox_preconnect_count_(0), |
139 recent_preconnects_( | |
140 TimeDelta::FromSeconds(kMaxUnusedSocketLifetimeSecondsWithoutAGet)), | |
141 next_trim_time_(base::TimeTicks::Now() + | 271 next_trim_time_(base::TimeTicks::Now() + |
142 TimeDelta::FromHours(kDurationBetweenTrimmingsHours)) { | 272 TimeDelta::FromHours(kDurationBetweenTrimmingsHours)) { |
143 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 273 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
144 } | 274 } |
145 | 275 |
146 Predictor::~Predictor() { | 276 Predictor::~Predictor() { |
147 // TODO(rlp): Add DCHECK for CurrentlyOn(BrowserThread::IO) when the | 277 // TODO(rlp): Add DCHECK for CurrentlyOn(BrowserThread::IO) when the |
148 // ProfileManagerTest has been updated with a mock profile. | 278 // ProfileManagerTest has been updated with a mock profile. |
149 DCHECK(shutdown_); | 279 DCHECK(shutdown_); |
150 } | 280 } |
(...skipping 510 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
661 void Predictor::FinalizeInitializationOnIOThread( | 791 void Predictor::FinalizeInitializationOnIOThread( |
662 const UrlList& startup_urls, | 792 const UrlList& startup_urls, |
663 base::ListValue* referral_list, | 793 base::ListValue* referral_list, |
664 IOThread* io_thread, | 794 IOThread* io_thread, |
665 bool predictor_enabled) { | 795 bool predictor_enabled) { |
666 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 796 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
667 | 797 |
668 predictor_enabled_ = predictor_enabled; | 798 predictor_enabled_ = predictor_enabled; |
669 initial_observer_.reset(new InitialObserver()); | 799 initial_observer_.reset(new InitialObserver()); |
670 host_resolver_ = io_thread->globals()->host_resolver.get(); | 800 host_resolver_ = io_thread->globals()->host_resolver.get(); |
801 preconnect_usage_.reset(new PreconnectUsage()); | |
671 | 802 |
672 // base::WeakPtrFactory instances need to be created and destroyed | 803 // base::WeakPtrFactory instances need to be created and destroyed |
673 // on the same thread. The predictor lives on the IO thread and will die | 804 // on the same thread. The predictor lives on the IO thread and will die |
674 // from there so now that we're on the IO thread we need to properly | 805 // from there so now that we're on the IO thread we need to properly |
675 // initialize the base::WeakPtrFactory. | 806 // initialize the base::WeakPtrFactory. |
676 // TODO(groby): Check if WeakPtrFactory has the same constraint. | 807 // TODO(groby): Check if WeakPtrFactory has the same constraint. |
677 weak_factory_.reset(new base::WeakPtrFactory<Predictor>(this)); | 808 weak_factory_.reset(new base::WeakPtrFactory<Predictor>(this)); |
678 | 809 |
679 // Prefetch these hostnames on startup. | 810 // Prefetch these hostnames on startup. |
680 DnsPrefetchMotivatedList(startup_urls, UrlInfo::STARTUP_LIST_MOTIVATED); | 811 DnsPrefetchMotivatedList(startup_urls, UrlInfo::STARTUP_LIST_MOTIVATED); |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
837 BrowserThread::PostTask( | 968 BrowserThread::PostTask( |
838 BrowserThread::IO, | 969 BrowserThread::IO, |
839 FROM_HERE, | 970 FROM_HERE, |
840 base::Bind(&Predictor::PreconnectUrlOnIOThread, | 971 base::Bind(&Predictor::PreconnectUrlOnIOThread, |
841 base::Unretained(this), url, first_party_for_cookies, | 972 base::Unretained(this), url, first_party_for_cookies, |
842 motivation, count)); | 973 motivation, count)); |
843 } | 974 } |
844 } | 975 } |
845 | 976 |
846 void Predictor::PreconnectUrlOnIOThread( | 977 void Predictor::PreconnectUrlOnIOThread( |
847 const GURL& url, const GURL& first_party_for_cookies, | 978 const GURL& url, |
848 UrlInfo::ResolutionMotivation motivation, int count) { | 979 const GURL& first_party_for_cookies, |
849 GURL canonical_url(CanonicalizeUrl(url)); | 980 UrlInfo::ResolutionMotivation motivation, |
850 recent_preconnects_.SetRecentlySeen(canonical_url); | 981 int count) { |
851 | |
852 PreconnectOnIOThread(url, | 982 PreconnectOnIOThread(url, |
853 first_party_for_cookies, | 983 first_party_for_cookies, |
854 motivation, | 984 motivation, |
855 count, | 985 count, |
856 url_request_context_getter_.get()); | 986 url_request_context_getter_.get()); |
857 } | 987 } |
858 | 988 |
859 void Predictor::RecordPreconnectNavigationStats(const GURL& url) { | 989 void Predictor::RecordPreconnectTrigger(const GURL& url) { |
860 UMA_HISTOGRAM_BOOLEAN( | 990 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
861 "Net.PreconnectedNavigation", | 991 BrowserThread::CurrentlyOn(BrowserThread::IO)); |
862 recent_preconnects_.WasRecentlySeen(url)); | 992 |
993 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { | |
994 RecordPreconnectTriggerOnIOThread(url); | |
995 } else { | |
996 BrowserThread::PostTask( | |
997 BrowserThread::IO, | |
998 FROM_HERE, | |
999 base::Bind(&Predictor::RecordPreconnectTriggerOnIOThread, | |
1000 base::Unretained(this), url)); | |
1001 } | |
1002 } | |
1003 | |
1004 void Predictor::RecordPreconnectTriggerOnIOThread(const GURL& url) { | |
1005 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
1006 if (preconnect_usage_) | |
1007 preconnect_usage_->ObservePreconnect(url); | |
1008 } | |
1009 | |
1010 void Predictor::RecordPreconnectNavigationStat( | |
1011 const std::vector<GURL>& url_chain, bool is_subresource) { | |
jar (doing other things)
2013/08/01 22:56:47
nit: one arg per line.
kouhei (in TOK)
2013/08/02 06:18:03
Done.
| |
1012 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
1013 | |
1014 if (preconnect_usage_) | |
1015 preconnect_usage_->ObserveNavigationChain(url_chain, is_subresource); | |
1016 } | |
1017 | |
1018 void Predictor::RecordLinkNavigation(const GURL& url) { | |
1019 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | |
1020 BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
1021 | |
1022 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { | |
1023 RecordLinkNavigationOnIOThread(url); | |
1024 } else { | |
1025 BrowserThread::PostTask( | |
1026 BrowserThread::IO, | |
1027 FROM_HERE, | |
1028 base::Bind(&Predictor::RecordLinkNavigationOnIOThread, | |
1029 base::Unretained(this), url)); | |
1030 } | |
1031 } | |
1032 | |
1033 void Predictor::RecordLinkNavigationOnIOThread(const GURL& url) { | |
1034 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
1035 if (preconnect_usage_) | |
1036 preconnect_usage_->ObserveLinkNavigation(url); | |
863 } | 1037 } |
864 | 1038 |
865 void Predictor::PredictFrameSubresources(const GURL& url, | 1039 void Predictor::PredictFrameSubresources(const GURL& url, |
866 const GURL& first_party_for_cookies) { | 1040 const GURL& first_party_for_cookies) { |
867 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | 1041 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
868 BrowserThread::CurrentlyOn(BrowserThread::IO)); | 1042 BrowserThread::CurrentlyOn(BrowserThread::IO)); |
869 if (!predictor_enabled_) | 1043 if (!predictor_enabled_) |
870 return; | 1044 return; |
871 DCHECK_EQ(url.GetWithEmptyPath(), url); | 1045 DCHECK_EQ(url.GetWithEmptyPath(), url); |
872 // Add one pass through the message loop to allow current navigation to | 1046 // Add one pass through the message loop to allow current navigation to |
(...skipping 354 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1227 IOThread* io_thread, | 1401 IOThread* io_thread, |
1228 net::URLRequestContextGetter* getter) { | 1402 net::URLRequestContextGetter* getter) { |
1229 // Empty function for unittests. | 1403 // Empty function for unittests. |
1230 } | 1404 } |
1231 | 1405 |
1232 void SimplePredictor::ShutdownOnUIThread(PrefService* user_prefs) { | 1406 void SimplePredictor::ShutdownOnUIThread(PrefService* user_prefs) { |
1233 SetShutdown(true); | 1407 SetShutdown(true); |
1234 } | 1408 } |
1235 | 1409 |
1236 } // namespace chrome_browser_net | 1410 } // namespace chrome_browser_net |
OLD | NEW |