Chromium Code Reviews| Index: chrome/browser/predictors/resource_prefetch_predictor.cc |
| diff --git a/chrome/browser/predictors/resource_prefetch_predictor.cc b/chrome/browser/predictors/resource_prefetch_predictor.cc |
| index a65f5d5967bff06fc78f6409c9c9fb3dcf4c4697..403ff80ab6a237c1b16d5559fe6291428960a4c7 100644 |
| --- a/chrome/browser/predictors/resource_prefetch_predictor.cc |
| +++ b/chrome/browser/predictors/resource_prefetch_predictor.cc |
| @@ -18,6 +18,7 @@ |
| #include "chrome/browser/history/url_database.h" |
| #include "chrome/browser/predictors/predictor_database.h" |
| #include "chrome/browser/predictors/predictor_database_factory.h" |
| +#include "chrome/browser/predictors/resource_prefetcher_manager.h" |
| #include "chrome/browser/prerender/prerender_field_trial.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/common/chrome_notification_types.h" |
| @@ -68,15 +69,6 @@ enum ResourceStatus { |
| namespace predictors { |
| -ResourcePrefetchPredictor::Config::Config() |
| - : max_navigation_lifetime_seconds(60), |
| - max_urls_to_track(500), |
| - min_url_visit_count(3), |
| - max_resources_per_entry(50), |
| - max_consecutive_misses(3), |
| - num_resources_assumed_prefetched(25) { |
| -} |
| - |
| ResourcePrefetchPredictor::URLRequestSummary::URLRequestSummary() |
| : resource_type(ResourceType::LAST_TYPE), |
| was_cached(false) { |
| @@ -101,22 +93,20 @@ ResourcePrefetchPredictor::UrlTableCacheValue::~UrlTableCacheValue() { |
| } |
| ResourcePrefetchPredictor::ResourcePrefetchPredictor( |
| - const Config& config, |
| + const ResourcePrefetchPredictorConfig& config, |
| Profile* profile) |
| : profile_(profile), |
| config_(config), |
| initialization_state_(NOT_INITIALIZED), |
| tables_(PredictorDatabaseFactory::GetForProfile( |
| - profile)->resource_prefetch_tables()) { |
| + profile)->resource_prefetch_tables()), |
| + results_map_deleter_(&results_map_) { |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| } |
| ResourcePrefetchPredictor::~ResourcePrefetchPredictor() { |
| -} |
| - |
| -// static |
| -bool ResourcePrefetchPredictor::IsEnabled(Profile* profile) { |
| - return prerender::IsSpeculativeResourcePrefetchingLearningEnabled(profile); |
| + prefetch_manager_->ShutdownOnUIThread(); |
| + prefetch_manager_ = NULL; |
| } |
| void ResourcePrefetchPredictor::LazilyInitialize() { |
| @@ -293,7 +283,6 @@ void ResourcePrefetchPredictor::RecordURLRequest( |
| } else if (initialization_state_ != INITIALIZED) { |
| return; |
| } |
| - DCHECK_EQ(INITIALIZED, initialization_state_); |
| CHECK_EQ(request.resource_type, ResourceType::MAIN_FRAME); |
| OnMainFrameRequest(request); |
| @@ -332,13 +321,58 @@ void ResourcePrefetchPredictor::OnMainFrameRequest( |
| // New empty navigation entry. |
| inflight_navigations_.insert(std::make_pair( |
| request.navigation_id, std::vector<URLRequestSummary>())); |
| + |
| + // If prefetching is enabled, and we can prefetch something, start |
| + // prefetching. |
| + if (!prefetch_manager_.get()) |
| + return; |
| + |
| + const GURL& main_frame_url = request.navigation_id.main_frame_url; |
| + const UrlTableCacheMap::const_iterator value_iter = url_table_cache_.find( |
| + main_frame_url); |
| + if (value_iter == url_table_cache_.end()) |
| + return; |
| + |
| + const UrlTableCacheValue& value = value_iter->second; |
| + |
| + scoped_ptr<ResourcePrefetcher::RequestVector> requests( |
| + new ResourcePrefetcher::RequestVector); |
| + for (UrlTableRowVector::const_iterator it = value.rows.begin(); |
| + it != value.rows.end(); ++it) { |
| + float confidence = static_cast<float>(it->number_of_hits) / |
| + (it->number_of_hits + it->number_of_misses); |
| + if (confidence < config_.min_resource_confidence_to_trigger_prefetch || |
| + it->number_of_hits < config_.min_resource_hits_to_trigger_prefetch) { |
| + continue; |
| + } |
| + |
| + ResourcePrefetcher::Request* req = new ResourcePrefetcher::Request( |
| + it->resource_url); |
| + requests->push_back(req); |
| + } |
| + |
| + if (requests->empty()) |
| + return; |
| + |
| + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| + base::Bind(&ResourcePrefetcherManager::MaybeAddPrefetch, |
| + prefetch_manager_, |
| + request.navigation_id, |
| + base::Passed(requests.Pass()))); |
|
dominich
2012/08/02 15:00:50
I believe you can use &requests instead of calling
Shishir
2012/08/02 22:06:54
Done.
|
| } |
| void ResourcePrefetchPredictor::OnMainFrameResponse( |
| const URLRequestSummary& response) { |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + if (initialization_state_ != INITIALIZED) |
| + return; |
| - // TODO(shishir): The prefreshing will be stopped here. |
| + if (prefetch_manager_.get()) |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, FROM_HERE, |
| + base::Bind(&ResourcePrefetcherManager::MaybeRemovePrefetch, |
| + prefetch_manager_, |
| + response.navigation_id)); |
| } |
| void ResourcePrefetchPredictor::OnMainFrameRedirect( |
| @@ -395,6 +429,16 @@ void ResourcePrefetchPredictor::CleanupAbandonedNavigations( |
| ++it; |
| } |
| } |
| + for (ResultsMap::iterator it = results_map_.begin(); |
| + it != results_map_.end();) { |
| + if (it->first.IsSameRenderer(navigation_id) || |
| + (time_now - it->first.creation_time > max_navigation_age)) { |
| + delete it->second; |
| + results_map_.erase(it++); |
| + } else { |
| + ++it; |
| + } |
| + } |
| } |
| void ResourcePrefetchPredictor::Observe( |
| @@ -457,6 +501,19 @@ void ResourcePrefetchPredictor::Observe( |
| } |
| } |
| +void ResourcePrefetchPredictor::FinishedPrefetchForNavigation( |
| + const NavigationID& navigation_id, |
| + scoped_ptr<ResourcePrefetcher::RequestVector> requests) { |
| + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + |
| + // Add the results to the results map. |
| + ResourcePrefetcher::RequestVector* req = requests.release(); |
| + if (!results_map_.insert(std::make_pair(navigation_id, req)).second) { |
| + DLOG(FATAL) << "Returning results for existing navigation."; |
| + delete req; |
| + } |
| +} |
| + |
| void ResourcePrefetchPredictor::OnHistoryAndCacheLoaded() { |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| DCHECK_EQ(initialization_state_, INITIALIZING); |
| @@ -499,6 +556,12 @@ void ResourcePrefetchPredictor::OnHistoryAndCacheLoaded() { |
| // TODO(shishir): Maybe listen for notifications for navigation being |
| // abandoned and cleanup the inflight_navigations_. |
| + // Initialize the prefetch manager only if prefetching is enabled. |
| + if (prerender::IsSpeculativeResourcePrefetchingEnabled(profile_)) { |
| + prefetch_manager_ = new ResourcePrefetcherManager( |
| + this, config_, profile_->GetRequestContext()); |
| + } |
| + |
| initialization_state_ = INITIALIZED; |
| } |
| @@ -535,7 +598,11 @@ void ResourcePrefetchPredictor::OnNavigationComplete( |
| NAVIGATION_STATUS_COUNT); |
| // Report any stats. |
| - MaybeReportAccuracyStats(navigation_id); |
| + if (prefetch_manager_.get()) { |
| + MaybeReportAccuracyStats(navigation_id); |
| + } else { |
| + MaybeReportSimulatedAccuracyStats(navigation_id); |
| + } |
| // Update the URL table. |
| const GURL& main_frame_url = navigation_id.main_frame_url; |
| @@ -544,6 +611,8 @@ void ResourcePrefetchPredictor::OnNavigationComplete( |
| // Remove the navigation. |
| inflight_navigations_.erase(navigation_id); |
| + delete results_map_[navigation_id]; |
| + results_map_.erase(navigation_id); |
| } |
| void ResourcePrefetchPredictor::LearnUrlNavigation( |
| @@ -552,7 +621,7 @@ void ResourcePrefetchPredictor::LearnUrlNavigation( |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| if (url_table_cache_.find(main_frame_url) == url_table_cache_.end()) { |
| - if (url_table_cache_.size() >= config_.max_urls_to_track) |
| + if (static_cast<int>(url_table_cache_.size()) >= config_.max_urls_to_track) |
|
dominich
2012/08/02 15:00:50
if it makes sense for this to be size_t that's fin
Shishir
2012/08/02 22:06:54
Lets leave it at int to be consistent through out.
|
| RemoveAnEntryFromUrlDB(); |
| url_table_cache_[main_frame_url].last_visit = base::Time::Now(); |
| @@ -680,7 +749,7 @@ void ResourcePrefetchPredictor::RemoveAnEntryFromUrlDB() { |
| urls_to_delete)); |
| } |
| -void ResourcePrefetchPredictor::MaybeReportAccuracyStats( |
| +void ResourcePrefetchPredictor::MaybeReportSimulatedAccuracyStats( |
| const NavigationID& navigation_id) const { |
| const GURL& main_frame_url = navigation_id.main_frame_url; |
| DCHECK(inflight_navigations_.find(navigation_id) != |
| @@ -730,6 +799,130 @@ void ResourcePrefetchPredictor::MaybeReportAccuracyStats( |
| prefetch_network * 100.0 / num_assumed_prefetched); |
| } |
| +void ResourcePrefetchPredictor::MaybeReportAccuracyStats( |
| + const NavigationID& navigation_id) { |
| + NavigationMap::iterator nav_it = inflight_navigations_.find(navigation_id); |
|
dominich
2012/08/02 15:00:50
no need to do this before checking have_prefetch_r
Shishir
2012/08/02 22:06:54
This is a correctness DCHECK, so I would prefer to
|
| + DCHECK(nav_it != inflight_navigations_.end()); |
| + |
| + ResultsMap::iterator results_it = results_map_.find(navigation_id); |
| + bool have_prefetch_results = results_it != results_map_.end(); |
| + UMA_HISTOGRAM_BOOLEAN("ResourcePrefetchPredictor.HavePrefetchResults", |
| + have_prefetch_results); |
| + if (!have_prefetch_results) |
| + return; |
| + |
| + // Annotate the results. |
| + const std::vector<URLRequestSummary>& actual = nav_it->second; |
| + ResourcePrefetcher::RequestVector* prefetched = results_it->second; |
| + |
| + std::map<GURL, bool> actual_resources; |
| + for (std::vector<URLRequestSummary>::const_iterator it = actual.begin(); |
| + it != actual.end(); ++it) { |
| + actual_resources[it->resource_url] = it->was_cached; |
| + } |
| + |
| + int prefetch_cancelled = 0, prefetch_failed = 0, prefetch_not_started = 0; |
| + // 'a_' -> actual, 'p_' -> predicted. |
| + int p_cache_a_cache = 0, p_cache_a_network = 0, p_cache_a_notused = 0, |
| + p_network_a_cache = 0, p_network_a_network = 0, p_network_a_notused = 0; |
| + |
| + for (ResourcePrefetcher::RequestVector::iterator it = prefetched->begin(); |
| + it != prefetched->end(); ++it) { |
| + ResourcePrefetcher::Request* req = *it; |
| + |
| + // Set the usage states if the resource was actually used. |
| + std::map<GURL, bool>::iterator actual_it = actual_resources.find( |
| + req->resource_url); |
| + if (actual_it != actual_resources.end()) { |
| + if (actual_it->second) { |
| + req->usage_status = |
| + ResourcePrefetcher::Request::USAGE_STATUS_FROM_CACHE; |
| + } else { |
| + req->usage_status = |
| + ResourcePrefetcher::Request::USAGE_STATUS_FROM_NETWORK; |
| + } |
| + } |
| + |
| + switch (req->prefetch_status) { |
| + |
| + // TODO(shishir): Add histogram for each cancellation reason. |
| + case ResourcePrefetcher::Request::PREFETCH_STATUS_REDIRECTED: |
| + case ResourcePrefetcher::Request::PREFETCH_STATUS_AUTH_REQUIRED: |
| + case ResourcePrefetcher::Request::PREFETCH_STATUS_CERT_REQUIRED: |
| + case ResourcePrefetcher::Request::PREFETCH_STATUS_CERT_ERROR: |
| + case ResourcePrefetcher::Request::PREFETCH_STATUS_CANCELLED: |
| + ++prefetch_cancelled; |
| + break; |
| + |
| + case ResourcePrefetcher::Request::PREFETCH_STATUS_FAILED: |
| + ++prefetch_failed; |
| + break; |
| + |
| + case ResourcePrefetcher::Request::PREFETCH_STATUS_FROM_CACHE: |
| + if (req->usage_status == |
| + ResourcePrefetcher::Request::USAGE_STATUS_FROM_CACHE) |
| + ++p_cache_a_cache; |
| + else if (req->usage_status == |
| + ResourcePrefetcher::Request::USAGE_STATUS_FROM_NETWORK) |
| + ++p_cache_a_network; |
| + else |
| + ++p_cache_a_notused; |
| + break; |
| + |
| + case ResourcePrefetcher::Request::PREFETCH_STATUS_FROM_NETWORK: |
| + if (req->usage_status == |
| + ResourcePrefetcher::Request::USAGE_STATUS_FROM_CACHE) |
| + ++p_network_a_cache; |
| + else if (req->usage_status == |
| + ResourcePrefetcher::Request::USAGE_STATUS_FROM_NETWORK) |
| + ++p_network_a_network; |
| + else |
| + ++p_network_a_notused; |
| + break; |
| + |
| + case ResourcePrefetcher::Request::PREFETCH_STATUS_NOT_STARTED: |
| + ++prefetch_not_started; |
| + break; |
| + |
| + case ResourcePrefetcher::Request::PREFETCH_STATUS_STARTED: |
| + DLOG(FATAL) << "Invalid prefetch status"; |
| + break; |
| + } |
| + } |
| + |
| + int total_prefetched = p_cache_a_cache + p_cache_a_network + p_cache_a_notused |
| + + p_network_a_cache + p_network_a_network + p_network_a_notused; |
| + |
| + UMA_HISTOGRAM_PERCENTAGE( |
| + "ResourcePrefetchPredictor.PrefetchCancelled", |
| + prefetch_cancelled * 100.0 / total_prefetched); |
| + UMA_HISTOGRAM_PERCENTAGE( |
| + "ResourcePrefetchPredictor.PrefetchFailed", |
| + prefetch_failed * 100.0 / total_prefetched); |
| + UMA_HISTOGRAM_PERCENTAGE( |
| + "ResourcePrefetchPredictor.PrefetchFromCacheUsedFromCache", |
| + p_cache_a_cache * 100.0 / total_prefetched); |
| + UMA_HISTOGRAM_PERCENTAGE( |
| + "ResourcePrefetchPredictor.PrefetchFromCacheUsedFromNetwork", |
| + p_cache_a_network * 100.0 / total_prefetched); |
| + UMA_HISTOGRAM_PERCENTAGE( |
| + "ResourcePrefetchPredictor.PrefetchFromCacheNotUsed", |
| + p_cache_a_notused * 100.0 / total_prefetched); |
| + UMA_HISTOGRAM_PERCENTAGE( |
| + "ResourcePrefetchPredictor.PrefetchFromNetworkUsedFromCache", |
| + p_network_a_cache * 100.0 / total_prefetched); |
| + UMA_HISTOGRAM_PERCENTAGE( |
| + "ResourcePrefetchPredictor.PrefetchFromNetworkUsedFromNetwork", |
| + p_network_a_network * 100.0 / total_prefetched); |
| + UMA_HISTOGRAM_PERCENTAGE( |
| + "ResourcePrefetchPredictor.PrefetchFromNetworkNotUsed", |
| + p_network_a_notused * 100.0 / total_prefetched); |
| + |
| + UMA_HISTOGRAM_PERCENTAGE( |
| + "ResourcePrefetchPredictor.PrefetchNotStarted", |
| + prefetch_not_started * 100.0 / (prefetch_not_started + total_prefetched)); |
| +} |
| + |
| void ResourcePrefetchPredictor::DeleteAllUrls() { |
| inflight_navigations_.clear(); |
| url_table_cache_.clear(); |