| Index: chrome/browser/net/predictor.cc
|
| diff --git a/chrome/browser/net/predictor.cc b/chrome/browser/net/predictor.cc
|
| index b7061c748b6479e801c1d9eacb63744ae752df38..d7d6a6eaf5fbaad5c4424ccf2bf45b47f0eaac8c 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,47 @@ 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::UI) ||
|
| + BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| +
|
| + if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
|
| + RecordPreconnectNavigationStatOnIOThread(url, original_url);
|
| + } else {
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO,
|
| + FROM_HERE,
|
| + base::Bind(&Predictor::RecordPreconnectNavigationStatOnIOThread,
|
| + base::Unretained(this), url, original_url));
|
| + }
|
| +}
|
| +
|
| +void Predictor::RecordPreconnectNavigationStatOnIOThread(
|
| + const GURL& url,
|
| + const GURL& original_url) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + preconnect_usage_->ObserveNavigation(url, original_url);
|
| }
|
|
|
| void Predictor::PredictFrameSubresources(const GURL& url,
|
|
|