diff options
-rw-r--r-- | chrome/browser/io_thread.cc | 21 | ||||
-rw-r--r-- | chrome/browser/net/connect_interceptor.cc | 25 | ||||
-rw-r--r-- | chrome/browser/net/preconnect.cc | 16 | ||||
-rw-r--r-- | chrome/browser/net/preconnect.h | 12 | ||||
-rw-r--r-- | chrome/browser/net/predictor.cc | 184 | ||||
-rw-r--r-- | chrome/browser/net/predictor.h | 34 | ||||
-rw-r--r-- | chrome/browser/net/predictor_api.cc | 178 | ||||
-rw-r--r-- | chrome/browser/net/predictor_api.h | 4 | ||||
-rw-r--r-- | chrome/browser/net/predictor_unittest.cc | 170 | ||||
-rw-r--r-- | chrome/browser/net/referrer.cc | 114 | ||||
-rw-r--r-- | chrome/browser/net/referrer.h | 44 | ||||
-rw-r--r-- | chrome/browser/net/url_info.cc | 124 | ||||
-rw-r--r-- | chrome/browser/net/url_info.h | 28 | ||||
-rw-r--r-- | chrome/browser/renderer_host/resource_dispatcher_host.cc | 7 | ||||
-rw-r--r-- | net/base/load_flags_list.h | 9 |
15 files changed, 285 insertions, 685 deletions
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc index b0e27b2..998722e 100644 --- a/chrome/browser/io_thread.cc +++ b/chrome/browser/io_thread.cc @@ -336,17 +336,16 @@ void IOThread::InitNetworkPredictorOnIOThread( preconnect_enabled); predictor_->AddRef(); - // TODO(jar): Until connection notification and DNS observation handling are - // properly combined into a learning model, we'll only use one observation - // mechanism or the other. - if (preconnect_enabled) { - DCHECK(!speculative_interceptor_); - speculative_interceptor_ = new chrome_browser_net::ConnectInterceptor; - } else { - DCHECK(!prefetch_observer_); - prefetch_observer_ = chrome_browser_net::CreateResolverObserver(); - globals_->host_resolver->AddObserver(prefetch_observer_); - } + // Speculative_interceptor_ is used to predict subresource usage. + DCHECK(!speculative_interceptor_); + speculative_interceptor_ = new chrome_browser_net::ConnectInterceptor; + + // TODO(jar): We can completely replace prefetch_observer with + // speculative_interceptor. + // Prefetch_observer is used to monitor initial resolutions. + DCHECK(!prefetch_observer_); + prefetch_observer_ = chrome_browser_net::CreateResolverObserver(); + globals_->host_resolver->AddObserver(prefetch_observer_); FinalizePredictorInitialization( predictor_, prefetch_observer_, startup_urls, referral_list); diff --git a/chrome/browser/net/connect_interceptor.cc b/chrome/browser/net/connect_interceptor.cc index 7415ac1..99abf24 100644 --- a/chrome/browser/net/connect_interceptor.cc +++ b/chrome/browser/net/connect_interceptor.cc @@ -5,6 +5,7 @@ #include "chrome/browser/net/connect_interceptor.h" #include "chrome/browser/net/predictor_api.h" +#include "net/base/load_flags.h" namespace chrome_browser_net { @@ -17,24 +18,18 @@ ConnectInterceptor::~ConnectInterceptor() { } URLRequestJob* ConnectInterceptor::MaybeIntercept(URLRequest* request) { - if (!request->referrer().empty()) { + bool is_subresource = !(request->load_flags() & net::LOAD_MAIN_FRAME); + if (is_subresource && !request->referrer().empty()) { // Learn about our referring URL, for use in the future. - GURL referring_url(GURL(request->referrer()).GetWithEmptyPath()); - // TODO(jar): Only call if we think this was part of a frame load, and not a - // link navigation. For now, we'll "learn" that to preconnect when a user - // actually does a click... which will probably waste space in our referrers - // table (since it probably won't be that deterministic). - LearnFromNavigation(referring_url, request->url().GetWithEmptyPath()); + GURL referring_url(request->referrer()); + LearnFromNavigation(referring_url.GetWithEmptyPath(), + request->url().GetWithEmptyPath()); } + bool is_frame = 0 != (request->load_flags() & (net::LOAD_SUB_FRAME | + net::LOAD_MAIN_FRAME)); // Now we use previous learning and setup for our subresources. - if (request->was_fetched_via_proxy()) - return NULL; - // TODO(jar): Only call if we believe this is a frame, and might have - // subresources. We could "guess" by looking at path extensions (such as - // foo.jpg or goo.gif etc.), but better would be to get this info from webkit - // and have it add the info to the request (we currently only set the - // priority, but we could record whether it was a frame). - PredictFrameSubresources(request->url().GetWithEmptyPath()); + if (is_frame && !request->was_fetched_via_proxy()) + PredictFrameSubresources(request->url().GetWithEmptyPath()); return NULL; } diff --git a/chrome/browser/net/preconnect.cc b/chrome/browser/net/preconnect.cc index af0c308..2d4db36 100644 --- a/chrome/browser/net/preconnect.cc +++ b/chrome/browser/net/preconnect.cc @@ -26,17 +26,18 @@ bool Preconnect::preconnect_despite_proxy_ = false; Preconnect* Preconnect::callback_instance_; // static -bool Preconnect::PreconnectOnUIThread(const GURL& url) { +void Preconnect::PreconnectOnUIThread(const GURL& url, + UrlInfo::ResolutionMotivation motivation) { // Try to do connection warming for this search provider. URLRequestContextGetter* getter = Profile::GetDefaultRequestContext(); if (!getter) - return false; + return; // Prewarm connection to Search URL. ChromeThread::PostTask( ChromeThread::IO, FROM_HERE, - NewRunnableFunction(Preconnect::PreconnectOnIOThread, url)); - return true; + NewRunnableFunction(Preconnect::PreconnectOnIOThread, url, motivation)); + return; } enum ProxyStatus { @@ -53,8 +54,8 @@ static void HistogramPreconnectStatus(ProxyStatus status) { } // static -void Preconnect::PreconnectOnIOThread(const GURL& url) { - // TODO(jar): This does not handle proxies currently. +void Preconnect::PreconnectOnIOThread(const GURL& url, + UrlInfo::ResolutionMotivation motivation) { URLRequestContextGetter* getter = Profile::GetDefaultRequestContext(); if (!getter) return; @@ -84,6 +85,9 @@ void Preconnect::PreconnectOnIOThread(const GURL& url) { } } + UMA_HISTOGRAM_ENUMERATION("Net.PreconnectMotivation", motivation, + UrlInfo::MAX_MOTIVATED); + net::HttpTransactionFactory* factory = context->http_transaction_factory(); net::HttpNetworkSession* session = factory->GetSession(); diff --git a/chrome/browser/net/preconnect.h b/chrome/browser/net/preconnect.h index 83966b1..d773636 100644 --- a/chrome/browser/net/preconnect.h +++ b/chrome/browser/net/preconnect.h @@ -10,6 +10,7 @@ #pragma once #include "base/ref_counted.h" +#include "chrome/browser/net/url_info.h" #include "net/base/completion_callback.h" #include "net/base/host_port_pair.h" #include "net/socket/client_socket_handle.h" @@ -20,9 +21,14 @@ namespace chrome_browser_net { class Preconnect : public net::CompletionCallback { public: - static bool PreconnectOnUIThread(const GURL& url); - - static void PreconnectOnIOThread(const GURL& url); + // Try to preconnect. Typically motivated by OMNIBOX to reach search service. + static void PreconnectOnUIThread(const GURL& url, + UrlInfo::ResolutionMotivation motivation); + + // Try to preconnect. Typically used by predictor when a subresource probably + // needs a connection. + static void PreconnectOnIOThread(const GURL& url, + UrlInfo::ResolutionMotivation motivation); static void SetPreconnectDespiteProxy(bool status) { preconnect_despite_proxy_ = status; diff --git a/chrome/browser/net/predictor.cc b/chrome/browser/net/predictor.cc index 2675f88..eb4992c 100644 --- a/chrome/browser/net/predictor.cc +++ b/chrome/browser/net/predictor.cc @@ -26,6 +26,14 @@ using base::TimeDelta; namespace chrome_browser_net { +// static +const double Predictor::kPreconnectWorthyExpectedValue = 0.7; +// static +const double Predictor::kDNSPreresolutionWorthyExpectedValue = 0.2; +// static +const double Predictor::kPersistWorthyExpectedValue = 0.1; + + class Predictor::LookupRequest { public: LookupRequest(Predictor* predictor, @@ -118,60 +126,6 @@ void Predictor::Resolve(const GURL& url, AppendToResolutionQueue(url, motivation); } -bool Predictor::AccruePrefetchBenefits(const GURL& referrer, - UrlInfo* navigation_info) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); - GURL url = navigation_info->url(); - Results::iterator it = results_.find(url); - if (it == results_.end()) { - // Use UMA histogram to quantify potential future gains here. - UMA_HISTOGRAM_LONG_TIMES("DNS.UnexpectedResolutionL", - navigation_info->resolve_duration()); - navigation_info->DLogResultsStats("DNS UnexpectedResolution"); - - LearnFromNavigation(referrer, navigation_info->url()); - return false; - } - UrlInfo& prefetched_host_info(it->second); - - // Sometimes a host is used as a subresource by several referrers, so it is - // in our list, but was never motivated by a page-link-scan. In that case, it - // really is an "unexpected" navigation, and we should tally it, and augment - // our referrers_. - bool referrer_based_prefetch = !prefetched_host_info.was_linked(); - if (referrer_based_prefetch) { - // This wasn't the first time this host refered to *some* referrer. - LearnFromNavigation(referrer, navigation_info->url()); - } - - DnsBenefit benefit = prefetched_host_info.AccruePrefetchBenefits( - navigation_info); - switch (benefit) { - case PREFETCH_NAME_FOUND: - case PREFETCH_NAME_NONEXISTANT: - dns_cache_hits_.push_back(*navigation_info); - if (referrer_based_prefetch) { - if (referrer.has_host()) { - referrers_[referrer].AccrueValue( - navigation_info->benefits_remaining(), url); - } - } - return true; - - case PREFETCH_CACHE_EVICTION: - cache_eviction_map_[url] = *navigation_info; - return false; - - case PREFETCH_NO_BENEFIT: - // Prefetch never hit the network. Name was pre-cached. - return false; - - default: - NOTREACHED(); - return false; - } -} - void Predictor::LearnFromNavigation(const GURL& referring_url, const GURL& target_url) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); @@ -182,22 +136,12 @@ void Predictor::LearnFromNavigation(const GURL& referring_url, } } -void Predictor::PredictSubresources(const GURL& url) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); - Referrers::iterator it = referrers_.find(url); - if (referrers_.end() == it) - return; - Referrer* referrer = &(it->second); - referrer->IncrementUseCount(); - for (Referrer::iterator future_url = referrer->begin(); - future_url != referrer->end(); ++future_url) { - UrlInfo* queued_info = AppendToResolutionQueue( - future_url->first, - UrlInfo::LEARNED_REFERAL_MOTIVATED); - if (queued_info) - queued_info->SetReferringHostname(url); - } -} +enum SubresourceValue { + PRECONNECTION, + PRERESOLUTION, + TOO_NEW, + SUBRESOURCE_VALUE_MAX +}; void Predictor::PredictFrameSubresources(const GURL& url) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); @@ -207,19 +151,36 @@ void Predictor::PredictFrameSubresources(const GURL& url) { return; Referrer* referrer = &(it->second); referrer->IncrementUseCount(); + const UrlInfo::ResolutionMotivation motivation = + UrlInfo::LEARNED_REFERAL_MOTIVATED; for (Referrer::iterator future_url = referrer->begin(); future_url != referrer->end(); ++future_url) { - if (future_url->second.IsPreconnectWorthDoing()) - Preconnect::PreconnectOnIOThread(future_url->first); + SubresourceValue evalution(TOO_NEW); + double connection_expectation = future_url->second.subresource_use_rate(); + UMA_HISTOGRAM_CUSTOM_COUNTS("Net.PreconnectSubresourceExpectation", + static_cast<int>(connection_expectation * 100), + 10, 5000, 50); + future_url->second.ReferrerWasObserved(); + if (preconnect_enabled_ && + kPreconnectWorthyExpectedValue < connection_expectation) { + evalution = PRECONNECTION; + future_url->second.IncrementPreconnectionCount(); + Preconnect::PreconnectOnIOThread(future_url->first, motivation); + } else if (kDNSPreresolutionWorthyExpectedValue < connection_expectation) { + evalution = PRERESOLUTION; + future_url->second.preresolution_increment(); + UrlInfo* queued_info = AppendToResolutionQueue(future_url->first, + motivation); + if (queued_info) + queued_info->SetReferringHostname(url); + } + UMA_HISTOGRAM_ENUMERATION("Net.PreconnectSubresourceEval", evalution, + SUBRESOURCE_VALUE_MAX); } } // Provide sort order so all .com's are together, etc. struct RightToLeftStringSorter { - bool operator()(const net::HostPortPair& left, - const net::HostPortPair& right) const { - return string_compare(left.host(), right.host()); - } bool operator()(const GURL& left, const GURL& right) const { return string_compare(left.host(), right.host()); @@ -301,8 +262,8 @@ void Predictor::GetHtmlReferrerLists(std::string* output) { "<th>Page Load<br>Count</th>" "<th>Subresource<br>Navigations</th>" "<th>Subresource<br>PreConnects</th>" + "<th>Subresource<br>PreResolves</th>" "<th>Expected<br>Connects</th>" - "<th>DNS<br>Savings</th>" "<th>Subresource Spec</th></tr>"); for (SortedNames::iterator it = sorted_names.begin(); @@ -320,11 +281,11 @@ void Predictor::GetHtmlReferrerLists(std::string* output) { static_cast<int>(referrer->use_count())); first_set_of_futures = false; StringAppendF(output, - "<td>%d</td><td>%d</td><td>%2.3f</td><td>%dms</td><td>%s</td></tr>", + "<td>%d</td><td>%d</td><td>%d</td><td>%2.3f</td><td>%s</td></tr>", static_cast<int>(future_url->second.navigation_count()), static_cast<int>(future_url->second.preconnection_count()), + static_cast<int>(future_url->second.preresolution_count()), static_cast<double>(future_url->second.subresource_use_rate()), - static_cast<int>(future_url->second.latency().InMilliseconds()), future_url->first.spec().c_str()); } } @@ -334,53 +295,26 @@ void Predictor::GetHtmlReferrerLists(std::string* output) { void Predictor::GetHtmlInfo(std::string* output) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); // Local lists for calling UrlInfo - UrlInfo::DnsInfoTable cache_hits; - UrlInfo::DnsInfoTable cache_evictions; - UrlInfo::DnsInfoTable name_not_found; - UrlInfo::DnsInfoTable network_hits; - UrlInfo::DnsInfoTable already_cached; + UrlInfo::UrlInfoTable name_not_found; + UrlInfo::UrlInfoTable name_preresolved; // Get copies of all useful data. - typedef std::map<GURL, UrlInfo, RightToLeftStringSorter> - Snapshot; - Snapshot snapshot; - { - // UrlInfo supports value semantics, so we can do a shallow copy. - for (Results::iterator it(results_.begin()); it != results_.end(); it++) { - snapshot[it->first] = it->second; - } - for (Results::iterator it(cache_eviction_map_.begin()); - it != cache_eviction_map_.end(); - it++) { - cache_evictions.push_back(it->second); - } - // Reverse list as we copy cache hits, so that new hits are at the top. - size_t index = dns_cache_hits_.size(); - while (index > 0) { - index--; - cache_hits.push_back(dns_cache_hits_[index]); - } - } + typedef std::map<GURL, UrlInfo, RightToLeftStringSorter> SortedUrlInfo; + SortedUrlInfo snapshot; + // UrlInfo supports value semantics, so we can do a shallow copy. + for (Results::iterator it(results_.begin()); it != results_.end(); it++) + snapshot[it->first] = it->second; // Partition the UrlInfo's into categories. - for (Snapshot::iterator it(snapshot.begin()); it != snapshot.end(); it++) { + for (SortedUrlInfo::iterator it(snapshot.begin()); + it != snapshot.end(); it++) { if (it->second.was_nonexistant()) { name_not_found.push_back(it->second); continue; } if (!it->second.was_found()) continue; // Still being processed. - if (TimeDelta() != it->second.benefits_remaining()) { - network_hits.push_back(it->second); // With no benefit yet. - continue; - } - if (UrlInfo::kMaxNonNetworkDnsLookupDuration > - it->second.resolve_duration()) { - already_cached.push_back(it->second); - continue; - } - // Remaining case is where prefetch benefit was significant, and was used. - // Since we shot those cases as historical hits, we won't bother here. + name_preresolved.push_back(it->second); } bool brief = false; @@ -389,16 +323,10 @@ void Predictor::GetHtmlInfo(std::string* output) { #endif // NDEBUG // Call for display of each table, along with title. - UrlInfo::GetHtmlTable(cache_hits, - "Prefetching DNS records produced benefits for ", false, output); - UrlInfo::GetHtmlTable(cache_evictions, - "Cache evictions negated DNS prefetching benefits for ", brief, output); - UrlInfo::GetHtmlTable(network_hits, - "Prefetching DNS records was not yet beneficial for ", brief, output); - UrlInfo::GetHtmlTable(already_cached, - "Previously cached resolutions were found for ", brief, output); + UrlInfo::GetHtmlTable(name_preresolved, + "Preresolution DNS records performed for ", brief, output); UrlInfo::GetHtmlTable(name_not_found, - "Prefetching DNS records revealed non-existance for ", brief, output); + "Preresolving DNS records revealed non-existance for ", brief, output); } UrlInfo* Predictor::AppendToResolutionQueue( @@ -507,8 +435,6 @@ void Predictor::LookupFinished(LookupRequest* request, const GURL& url, void Predictor::DiscardAllResults() { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); // Delete anything listed so far in this session that shows in about:dns. - cache_eviction_map_.clear(); - dns_cache_hits_.clear(); referrers_.clear(); @@ -559,7 +485,7 @@ void Predictor::TrimReferrers() { void Predictor::SerializeReferrers(ListValue* referral_list) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); referral_list->Clear(); - referral_list->Append(new FundamentalValue(DNS_REFERRER_VERSION)); + referral_list->Append(new FundamentalValue(PREDICTOR_REFERRER_VERSION)); for (Referrers::const_iterator it = referrers_.begin(); it != referrers_.end(); ++it) { // Serialize the list of subresource names. @@ -579,7 +505,7 @@ void Predictor::DeserializeReferrers(const ListValue& referral_list) { int format_version = -1; if (referral_list.GetSize() > 0 && referral_list.GetInteger(0, &format_version) && - format_version == DNS_REFERRER_VERSION) { + format_version == PREDICTOR_REFERRER_VERSION) { for (size_t i = 1; i < referral_list.GetSize(); ++i) { ListValue* motivator; if (!referral_list.GetList(i, &motivator)) { diff --git a/chrome/browser/net/predictor.h b/chrome/browser/net/predictor.h index f6c14f3..a5b71e7 100644 --- a/chrome/browser/net/predictor.h +++ b/chrome/browser/net/predictor.h @@ -13,8 +13,8 @@ // Subresource relationships are usually acquired from the referrer field in a // navigation. A subresource URL may be associated with a referrer URL. Later // navigations may, if the likelihood of needing the subresource is high enough, -// cause this module to speculatively create a TCP/IP connection that will -// probably be needed to fetch the subresource. +// cause this module to speculatively create a TCP/IP connection. If there is +// only a low likelihood, then a DNS pre-resolution operation may be performed. #ifndef CHROME_BROWSER_NET_PREDICTOR_H_ #define CHROME_BROWSER_NET_PREDICTOR_H_ @@ -49,9 +49,19 @@ class Predictor : public base::RefCountedThreadSafe<Predictor> { public: // A version number for prefs that are saved. This should be incremented when // we change the format so that we discard old data. - enum { DNS_REFERRER_VERSION = 1 }; - -// |max_concurrent| specifies how many concurrent (parallel) prefetches will + enum { PREDICTOR_REFERRER_VERSION = 2 }; + + // Depending on the expected_subresource_use_, we may either make a TCP/IP + // preconnection, or merely pre-resolve the hostname via DNS (or even do + // nothing). The following are the threasholds for taking those actions. + static const double kPreconnectWorthyExpectedValue; + static const double kDNSPreresolutionWorthyExpectedValue; + // Values of expected_subresource_use_ that are less than the following + // threshold will be discarded when we Trim() the values, such as is done when + // the process ends, and some values are persisted. + static const double kPersistWorthyExpectedValue; + + // |max_concurrent| specifies how many concurrent (parallel) prefetches will // be performed. Host lookups will be issued through |host_resolver|. Predictor(net::HostResolver* host_resolver, base::TimeDelta max_queue_delay_ms, size_t max_concurrent, @@ -73,14 +83,6 @@ class Predictor : public base::RefCountedThreadSafe<Predictor> { void Resolve(const GURL& url, UrlInfo::ResolutionMotivation motivation); - // Get latency benefit of the prefetch that we are navigating to. - bool AccruePrefetchBenefits(const GURL& referrer, - UrlInfo* navigation_info); - - // Instigate preresolution of any domains we predict will be needed after this - // navigation. - void PredictSubresources(const GURL& url); - // Instigate pre-connection to any URLs we predict will be needed after this // navigation (typically more-embedded resources on a page). void PredictFrameSubresources(const GURL& url); @@ -240,12 +242,6 @@ class Predictor : public base::RefCountedThreadSafe<Predictor> { // When true, we don't make new lookup requests. bool shutdown_; - // A list of successful events resulting from pre-fetching. - UrlInfo::DnsInfoTable dns_cache_hits_; - // A map of hosts that were evicted from our cache (after we prefetched them) - // and before the HTTP stack tried to look them up. - Results cache_eviction_map_; - // The number of concurrent lookups currently allowed. const size_t max_concurrent_dns_lookups_; diff --git a/chrome/browser/net/predictor_api.cc b/chrome/browser/net/predictor_api.cc index d5f661dc..4e4569e 100644 --- a/chrome/browser/net/predictor_api.cc +++ b/chrome/browser/net/predictor_api.cc @@ -35,14 +35,14 @@ using base::TimeDelta; namespace chrome_browser_net { -static void DnsMotivatedPrefetch(const GURL& url, - UrlInfo::ResolutionMotivation motivation); +static void ResolveOnUIThread(const GURL& url, + UrlInfo::ResolutionMotivation motivation); static void DnsPrefetchMotivatedList(const UrlList& urls, - UrlInfo::ResolutionMotivation motivation); + UrlInfo::ResolutionMotivation motivation); -static UrlList GetPredictedUrlListAtStartup( - PrefService* user_prefs, PrefService* local_state); +static UrlList GetPredictedUrlListAtStartup(PrefService* user_prefs, + PrefService* local_state); // static const size_t PredictorInit::kMaxPrefetchConcurrentLookups = 8; @@ -186,6 +186,7 @@ void AnticipateUrl(const GURL& url, bool preconnectable) { last_prefetch_for_host = now; GURL canonical_url(CanonicalizeUrl(url)); + UrlInfo::ResolutionMotivation motivation(UrlInfo::OMNIBOX_MOTIVATED); if (predictor->preconnect_enabled() && preconnectable) { static base::TimeTicks last_keepalive; @@ -197,16 +198,16 @@ void AnticipateUrl(const GURL& url, bool preconnectable) { return; last_keepalive = now; - if (Preconnect::PreconnectOnUIThread(canonical_url)) - return; // Skip pre-resolution, since we'll open a connection. + Preconnect::PreconnectOnUIThread(canonical_url, motivation); + return; // Skip pre-resolution, since we'll open a connection. } // Perform at least DNS pre-resolution. - DnsMotivatedPrefetch(canonical_url, UrlInfo::OMNIBOX_MOTIVATED); + ResolveOnUIThread(canonical_url, motivation); } -static void DnsMotivatedPrefetch(const GURL& url, - UrlInfo::ResolutionMotivation motivation) { +static void ResolveOnUIThread(const GURL& url, + UrlInfo::ResolutionMotivation motivation) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); if (!dns_prefetch_enabled || NULL == predictor || !url.has_host()) return; @@ -225,24 +226,6 @@ static void DnsMotivatedPrefetch(const GURL& url, // helpful during the next chrome-startup. //------------------------------------------------------------------------------ -// This function determines if there was a saving by prefetching the hostname -// for which the navigation_info is supplied. -static bool AccruePrefetchBenefits(const GURL& referrer_url, - UrlInfo* navigation_info) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); - if (!dns_prefetch_enabled || NULL == predictor) - return false; - DCHECK(referrer_url == referrer_url.GetWithEmptyPath()); - return predictor->AccruePrefetchBenefits(referrer_url, navigation_info); -} - -void PredictSubresources(const GURL& url) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); - if (!dns_prefetch_enabled || NULL == predictor) - return; - predictor->PredictSubresources(url); -} - void PredictFrameSubresources(const GURL& url) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); if (!dns_prefetch_enabled || NULL == predictor) @@ -263,34 +246,32 @@ typedef std::map<int, UrlInfo> ObservedResolutionMap; // There will only be one instance ever created of the following Observer // class. The PrefetchObserver lives on the IO thread, and intercepts DNS -// resolutions made by the network stack. +// resolutions made by the network stack. This is only used to identify startup +// time resolutions (for re-resolution during our next process startup). class PrefetchObserver : public net::HostResolver::Observer { public: - typedef std::map<GURL, UrlInfo> FirstResolutionMap; + typedef std::set<GURL> FirstResolutions; // net::HostResolver::Observer implementation: virtual void OnStartResolution( int request_id, - const net::HostResolver::RequestInfo& request_info); - virtual void OnFinishResolutionWithStatus( - int request_id, - bool was_resolved, - const net::HostResolver::RequestInfo& request_info); - virtual void OnCancelResolution( - int request_id, - const net::HostResolver::RequestInfo& request_info); + const net::HostResolver::RequestInfo& request_info) {} + + virtual void OnFinishResolutionWithStatus(int id, bool was_resolved, + const net::HostResolver::RequestInfo& info); - void DnsGetFirstResolutionsHtml(std::string* output); + virtual void OnCancelResolution(int id, + const net::HostResolver::RequestInfo& info) {} + + void DnsGetFirstResolutionsHtml(std::string* output); void GetInitialDnsResolutionList(ListValue* startup_list); private: - void StartupListAppend(const UrlInfo& navigation_info); + void StartupListAppend(const std::string& host, int port); - // Map of pending resolutions seen by observer. - ObservedResolutionMap resolutions_; - // List of the first N hostname resolutions observed in this run. - FirstResolutionMap first_resolutions_; - // The number of hostnames we'll save for prefetching at next startup. + // List of the first N URL resolutions observed in this run. + FirstResolutions first_resolutions_; + // The number of URLs we'll save for pre-resolving at next startup. static const size_t kStartupResolutionCount = 10; }; @@ -300,101 +281,39 @@ static PrefetchObserver* g_prefetch_observer = NULL; //------------------------------------------------------------------------------ // Member definitions for above Observer class. -void PrefetchObserver::OnStartResolution( - int request_id, - const net::HostResolver::RequestInfo& request_info) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); - if (request_info.is_speculative()) - return; // One of our own requests. - if (!request_info.hostname().length()) - return; // PAC scripts may create queries without a hostname. - - UrlInfo navigation_info; - // TODO(jar): Remove hack which guestimates ssl via port number, and perhaps - // have actual URL passed down in request_info instead. - bool is_ssl(443 == request_info.port()); - std::string url_spec = is_ssl ? "https://" : "http://"; - url_spec += request_info.hostname(); - url_spec += ":"; - url_spec += IntToString(request_info.port()); - navigation_info.SetUrl(GURL(url_spec)); - navigation_info.SetStartedState(); - - // This entry will be deleted either by OnFinishResolutionWithStatus(), or - // by OnCancelResolution(). - resolutions_[request_id] = navigation_info; -} - void PrefetchObserver::OnFinishResolutionWithStatus( int request_id, bool was_resolved, const net::HostResolver::RequestInfo& request_info) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); + if (!was_resolved) + return; // Don't remember failures. if (request_info.is_speculative()) return; // One of our own requests. if (!request_info.hostname().length()) return; // PAC scripts may create queries without a hostname. - UrlInfo navigation_info; - size_t startup_count = 0; - { - ObservedResolutionMap::iterator it = resolutions_.find(request_id); - if (resolutions_.end() == it) { - NOTREACHED(); - return; - } - navigation_info = it->second; - resolutions_.erase(it); - startup_count = first_resolutions_.size(); - } - - navigation_info.SetFinishedState(was_resolved); // Get timing info - AccruePrefetchBenefits(CanonicalizeUrl(request_info.referrer()), - &navigation_info); - // Handle sub-resource resolutions now that the critical navigational - // resolution has completed. This prevents us from in any way delaying that - // navigational resolution. - std::string url_spec; - StringAppendF(&url_spec, "http%s://%s:%d", "", - request_info.hostname().c_str(), request_info.port()); - PredictSubresources(GURL(url_spec)); - - if (kStartupResolutionCount <= startup_count || !was_resolved) - return; - // TODO(jar): Don't add host to our list if it is a non-linked lookup, and - // instead rely on Referrers to pull this in automatically with the enclosing - // page load (once we start to persist elements of our referrer tree). - StartupListAppend(navigation_info); + StartupListAppend(request_info.hostname(), request_info.port()); } -void PrefetchObserver::OnCancelResolution( - int request_id, - const net::HostResolver::RequestInfo& request_info) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); - if (request_info.is_speculative()) - return; // One of our own requests. - if (!request_info.hostname().length()) - return; // PAC scripts may create queries without a hostname. - - // Remove the entry from |resolutions| that was added by OnStartResolution(). - ObservedResolutionMap::iterator it = resolutions_.find(request_id); - if (resolutions_.end() == it) { - NOTREACHED(); - return; - } - resolutions_.erase(it); -} - -void PrefetchObserver::StartupListAppend(const UrlInfo& navigation_info) { +void PrefetchObserver::StartupListAppend(const std::string& host, int port) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); if (!on_the_record_switch || NULL == predictor) return; if (kStartupResolutionCount <= first_resolutions_.size()) return; // Someone just added the last item. - if (ContainsKey(first_resolutions_, navigation_info.url())) - return; // We already have this hostname listed. - first_resolutions_[navigation_info.url()] = navigation_info; + + // TODO(jar): Switch to using speculative_interceptor_ instead of + // PrefetchObserver. + bool is_ssl(443 == port); + std::string url_spec = is_ssl ? "https://" : "http://"; + url_spec += host; + url_spec += ":"; + url_spec += IntToString(port); + + GURL url(url_spec); + first_resolutions_.insert(url); } void PrefetchObserver::GetInitialDnsResolutionList(ListValue* startup_list) { @@ -403,23 +322,26 @@ void PrefetchObserver::GetInitialDnsResolutionList(ListValue* startup_list) { startup_list->Clear(); DCHECK_EQ(0u, startup_list->GetSize()); startup_list->Append(new FundamentalValue(kPredictorStartupFormatVersion)); - for (FirstResolutionMap::iterator it = first_resolutions_.begin(); + for (FirstResolutions::iterator it = first_resolutions_.begin(); it != first_resolutions_.end(); ++it) { - DCHECK(it->first == CanonicalizeUrl(it->first)); - startup_list->Append(new StringValue(it->first.spec())); + DCHECK(*it == CanonicalizeUrl(*it)); + startup_list->Append(new StringValue(it->spec())); } } void PrefetchObserver::DnsGetFirstResolutionsHtml(std::string* output) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); - UrlInfo::DnsInfoTable resolution_list; + UrlInfo::UrlInfoTable resolution_list; { - for (FirstResolutionMap::iterator it(first_resolutions_.begin()); + for (FirstResolutions::iterator it(first_resolutions_.begin()); it != first_resolutions_.end(); it++) { - resolution_list.push_back(it->second); + UrlInfo info; + info.SetUrl(*it); + // TODO(jar): Need to set time of info.GetDuration(); + resolution_list.push_back(info); } } UrlInfo::GetHtmlTable(resolution_list, diff --git a/chrome/browser/net/predictor_api.h b/chrome/browser/net/predictor_api.h index 9f91634..90603a3 100644 --- a/chrome/browser/net/predictor_api.h +++ b/chrome/browser/net/predictor_api.h @@ -59,10 +59,6 @@ void AnticipateUrl(const GURL& url, bool preconnectable); void PredictorGetHtmlInfo(std::string* output); //------------------------------------------------------------------------------ -// When we navigate, we may know in advance some other URLs that will need to -// be resolved. This function initiates those side effects. -void PredictSubresources(const GURL& url); - // When we navigate to a frame that may contain embedded resources, we may know // in advance some other URLs that will need to be connected to (via TCP and // sometimes SSL). This function initiates those connections diff --git a/chrome/browser/net/predictor_unittest.cc b/chrome/browser/net/predictor_unittest.cc index d60961e..9a3eea3 100644 --- a/chrome/browser/net/predictor_unittest.cc +++ b/chrome/browser/net/predictor_unittest.cc @@ -115,70 +115,6 @@ TEST_F(PredictorTest, StartupShutdownTest) { testing_master->Shutdown(); } -TEST_F(PredictorTest, BenefitLookupTest) { - scoped_refptr<Predictor> testing_master = new Predictor( - host_resolver_, - default_max_queueing_delay_, - PredictorInit::kMaxPrefetchConcurrentLookups, - false); - - GURL goog("http://www.google.com:80"), - goog2("http://gmail.google.com.com:80"), - goog3("http://mail.google.com:80"), - goog4("http://gmail.com:80"); - UrlInfo goog_info, goog2_info, goog3_info, goog4_info; - - // Simulate getting similar names from a network observer - goog_info.SetUrl(goog); - goog2_info.SetUrl(goog2); - goog3_info.SetUrl(goog3); - goog4_info.SetUrl(goog4); - - goog_info.SetStartedState(); - goog2_info.SetStartedState(); - goog3_info.SetStartedState(); - goog4_info.SetStartedState(); - - goog_info.SetFinishedState(true); - goog2_info.SetFinishedState(true); - goog3_info.SetFinishedState(true); - goog4_info.SetFinishedState(true); - - UrlList names; - names.push_back(goog); - names.push_back(goog2); - names.push_back(goog3); - names.push_back(goog4); - - testing_master->ResolveList(names, UrlInfo::PAGE_SCAN_MOTIVATED); - - WaitForResolution(testing_master, names); - - EXPECT_TRUE(testing_master->WasFound(goog)); - EXPECT_TRUE(testing_master->WasFound(goog2)); - EXPECT_TRUE(testing_master->WasFound(goog3)); - EXPECT_TRUE(testing_master->WasFound(goog4)); - - // With the mock DNS, each of these should have taken some time, and hence - // shown a benefit (i.e., prefetch cost more than network access time). - - GURL referer; // Null host. - - // Simulate actual navigation, and acrue the benefit for "helping" the DNS - // part of the navigation. - EXPECT_TRUE(testing_master->AccruePrefetchBenefits(referer, &goog_info)); - EXPECT_TRUE(testing_master->AccruePrefetchBenefits(referer, &goog2_info)); - EXPECT_TRUE(testing_master->AccruePrefetchBenefits(referer, &goog3_info)); - EXPECT_TRUE(testing_master->AccruePrefetchBenefits(referer, &goog4_info)); - - // Benefits can ONLY be reported once (for the first navigation). - EXPECT_FALSE(testing_master->AccruePrefetchBenefits(referer, &goog_info)); - EXPECT_FALSE(testing_master->AccruePrefetchBenefits(referer, &goog2_info)); - EXPECT_FALSE(testing_master->AccruePrefetchBenefits(referer, &goog3_info)); - EXPECT_FALSE(testing_master->AccruePrefetchBenefits(referer, &goog4_info)); - - testing_master->Shutdown(); -} TEST_F(PredictorTest, ShutdownWhenResolutionIsPendingTest) { scoped_refptr<net::WaitingHostResolverProc> resolver_proc = @@ -327,7 +263,7 @@ static ListValue* FindSerializationMotivation( CHECK_LT(0u, referral_list.GetSize()); // Room for version. int format_version = -1; CHECK(referral_list.GetInteger(0, &format_version)); - CHECK_EQ(Predictor::DNS_REFERRER_VERSION, format_version); + CHECK_EQ(Predictor::PREDICTOR_REFERRER_VERSION, format_version); ListValue* motivation_list(NULL); for (size_t i = 1; i < referral_list.GetSize(); ++i) { referral_list.GetList(i, &motivation_list); @@ -342,17 +278,16 @@ static ListValue* FindSerializationMotivation( // Create a new empty serialization list. static ListValue* NewEmptySerializationList() { ListValue* list = new ListValue; - list->Append(new FundamentalValue(Predictor::DNS_REFERRER_VERSION)); + list->Append(new FundamentalValue(Predictor::PREDICTOR_REFERRER_VERSION)); return list; } -// Add a motivating_host and a subresource_host to a serialized list, using +// Add a motivating_url and a subresource_url to a serialized list, using // this given latency. This is a helper function for quickly building these // lists. static void AddToSerializedList(const GURL& motivation, const GURL& subresource, - int latency, - double rate, + double use_rate, ListValue* referral_list ) { // Find the motivation if it is already used. ListValue* motivation_list = FindSerializationMotivation(motivation, @@ -377,8 +312,7 @@ static void AddToSerializedList(const GURL& motivation, // existing value(s) will be added to the referrer. subresource_list->Append(new StringValue(subresource.spec())); - subresource_list->Append(new FundamentalValue(latency)); - subresource_list->Append(new FundamentalValue(rate)); + subresource_list->Append(new FundamentalValue(use_rate)); } static const int kLatencyNotFound = -1; @@ -386,12 +320,11 @@ static const int kLatencyNotFound = -1; // For a given motivation, and subresource, find what latency is currently // listed. This assume a well formed serialization, which has at most one such // entry for any pair of names. If no such pair is found, then return false. -// Data is written into rate and latency arguments. +// Data is written into use_rate arguments. static bool GetDataFromSerialization(const GURL& motivation, const GURL& subresource, const ListValue& referral_list, - double* rate, - int* latency) { + double* use_rate) { ListValue* motivation_list = FindSerializationMotivation(motivation, referral_list); if (!motivation_list) @@ -401,8 +334,7 @@ static bool GetDataFromSerialization(const GURL& motivation, for (size_t i = 0; i < subresource_list->GetSize();) { std::string url_spec; EXPECT_TRUE(subresource_list->GetString(i++, &url_spec)); - EXPECT_TRUE(subresource_list->GetInteger(i++, latency)); - EXPECT_TRUE(subresource_list->GetReal(i++, rate)); + EXPECT_TRUE(subresource_list->GetReal(i++, use_rate)); if (subresource == GURL(url_spec)) { return true; } @@ -423,7 +355,7 @@ TEST_F(PredictorTest, ReferrerSerializationNilTest) { EXPECT_EQ(1U, referral_list->GetSize()); EXPECT_FALSE(GetDataFromSerialization( GURL("http://a.com:79"), GURL("http://b.com:78"), - *referral_list.get(), NULL, NULL)); + *referral_list.get(), NULL)); predictor->Shutdown(); } @@ -438,25 +370,21 @@ TEST_F(PredictorTest, ReferrerSerializationSingleReferrerTest) { false); const GURL motivation_url("http://www.google.com:91"); const GURL subresource_url("http://icons.google.com:90"); - const int kLatency = 3; - const double kRate = 23.4; + const double kUseRate = 23.4; scoped_ptr<ListValue> referral_list(NewEmptySerializationList()); AddToSerializedList(motivation_url, subresource_url, - kLatency, kRate, referral_list.get()); + kUseRate, referral_list.get()); predictor->DeserializeReferrers(*referral_list.get()); ListValue recovered_referral_list; predictor->SerializeReferrers(&recovered_referral_list); EXPECT_EQ(2U, recovered_referral_list.GetSize()); - int latency; double rate; EXPECT_TRUE(GetDataFromSerialization( - motivation_url, subresource_url, recovered_referral_list, &rate, - &latency)); - EXPECT_EQ(rate, kRate); - EXPECT_EQ(latency, kLatency); + motivation_url, subresource_url, recovered_referral_list, &rate)); + EXPECT_EQ(rate, kUseRate); predictor->Shutdown(); } @@ -470,99 +398,73 @@ TEST_F(PredictorTest, ReferrerSerializationTrimTest) { GURL motivation_url("http://www.google.com:110"); GURL icon_subresource_url("http://icons.google.com:111"); - const int kLatencyIcon = 10; - const double kRateIcon = 0.; // User low rate, so latency will dominate. + const double kRateIcon = 16.0 * Predictor::kPersistWorthyExpectedValue; GURL img_subresource_url("http://img.google.com:118"); - const int kLatencyImg = 3; - const double kRateImg = 0.; + const double kRateImg = 8.0 * Predictor::kPersistWorthyExpectedValue; scoped_ptr<ListValue> referral_list(NewEmptySerializationList()); AddToSerializedList( - motivation_url, icon_subresource_url, - kLatencyIcon, kRateIcon, referral_list.get()); + motivation_url, icon_subresource_url, kRateIcon, referral_list.get()); AddToSerializedList( - motivation_url, img_subresource_url, - kLatencyImg, kRateImg, referral_list.get()); + motivation_url, img_subresource_url, kRateImg, referral_list.get()); predictor->DeserializeReferrers(*referral_list.get()); ListValue recovered_referral_list; predictor->SerializeReferrers(&recovered_referral_list); EXPECT_EQ(2U, recovered_referral_list.GetSize()); - int latency; double rate; EXPECT_TRUE(GetDataFromSerialization( motivation_url, icon_subresource_url, recovered_referral_list, - &rate, &latency)); - EXPECT_EQ(latency, kLatencyIcon); + &rate)); EXPECT_EQ(rate, kRateIcon); EXPECT_TRUE(GetDataFromSerialization( - motivation_url, img_subresource_url, recovered_referral_list, - &rate, &latency)); - EXPECT_EQ(latency, kLatencyImg); + motivation_url, img_subresource_url, recovered_referral_list, &rate)); EXPECT_EQ(rate, kRateImg); - // Each time we Trim, the latency figures should reduce by a factor of two, - // until they both are 0, an then a trim will delete the whole entry. + // Each time we Trim, the user_rate figures should reduce by a factor of two, + // until they both are small, an then a trim will delete the whole entry. predictor->TrimReferrers(); predictor->SerializeReferrers(&recovered_referral_list); EXPECT_EQ(2U, recovered_referral_list.GetSize()); EXPECT_TRUE(GetDataFromSerialization( - motivation_url, icon_subresource_url, recovered_referral_list, - &rate, &latency)); - EXPECT_EQ(latency, kLatencyIcon / 2); - EXPECT_EQ(rate, kRateIcon); + motivation_url, icon_subresource_url, recovered_referral_list, &rate)); + EXPECT_EQ(rate, kRateIcon / 2); EXPECT_TRUE(GetDataFromSerialization( - motivation_url, img_subresource_url, recovered_referral_list, - &rate, &latency)); - EXPECT_EQ(latency, kLatencyImg / 2); - EXPECT_EQ(rate, kRateImg); + motivation_url, img_subresource_url, recovered_referral_list, &rate)); + EXPECT_EQ(rate, kRateImg / 2); predictor->TrimReferrers(); predictor->SerializeReferrers(&recovered_referral_list); EXPECT_EQ(2U, recovered_referral_list.GetSize()); EXPECT_TRUE(GetDataFromSerialization( - motivation_url, icon_subresource_url, recovered_referral_list, - &rate, &latency)); - EXPECT_EQ(latency, kLatencyIcon / 4); - EXPECT_EQ(rate, kRateIcon); - // Img is down to zero, but we don't delete it yet. + motivation_url, icon_subresource_url, recovered_referral_list, &rate)); + EXPECT_EQ(rate, kRateIcon / 4); EXPECT_TRUE(GetDataFromSerialization( - motivation_url, img_subresource_url, recovered_referral_list, - &rate, &latency)); - EXPECT_EQ(kLatencyImg / 4, 0); - EXPECT_EQ(latency, kLatencyImg / 4); - EXPECT_EQ(rate, kRateImg); + motivation_url, img_subresource_url, recovered_referral_list, &rate)); + EXPECT_EQ(rate, kRateImg / 4); predictor->TrimReferrers(); predictor->SerializeReferrers(&recovered_referral_list); EXPECT_EQ(2U, recovered_referral_list.GetSize()); EXPECT_TRUE(GetDataFromSerialization( - motivation_url, icon_subresource_url, recovered_referral_list, - &rate, &latency)); - EXPECT_EQ(latency, kLatencyIcon / 8); - EXPECT_EQ(rate, kRateIcon); + motivation_url, icon_subresource_url, recovered_referral_list, &rate)); + EXPECT_EQ(rate, kRateIcon / 8); - // Img is down to zero, but we don't delete it yet. - EXPECT_TRUE(GetDataFromSerialization( - motivation_url, img_subresource_url, recovered_referral_list, - &rate, &latency)); - EXPECT_EQ(kLatencyImg / 8, 0); - EXPECT_EQ(latency, kLatencyImg / 8); - EXPECT_EQ(rate, kRateImg); + // Img is below threshold, and so it gets deleted. + EXPECT_FALSE(GetDataFromSerialization( + motivation_url, img_subresource_url, recovered_referral_list, &rate)); predictor->TrimReferrers(); predictor->SerializeReferrers(&recovered_referral_list); // Icon is also trimmed away, so entire set gets discarded. EXPECT_EQ(1U, recovered_referral_list.GetSize()); EXPECT_FALSE(GetDataFromSerialization( - motivation_url, icon_subresource_url, recovered_referral_list, - &rate, &latency)); + motivation_url, icon_subresource_url, recovered_referral_list, &rate)); EXPECT_FALSE(GetDataFromSerialization( - motivation_url, img_subresource_url, recovered_referral_list, - &rate, &latency)); + motivation_url, img_subresource_url, recovered_referral_list, &rate)); predictor->Shutdown(); } diff --git a/chrome/browser/net/referrer.cc b/chrome/browser/net/referrer.cc index 936d25b..b5d8c82 100644 --- a/chrome/browser/net/referrer.cc +++ b/chrome/browser/net/referrer.cc @@ -7,23 +7,24 @@ #include <limits.h> #include "base/logging.h" +#include "chrome/browser/net/predictor.h" namespace chrome_browser_net { //------------------------------------------------------------------------------ // Smoothing parameter for updating subresource_use_rate_. -// We always combine our old expected value, weighted by some factor, with the -// new expected value Enew. The new "expected value" is the number of actual -// connections made due to the curernt navigations. -// This means the formula (in a concise form) is: -// Eupdated = Eold * W + Enew * (1 - W) +// We always combine our old expected value, weighted by some factor W (we use +// kWeightingForOldExpectedValue), with the new expected value Enew. The new +// "expected value" is the number of actual connections made due to the current +// navigations. // That means that IF we end up needing to connect, we should apply the formula: -// Pupdated = Pold * W + Enew * (1 - W) -// If we visit the containing url, but don't end up needing a connection: -// Pupdated = Pold * W -// To achive the above upating algorithm, we end up doing the multiplication -// by W every time we contemplate doing a preconneciton (i.e., when we navigate +// Eupdated = Eold * W + Enew * (1 - W) +// If we visit the containing url, but don't end up needing a connection, then +// Enew == 0, so we use the formula: +// Eupdated = Eold * W +// To achieve the above updating algorithm, we end up doing the multiplication +// by W every time we contemplate doing a preconnection (i.e., when we navigate // to the containing URL, and consider doing a preconnection), and then IFF we // learn that we really needed a connection to the subresource, we complete the // above algorithm by adding the (1 - W) for each connection we make. @@ -32,13 +33,14 @@ namespace chrome_browser_net { // 1.0. static const double kWeightingForOldExpectedValue = 0.66; -// The expected value needed before we actually do a preconnection. -static const double kPreconnectWorthyExpectedValue = 0.7; - -// The expected value that we'll need a preconnection when we first see the -// subresource getting fetched. Very conservative is 0.0, which will mean that -// we have to wait for a while before using preconnection... but we do persist -// results, so we'll have the learned answer in the long run. +// To estimate the expected value of the number of connections that we'll need +// when a referrer is navigated to, we start with the following rather low +// initial value. Each time we do indeed (again) need the subresource, this +// value will get increased. Each time we navigate to the refererrer but never +// end up needing this subresource, the value will decrease. +// Very conservative is 0.0, which will mean that we have to wait for a while +// before doing much speculative acvtivity... but we do persist results, so +// we'll save the asymptotic (correct?) learned answer in the long run. static const double kInitialExpectedValue = 0.0; // static @@ -71,71 +73,43 @@ void Referrer::SuggestHost(const GURL& url) { void Referrer::DeleteLeastUseful() { // Find the item with the lowest value. Most important is preconnection_rate, - // next is latency savings, and last is lifetime (age). + // and least is lifetime (age). GURL least_useful_url; double lowest_rate_seen = 0.0; // We use longs for durations because we will use multiplication on them. - int64 lowest_latency_seen = 0; // Duration in milliseconds. int64 least_useful_lifetime = 0; // Duration in milliseconds. const base::Time kNow(base::Time::Now()); // Avoid multiple calls. for (SubresourceMap::iterator it = begin(); it != end(); ++it) { int64 lifetime = (kNow - it->second.birth_time()).InMilliseconds(); - int64 latency = it->second.latency().InMilliseconds(); double rate = it->second.subresource_use_rate(); if (least_useful_url.has_host()) { if (rate > lowest_rate_seen) continue; - if (!latency && !lowest_latency_seen) { - // Older name is less useful. - if (lifetime <= least_useful_lifetime) - continue; - } else { - // Compare the ratios: - // latency/lifetime - // vs. - // lowest_latency_seen/least_useful_lifetime - // by cross multiplying (to avoid integer division hassles). Overflow's - // won't happen until both latency and lifetime pass about 49 days. - if (latency * least_useful_lifetime > - lowest_latency_seen * lifetime) { - continue; - } - } + if (lifetime <= least_useful_lifetime) + continue; } least_useful_url = it->first; lowest_rate_seen = rate; - lowest_latency_seen = latency; least_useful_lifetime = lifetime; } - erase(least_useful_url); - // Note: there is a small chance that we will discard a least_useful_url - // that is currently being prefetched because it *was* in this referer list. - // In that case, when a benefit appears in AccrueValue() below, we are careful - // to check before accessing the member. -} - -void Referrer::AccrueValue(const base::TimeDelta& delta, - const GURL& url) { - SubresourceMap::iterator it = find(url); - // Be careful that we weren't evicted from this referrer in DeleteLeastUseful. - if (it != end()) - it->second.AccrueValue(delta); + if (least_useful_url.has_host()) + erase(least_useful_url); } bool Referrer::Trim() { - bool has_some_latency_left = false; + std::vector<GURL> discarded_urls; for (SubresourceMap::iterator it = begin(); it != end(); ++it) - if (it->second.Trim()) - has_some_latency_left = true; - return has_some_latency_left; + if (!it->second.Trim()) + discarded_urls.push_back(it->first); + for (size_t i = 0; i < discarded_urls.size(); ++i) + erase(discarded_urls[i]); + return size() > 0; } bool ReferrerValue::Trim() { - int64 latency_ms = latency_.InMilliseconds() / 2; - latency_ = base::TimeDelta::FromMilliseconds(latency_ms); - return latency_ms > 0 || - subresource_use_rate_ > kPreconnectWorthyExpectedValue / 2; + subresource_use_rate_ /= 2.0; + return subresource_use_rate_ > Predictor::kPersistWorthyExpectedValue; } @@ -148,22 +122,17 @@ void Referrer::Deserialize(const Value& value) { std::string url_spec; if (!subresource_list->GetString(index++, &url_spec)) return; - int latency_ms; - if (!subresource_list->GetInteger(index++, &latency_ms)) - return; double rate; if (!subresource_list->GetReal(index++, &rate)) return; GURL url(url_spec); - base::TimeDelta latency = base::TimeDelta::FromMilliseconds(latency_ms); // TODO(jar): We could be more direct, and change birth date or similar to // show that this is a resurrected value we're adding in. I'm not yet sure // of how best to optimize the learning and pruning (Trim) algorithm at this // level, so for now, we just suggest subresources, which leaves them all // with the same birth date (typically start of process). SuggestHost(url); - AccrueValue(latency, url); (*this)[url].SetSubresourceUseRate(rate); } } @@ -172,21 +141,10 @@ Value* Referrer::Serialize() const { ListValue* subresource_list(new ListValue); for (const_iterator it = begin(); it != end(); ++it) { StringValue* url_spec(new StringValue(it->first.spec())); - int latency_integer = static_cast<int>(it->second.latency(). - InMilliseconds()); - // Watch out for overflow in the above static_cast! Check to see if we went - // negative, and just use a "big" value. The value seems unimportant once - // we get to such high latencies. Probable cause of high latency is a bug - // in other code, so also do a DCHECK. - DCHECK_GE(latency_integer, 0); - if (latency_integer < 0) - latency_integer = INT_MAX; - FundamentalValue* latency(new FundamentalValue(latency_integer)); FundamentalValue* rate(new FundamentalValue( it->second.subresource_use_rate())); subresource_list->Append(url_spec); - subresource_list->Append(latency); subresource_list->Append(rate); } return subresource_list; @@ -198,6 +156,7 @@ ReferrerValue::ReferrerValue() : birth_time_(base::Time::Now()), navigation_count_(0), preconnection_count_(0), + preresolution_count_(0), subresource_use_rate_(kInitialExpectedValue) { } @@ -208,15 +167,12 @@ void ReferrerValue::SubresourceIsNeeded() { subresource_use_rate_ += 1 - kWeightingForOldExpectedValue; } -bool ReferrerValue::IsPreconnectWorthDoing() { - bool preconnecting = kPreconnectWorthyExpectedValue < subresource_use_rate_; - if (preconnecting) - ++preconnection_count_; +void ReferrerValue::ReferrerWasObserved() { subresource_use_rate_ *= kWeightingForOldExpectedValue; // Note: the use rate is temporarilly possibly incorect, as we need to find // out if we really end up connecting. This will happen in a few hundred // milliseconds (when content arrives, etc.). - return preconnecting; + // Value of subresource_use_rate_ should be sampled before this call. } } // namespace chrome_browser_net diff --git a/chrome/browser/net/referrer.h b/chrome/browser/net/referrer.h index 267648a..2e43f89 100644 --- a/chrome/browser/net/referrer.h +++ b/chrome/browser/net/referrer.h @@ -38,28 +38,32 @@ class ReferrerValue { // Used during deserialization. void SetSubresourceUseRate(double rate) { subresource_use_rate_ = rate; } - base::TimeDelta latency() const { return latency_; } base::Time birth_time() const { return birth_time_; } - void AccrueValue(const base::TimeDelta& delta) { latency_ += delta; } - // Record the fact that we navigated to the associated subresource URL. + // Record the fact that we navigated to the associated subresource URL. This + // will increase the value of the expected subresource_use_rate_ void SubresourceIsNeeded(); - // Evaluate if it is worth making this preconnection, and return true if it - // seems worthwhile. As a side effect, we also tally the proconnection for - // statistical purposes only. - bool IsPreconnectWorthDoing(); + // Record the fact that the referrer of this subresource was observed. This + // will diminish the expected subresource_use_rate_ (and will only be + // counteracted later if we really needed this subresource as a consequence + // of our associated referrer.) + void ReferrerWasObserved(); int64 navigation_count() const { return navigation_count_; } - int64 preconnection_count() const { return preconnection_count_; } double subresource_use_rate() const { return subresource_use_rate_; } - // Reduce the latency figure by a factor of 2, and return true if any latency - // remains. + int64 preconnection_count() const { return preconnection_count_; } + void IncrementPreconnectionCount() { ++preconnection_count_; } + + int64 preresolution_count() const { return preresolution_count_; } + void preresolution_increment() { ++preresolution_count_; } + + // Reduce the latency figure by a factor of 2, and return true if it still has + // subresources that could potentially be used. bool Trim(); private: - base::TimeDelta latency_; // Accumulated DNS resolution latency savings. const base::Time birth_time_; // The number of times this item was navigated to with the fixed referrer. @@ -69,7 +73,12 @@ class ReferrerValue { // referrer. int64 preconnection_count_; - // A smoothed estimate of the probability that a connection will be needed. + // The number of times this item was pre-resolved (via DNS) as a consequence + // of its referrer. + int64 preresolution_count_; + + // A smoothed estimate of the expected number of connections that will be made + // to this subresource. double subresource_use_rate_; }; @@ -99,14 +108,9 @@ class Referrer : public SubresourceMap { // discarded to make room for this insertion. void SuggestHost(const GURL& url); - // Record additional usefulness of having this url in the list. - // Value is expressed as positive latency of amount delta. - void AccrueValue(const base::TimeDelta& delta, - const GURL& url); - - // Trim the Referrer, by first diminishing (scaling down) the latency for each - // ReferredValue. - // Returns true if there are any referring names with some latency left. + // Trim the Referrer, by first diminishing (scaling down) the subresource + // use expectation for each ReferredValue. + // Returns true if there are any referring names left. bool Trim(); // Provide methods for persisting, and restoring contents into a Value class. diff --git a/chrome/browser/net/url_info.cc b/chrome/browser/net/url_info.cc index fab27ad..a92f5a2 100644 --- a/chrome/browser/net/url_info.cc +++ b/chrome/browser/net/url_info.cc @@ -131,11 +131,6 @@ void UrlInfo::SetFoundState() { resolve_duration_, kMaxNonNetworkDnsLookupDuration, TimeDelta::FromMinutes(15), 100); } - - // Record potential beneficial time, and maybe we'll get a cache hit. - // We keep the maximum, as the warming we did earlier may still be - // helping with a cache upstream in DNS resolution. - benefits_remaining_ = std::max(resolve_duration_, benefits_remaining_); } sequence_number_ = sequence_counter++; DLogResultsStats("DNS PrefetchFound"); @@ -147,29 +142,11 @@ void UrlInfo::SetNoSuchNameState() { resolve_duration_ = GetDuration(); if (kMaxNonNetworkDnsLookupDuration <= resolve_duration_) { DHISTOGRAM_TIMES("DNS.PrefetchNotFoundName", resolve_duration_); - // Record potential beneficial time, and maybe we'll get a cache hit. - benefits_remaining_ = std::max(resolve_duration_, benefits_remaining_); } sequence_number_ = sequence_counter++; DLogResultsStats("DNS PrefetchNotFound"); } -void UrlInfo::SetStartedState() { - DCHECK(PENDING == state_); - state_ = STARTED; - queue_duration_ = resolve_duration_ = TimeDelta(); // 0ms. - SetMotivation(NO_PREFETCH_MOTIVATION); - GetDuration(); // Set time. -} - -void UrlInfo::SetFinishedState(bool was_resolved) { - DCHECK(STARTED == state_); - state_ = was_resolved ? FINISHED : FINISHED_UNRESOLVED; - resolve_duration_ = GetDuration(); - // TODO(jar): Sequence number should be incremented in prefetched HostInfo. - DLogResultsStats("DNS HTTP Finished"); -} - void UrlInfo::SetUrl(const GURL& url) { if (url_.is_empty()) // Not yet initialized. url_ = url; @@ -195,65 +172,12 @@ bool UrlInfo::IsStillCached() const { return time_since_resolution < kCacheExpirationDuration; } -// Compare the actual navigation DNS latency found in navigation_info, to the -// previously prefetched info. -DnsBenefit UrlInfo::AccruePrefetchBenefits(UrlInfo* navigation_info) { - DCHECK(FINISHED == navigation_info->state_ || - FINISHED_UNRESOLVED == navigation_info->state_); - DCHECK(navigation_info->url() == url_); - - if ((0 == benefits_remaining_.InMilliseconds()) || - (FOUND != state_ && NO_SUCH_NAME != state_)) { - if (FINISHED == navigation_info->state_) - UMA_HISTOGRAM_LONG_TIMES("DNS.IndependentNavigation", - navigation_info->resolve_duration_); - else - UMA_HISTOGRAM_LONG_TIMES("DNS.IndependentFailedNavigation", - navigation_info->resolve_duration_); - return PREFETCH_NO_BENEFIT; - } - - TimeDelta benefit = benefits_remaining_ - navigation_info->resolve_duration_; - navigation_info->benefits_remaining_ = benefits_remaining_; - benefits_remaining_ = TimeDelta(); // We used up all our benefits here. - - navigation_info->motivation_ = motivation_; - if (LEARNED_REFERAL_MOTIVATED == motivation_ || - STATIC_REFERAL_MOTIVATED == motivation_) - navigation_info->referring_url_ = referring_url_; - - if (navigation_info->resolve_duration_ > kMaxNonNetworkDnsLookupDuration) { - // Our precache effort didn't help since HTTP stack hit the network. - UMA_HISTOGRAM_LONG_TIMES("DNS.PrefetchCacheEvictionL", resolve_duration_); - DLogResultsStats("DNS PrefetchCacheEviction"); - return PREFETCH_CACHE_EVICTION; - } - - if (NO_SUCH_NAME == state_) { - UMA_HISTOGRAM_LONG_TIMES("DNS.PrefetchNegativeHitL", benefit); - DLogResultsStats("DNS PrefetchNegativeHit"); - return PREFETCH_NAME_NONEXISTANT; - } - - DCHECK_EQ(FOUND, state_); - if (LEARNED_REFERAL_MOTIVATED == motivation_ || - STATIC_REFERAL_MOTIVATED == motivation_) { - UMA_HISTOGRAM_TIMES("DNS.PrefetchReferredPositiveHit", benefit); - DLogResultsStats("DNS PrefetchReferredPositiveHit"); - } else { - UMA_HISTOGRAM_LONG_TIMES("DNS.PrefetchPositiveHitL", benefit); - DLogResultsStats("DNS PrefetchPositiveHit"); - } - return PREFETCH_NAME_FOUND; -} - void UrlInfo::DLogResultsStats(const char* message) const { if (!detailed_logging_enabled) return; DLOG(INFO) << "\t" << message << "\tq=" << queue_duration().InMilliseconds() << "ms,\tr=" << resolve_duration().InMilliseconds() << "ms\tp=" - << benefits_remaining_.InMilliseconds() << "ms\tseq=" << sequence_number_ << "\t" << url_.spec(); } @@ -329,7 +253,7 @@ static std::string HoursMinutesSeconds(int seconds) { } // static -void UrlInfo::GetHtmlTable(const DnsInfoTable host_infos, +void UrlInfo::GetHtmlTable(const UrlInfoTable host_infos, const char* description, const bool brief, std::string* output) { @@ -344,53 +268,29 @@ void UrlInfo::GetHtmlTable(const DnsInfoTable host_infos, return; } - const char* row_format = "<tr align=right><td>%s</td>" - "<td>%d</td><td>%d</td><td>%s</td><td>%s</td></tr>"; + output->append("<br><table border=1>" + "<tr><th>Host name</th>" + "<th>How long ago<br>(HH:MM:SS)</th>" + "<th>Motivation</th>" + "</tr>"); - output->append("<br><table border=1>"); - StringAppendF(output, - "<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>", - "Host name", "Applicable Prefetch<br>Time (ms)", - "Recent Resolution<br>Time(ms)", "How long ago<br>(HH:MM:SS)", - "Motivation"); + const char* row_format = "<tr align=right><td>%s</td>" // Host name. + "<td>%s</td>" // How long ago. + "<td>%s</td>" // Motivation. + "</tr>"; // Print bulk of table, and gather stats at same time. - MinMaxAverage queue, resolve, preresolve, when; + MinMaxAverage queue, when; TimeTicks current_time = TimeTicks::Now(); - for (DnsInfoTable::const_iterator it(host_infos.begin()); + for (UrlInfoTable::const_iterator it(host_infos.begin()); it != host_infos.end(); it++) { queue.sample((it->queue_duration_.InMilliseconds())); StringAppendF(output, row_format, RemoveJs(it->url_.spec()).c_str(), - preresolve.sample((it->benefits_remaining_.InMilliseconds())), - resolve.sample((it->resolve_duration_.InMilliseconds())), HoursMinutesSeconds(when.sample( (current_time - it->time_).InSeconds())).c_str(), it->GetAsciiMotivation().c_str()); } - // Write min, max, and average summary lines. - if (host_infos.size() > 2) { - output->append("<B>"); - StringAppendF(output, row_format, - "<b>---minimum---</b>", - preresolve.minimum(), resolve.minimum(), - HoursMinutesSeconds(when.minimum()).c_str(), ""); - StringAppendF(output, row_format, - "<b>---average---</b>", - preresolve.average(), resolve.average(), - HoursMinutesSeconds(when.average()).c_str(), ""); - StringAppendF(output, row_format, - "<b>standard deviation</b>", - preresolve.standard_deviation(), - resolve.standard_deviation(), "n/a", ""); - StringAppendF(output, row_format, - "<b>---maximum---</b>", - preresolve.maximum(), resolve.maximum(), - HoursMinutesSeconds(when.maximum()).c_str(), ""); - StringAppendF(output, row_format, - "<b>-----SUM-----</b>", - preresolve.sum(), resolve.sum(), "n/a", ""); - } output->append("</table>"); #ifdef DEBUG diff --git a/chrome/browser/net/url_info.h b/chrome/browser/net/url_info.h index e9ee771..c2af6e9 100644 --- a/chrome/browser/net/url_info.h +++ b/chrome/browser/net/url_info.h @@ -29,14 +29,6 @@ namespace chrome_browser_net { // Use command line switch to enable detailed logging. void EnablePredictorDetailedLog(bool enable); -enum DnsBenefit { - PREFETCH_NO_BENEFIT, // Prefetch never hit the network. Name was pre-cached. - PREFETCH_CACHE_EVICTION, // Prefetch used network, but so did HTTP stack. - PREFETCH_NAME_NONEXISTANT, // Valuable prefetch of "name not found" was used. - PREFETCH_NAME_FOUND, // Valuable prefetch was used. - PREFETCH_OBLIVIOUS // No prefetch attempt was even made. -}; - class UrlInfo { public: // Reasons for a domain to be resolved. @@ -55,6 +47,8 @@ class UrlInfo { // TODO(jar): Support STATIC_REFERAL_MOTIVATED API and integration. STATIC_REFERAL_MOTIVATED, // External database suggested this resolution. LEARNED_REFERAL_MOTIVATED, // Prior navigation taught us this resolution. + + MAX_MOTIVATED // Beyond all enums, for use in histogram bounding. }; enum DnsProcessingState { @@ -65,16 +59,13 @@ class UrlInfo { ASSIGNED_BUT_MARKED, // Needs to be deleted as soon as it's resolved. FOUND, // DNS resolution completed. NO_SUCH_NAME, // DNS resolution completed. - // When processed by the network stack during navigation, the states are: - STARTED, // Resolution has begun for a navigation. - FINISHED, // Resolution has completed for a navigation. - FINISHED_UNRESOLVED}; // No resolution found, so navigation will fail. + }; static const base::TimeDelta kMaxNonNetworkDnsLookupDuration; // The number of OS cache entries we can guarantee(?) before cache eviction // might likely take place. static const int kMaxGuaranteedDnsCacheSize = 50; - typedef std::vector<UrlInfo> DnsInfoTable; + typedef std::vector<UrlInfo> UrlInfoTable; static const base::TimeDelta kNullDuration; @@ -85,7 +76,6 @@ class UrlInfo { old_prequeue_state_(state_), resolve_duration_(kNullDuration), queue_duration_(kNullDuration), - benefits_remaining_(), sequence_number_(0), motivation_(NO_PREFETCH_MOTIVATION), was_linked_(false) { @@ -108,9 +98,6 @@ class UrlInfo { void SetPendingDeleteState(); void SetFoundState(); void SetNoSuchNameState(); - // The actual browsing resolution lifecycle. - void SetStartedState(); - void SetFinishedState(bool was_resolved); // Finish initialization. Must only be called once. void SetUrl(const GURL& url); @@ -136,13 +123,10 @@ class UrlInfo { base::TimeDelta resolve_duration() const { return resolve_duration_;} base::TimeDelta queue_duration() const { return queue_duration_;} - base::TimeDelta benefits_remaining() const { return benefits_remaining_; } - - DnsBenefit AccruePrefetchBenefits(UrlInfo* navigation_info); void DLogResultsStats(const char* message) const; - static void GetHtmlTable(const DnsInfoTable host_infos, + static void GetHtmlTable(const UrlInfoTable host_infos, const char* description, const bool brief, std::string* output); @@ -182,8 +166,6 @@ class UrlInfo { base::TimeDelta resolve_duration_; // Time spent in queue. base::TimeDelta queue_duration_; - // Unused potential benefits of a prefetch. - base::TimeDelta benefits_remaining_; int sequence_number_; // Used to calculate potential of cache eviction. static int sequence_counter; // Used to allocate sequence_number_'s. diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.cc b/chrome/browser/renderer_host/resource_dispatcher_host.cc index b9a11c7..7e53a5e 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host.cc +++ b/chrome/browser/renderer_host/resource_dispatcher_host.cc @@ -425,8 +425,11 @@ void ResourceDispatcherHost::BeginRequest( // EV certificate verification could be expensive. We don't want to spend // time performing EV certificate verification on all resources because // EV status is irrelevant to sub-frames and sub-resources. - if (request_data.resource_type == ResourceType::MAIN_FRAME) - load_flags |= net::LOAD_VERIFY_EV_CERT; + if (request_data.resource_type == ResourceType::MAIN_FRAME) { + load_flags |= net::LOAD_VERIFY_EV_CERT | net::LOAD_MAIN_FRAME; + } else if (request_data.resource_type == ResourceType::SUB_FRAME) { + load_flags |= net::LOAD_SUB_FRAME; + } request->set_load_flags(load_flags); request->set_context(context); request->set_priority(DetermineRequestPriority(request_data.resource_type)); diff --git a/net/base/load_flags_list.h b/net/base/load_flags_list.h index ff5e609..1125a3e 100644 --- a/net/base/load_flags_list.h +++ b/net/base/load_flags_list.h @@ -82,3 +82,12 @@ LOAD_FLAG(DO_NOT_SEND_AUTH_DATA, 1 << 18) // This should only be used for testing (set by HttpNetworkTransaction). LOAD_FLAG(IGNORE_ALL_CERT_ERRORS, 1 << 19) + +// Indicate that this is a top level frame, so that we don't assume it is a +// subresource and speculatively pre-connect or pre-resolve when a referring +// page is loaded. +LOAD_FLAG(MAIN_FRAME, 1 << 20) + +// Indicate that this is a sub frame, and hence it might have subresources that +// should be speculatively resolved, or even speculatively preconnected. +LOAD_FLAG(SUB_FRAME, 1 << 21) |