Index: chrome/browser/net/predictor.cc |
diff --git a/chrome/browser/net/predictor.cc b/chrome/browser/net/predictor.cc |
index b7061c748b6479e801c1d9eacb63744ae752df38..d217650a34138f9531286dcae4bc5bd5222ad7b7 100644 |
--- a/chrome/browser/net/predictor.cc |
+++ b/chrome/browser/net/predictor.cc |
@@ -9,9 +9,11 @@ |
#include <set> |
#include <sstream> |
+#include "base/basictypes.h" |
#include "base/bind.h" |
#include "base/command_line.h" |
#include "base/compiler_specific.h" |
+#include "base/containers/mru_cache.h" |
#include "base/metrics/histogram.h" |
#include "base/prefs/pref_service.h" |
#include "base/stl_util.h" |
@@ -125,6 +127,129 @@ class Predictor::LookupRequest { |
DISALLOW_COPY_AND_ASSIGN(LookupRequest); |
}; |
+// This records UMAs for preconnect usage based on navigation URLs to |
+// gather precision/recall for user-event based preconnect triggers. |
+// Stats are gathered via two LRU caches that remembers all preconnect and |
+// user link navigations within the last N seconds. |
+// A preconnect trigger is considered as used iff a navigation including |
+// access to the preconnected host occurs within a time period specified by |
+// kMaxUnusedSocketLifetimeSecondsWithoutAGet. |
+class Predictor::PreconnectUsage { |
+ public: |
+ PreconnectUsage(); |
+ ~PreconnectUsage(); |
+ |
+ // Record a preconnect trigger to the url. |
+ void ObservePreconnect(const GURL& url); |
+ |
+ // Record a user link navigation to the url. |
+ void ObserveNavigation(const GURL& url, const GURL& original_url); |
+ |
+ private: |
+ // This tracks whether a preconnect was used in some navigation or not |
+ class PreconnectPrecisionStat { |
+ public: |
+ PreconnectPrecisionStat() |
+ : timestamp_(base::TimeTicks::Now()), |
+ was_used_(false) { |
+ } |
+ |
+ const base::TimeTicks& timestamp() { return timestamp_; } |
+ |
+ void set_was_used() { was_used_ = true; } |
+ bool was_used() const { return was_used_; } |
+ |
+ private: |
+ base::TimeTicks timestamp_; |
+ bool was_used_; |
+ }; |
+ |
+ typedef base::MRUCache<GURL, PreconnectPrecisionStat> MRUPreconnects; |
+ MRUPreconnects mru_preconnects_; |
+ |
+ // This tracks whether a navigation used a preconnected session or not |
+ class PreconnectRecallStat { |
+ public: |
+ PreconnectRecallStat() |
+ : timestamp_(base::TimeTicks::Now()), |
+ did_use_preconnect_(false) { |
+ } |
+ |
+ const base::TimeTicks& timestamp() { return timestamp_; } |
+ |
+ void set_did_use_preconnect() { did_use_preconnect_ = true; } |
+ bool did_use_preconnect() const { return did_use_preconnect_; } |
+ |
+ private: |
+ base::TimeTicks timestamp_; |
+ bool did_use_preconnect_; |
+ }; |
+ |
+ typedef base::MRUCache<GURL, PreconnectRecallStat> MRUNavigations; |
+ MRUNavigations mru_navigations_; |
+ |
+ // The longest time an entry can persist in mru_preconnect_/mru_navigations_ |
+ const base::TimeDelta max_duration_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(PreconnectUsage); |
+}; |
+ |
+Predictor::PreconnectUsage::PreconnectUsage() |
+ : mru_preconnects_(MRUPreconnects::NO_AUTO_EVICT), |
+ mru_navigations_(MRUNavigations::NO_AUTO_EVICT), |
+ max_duration_(base::TimeDelta::FromSeconds( |
+ Predictor::kMaxUnusedSocketLifetimeSecondsWithoutAGet)) { |
+} |
+ |
+Predictor::PreconnectUsage::~PreconnectUsage() {} |
+ |
+void Predictor::PreconnectUsage::ObservePreconnect(const GURL& url) { |
+ GURL canonical_url(Predictor::CanonicalizeUrl(url)); |
+ mru_preconnects_.Put(canonical_url, PreconnectPrecisionStat()); |
+} |
+ |
+void Predictor::PreconnectUsage::ObserveNavigation(const GURL& url, |
+ const GURL& original_url) { |
+ GURL canonical_url(Predictor::CanonicalizeUrl(url)); |
+ |
+ // Evict any overly old entries and record stats |
+ base::TimeTicks now = base::TimeTicks::Now(); |
+ |
+ MRUPreconnects::reverse_iterator eldestPreconnect = mru_preconnects_.rbegin(); |
+ while (!mru_preconnects_.empty()) { |
+ DCHECK(eldestPreconnect == mru_preconnects_.rbegin()); |
+ if (now - eldestPreconnect->second.timestamp() < max_duration_) |
+ break; |
+ |
+ UMA_HISTOGRAM_BOOLEAN("Net.PreconnectTriggerUsed", |
+ eldestPreconnect->second.was_used()); |
+ eldestPreconnect = mru_preconnects_.Erase(eldestPreconnect); |
+ } |
+ |
+ MRUNavigations::reverse_iterator eldestNavigation = mru_navigations_.rbegin(); |
+ while (!mru_navigations_.empty()) { |
+ DCHECK(eldestNavigation == mru_navigations_.rbegin()); |
+ if (now - eldestNavigation->second.timestamp() < max_duration_) |
+ break; |
+ |
+ UMA_HISTOGRAM_BOOLEAN("Net.PreconnectedNavigation", |
+ eldestNavigation->second.did_use_preconnect()); |
+ eldestNavigation = mru_navigations_.Erase(eldestNavigation); |
+ } |
+ |
+ // Record the preconnect trigger for the url as used if exist |
+ MRUNavigations::iterator itNavigation = mru_navigations_.Peek(original_url); |
+ if (itNavigation == mru_navigations_.end()) |
+ itNavigation = mru_navigations_.Put(original_url, PreconnectRecallStat()); |
+ |
+ MRUPreconnects::iterator itPreconnect = mru_preconnects_.Peek(canonical_url); |
+ bool wasPreconnected = (itPreconnect != mru_preconnects_.end()); |
+ if (wasPreconnected) { |
+ itPreconnect->second.set_was_used(); |
+ itNavigation->second.set_did_use_preconnect(); |
+ } |
+} |
+ |
Predictor::Predictor(bool preconnect_enabled) |
: url_request_context_getter_(NULL), |
predictor_enabled_(true), |
@@ -136,8 +261,6 @@ Predictor::Predictor(bool preconnect_enabled) |
host_resolver_(NULL), |
preconnect_enabled_(preconnect_enabled), |
consecutive_omnibox_preconnect_count_(0), |
- recent_preconnects_( |
- TimeDelta::FromSeconds(kMaxUnusedSocketLifetimeSecondsWithoutAGet)), |
next_trim_time_(base::TimeTicks::Now() + |
TimeDelta::FromHours(kDurationBetweenTrimmingsHours)) { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
@@ -667,6 +790,7 @@ void Predictor::FinalizeInitializationOnIOThread( |
predictor_enabled_ = predictor_enabled; |
initial_observer_.reset(new InitialObserver()); |
host_resolver_ = io_thread->globals()->host_resolver.get(); |
+ preconnect_usage_.reset(new PreconnectUsage()); |
// base::WeakPtrFactory instances need to be created and destroyed |
// on the same thread. The predictor lives on the IO thread and will die |
@@ -845,9 +969,6 @@ void Predictor::PreconnectUrl(const GURL& url, |
void Predictor::PreconnectUrlOnIOThread( |
const GURL& url, const GURL& first_party_for_cookies, |
UrlInfo::ResolutionMotivation motivation, int count) { |
- GURL canonical_url(CanonicalizeUrl(url)); |
- recent_preconnects_.SetRecentlySeen(canonical_url); |
- |
PreconnectOnIOThread(url, |
first_party_for_cookies, |
motivation, |
@@ -855,10 +976,30 @@ void Predictor::PreconnectUrlOnIOThread( |
url_request_context_getter_.get()); |
} |
-void Predictor::RecordPreconnectNavigationStats(const GURL& url) { |
- UMA_HISTOGRAM_BOOLEAN( |
- "Net.PreconnectedNavigation", |
- recent_preconnects_.WasRecentlySeen(url)); |
+void Predictor::RecordPreconnectTrigger(const GURL& url) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
+ BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ |
+ if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { |
+ RecordPreconnectTriggerOnIOThread(url); |
+ } else { |
+ BrowserThread::PostTask( |
+ BrowserThread::IO, |
+ FROM_HERE, |
+ base::Bind(&Predictor::RecordPreconnectTriggerOnIOThread, |
+ base::Unretained(this), url)); |
+ } |
+} |
+ |
+void Predictor::RecordPreconnectTriggerOnIOThread(const GURL& url) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ preconnect_usage_->ObservePreconnect(url); |
+} |
+ |
+void Predictor::RecordPreconnectNavigationStat(const GURL& url, |
+ const GURL& original_url) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ preconnect_usage_->ObserveNavigation(url, original_url); |
} |
void Predictor::PredictFrameSubresources(const GURL& url, |