diff options
-rw-r--r-- | chrome/browser/io_thread.cc | 15 | ||||
-rw-r--r-- | chrome/browser/io_thread.h | 7 | ||||
-rw-r--r-- | chrome/browser/net/predictor.h | 5 | ||||
-rw-r--r-- | chrome/browser/net/predictor_api.cc | 78 | ||||
-rw-r--r-- | chrome/browser/net/predictor_api.h | 9 | ||||
-rw-r--r-- | chrome/browser/net/predictor_unittest.cc | 19 | ||||
-rw-r--r-- | net/base/host_resolver_impl.cc | 8 |
7 files changed, 89 insertions, 52 deletions
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc index f1cb24b..3c7fdc6 100644 --- a/chrome/browser/io_thread.cc +++ b/chrome/browser/io_thread.cc @@ -68,7 +68,9 @@ net::HostResolver* CreateGlobalHostResolver(net::NetLog* net_log) { // List options with different counts. // Firefox limits total to 8 in parallel, and default is currently 50. int parallel_6 = trial->AppendGroup("parallel_6", kProbabilityPerGroup); + int parallel_7 = trial->AppendGroup("parallel_7", kProbabilityPerGroup); int parallel_8 = trial->AppendGroup("parallel_8", kProbabilityPerGroup); + int parallel_9 = trial->AppendGroup("parallel_9", kProbabilityPerGroup); int parallel_10 = trial->AppendGroup("parallel_10", kProbabilityPerGroup); int parallel_14 = trial->AppendGroup("parallel_14", kProbabilityPerGroup); int parallel_20 = trial->AppendGroup("parallel_20", kProbabilityPerGroup); @@ -78,8 +80,12 @@ net::HostResolver* CreateGlobalHostResolver(net::NetLog* net_log) { if (trial->group() == parallel_6) parallelism = 6; + else if (trial->group() == parallel_7) + parallelism = 7; else if (trial->group() == parallel_8) parallelism = 8; + else if (trial->group() == parallel_9) + parallelism = 9; else if (trial->group() == parallel_10) parallelism = 10; else if (trial->group() == parallel_14) @@ -221,7 +227,7 @@ IOThread::Globals* IOThread::globals() { void IOThread::InitNetworkPredictor( bool prefetching_enabled, base::TimeDelta max_dns_queue_delay, - size_t max_concurrent, + size_t max_speculative_parallel_resolves, const chrome_common_net::UrlList& startup_urls, ListValue* referral_list, bool preconnect_enabled) { @@ -231,7 +237,8 @@ void IOThread::InitNetworkPredictor( NewRunnableMethod( this, &IOThread::InitNetworkPredictorOnIOThread, - prefetching_enabled, max_dns_queue_delay, max_concurrent, + prefetching_enabled, max_dns_queue_delay, + max_speculative_parallel_resolves, startup_urls, referral_list, preconnect_enabled)); } @@ -391,7 +398,7 @@ net::HttpAuthHandlerFactory* IOThread::CreateDefaultAuthHandlerFactory( void IOThread::InitNetworkPredictorOnIOThread( bool prefetching_enabled, base::TimeDelta max_dns_queue_delay, - size_t max_concurrent, + size_t max_speculative_parallel_resolves, const chrome_common_net::UrlList& startup_urls, ListValue* referral_list, bool preconnect_enabled) { @@ -403,7 +410,7 @@ void IOThread::InitNetworkPredictorOnIOThread( predictor_ = new chrome_browser_net::Predictor( globals_->host_resolver.get(), max_dns_queue_delay, - max_concurrent, + max_speculative_parallel_resolves, preconnect_enabled); predictor_->AddRef(); diff --git a/chrome/browser/io_thread.h b/chrome/browser/io_thread.h index 2baec87..09f4df4 100644 --- a/chrome/browser/io_thread.h +++ b/chrome/browser/io_thread.h @@ -61,7 +61,7 @@ class IOThread : public BrowserProcessSubThread { // It will post a task to the IO thread to perform the actual initialization. void InitNetworkPredictor(bool prefetching_enabled, base::TimeDelta max_dns_queue_delay, - size_t max_concurrent, + size_t max_speculative_parallel_resolves, const chrome_common_net::UrlList& startup_urls, ListValue* referral_list, bool preconnect_enabled); @@ -92,9 +92,8 @@ class IOThread : public BrowserProcessSubThread { void InitNetworkPredictorOnIOThread( bool prefetching_enabled, base::TimeDelta max_dns_queue_delay, - size_t max_concurrent, - const chrome_common_net::UrlList& startup_urls, - + size_t max_speculative_parallel_resolves, + const chrome_common_net::UrlList& startup_urls, ListValue* referral_list, bool preconnect_enabled); diff --git a/chrome/browser/net/predictor.h b/chrome/browser/net/predictor.h index e6363ad..f735cb4 100644 --- a/chrome/browser/net/predictor.h +++ b/chrome/browser/net/predictor.h @@ -263,7 +263,10 @@ class Predictor : public base::RefCountedThreadSafe<Predictor> { // When true, we don't make new lookup requests. bool shutdown_; - // The number of concurrent lookups currently allowed. + // The number of concurrent speculative lookups currently allowed to be sent + // to the resolver. Any additional lookups will be queued to avoid exceeding + // this value. The queue is a priority queue that will accelerate + // sub-resource speculation, and retard resolutions suggested by page scans. const size_t max_concurrent_dns_lookups_; // The maximum queueing delay that is acceptable before we enter congestion diff --git a/chrome/browser/net/predictor_api.cc b/chrome/browser/net/predictor_api.cc index 9149095..a0d885b 100644 --- a/chrome/browser/net/predictor_api.cc +++ b/chrome/browser/net/predictor_api.cc @@ -41,11 +41,35 @@ static void DnsPrefetchMotivatedList(const UrlList& urls, static UrlList GetPredictedUrlListAtStartup(PrefService* user_prefs, PrefService* local_state); +// Given that the underlying Chromium resolver defaults to a total maximum of +// 8 paralell resolutions, we will avoid any chance of starving navigational +// resolutions by limiting the number of paralell speculative resolutions. +// TODO(jar): Move this limitation into the resolver. // static -const size_t PredictorInit::kMaxPrefetchConcurrentLookups = 8; - +const size_t PredictorInit::kMaxSpeculativeParallelResolves = 3; + +// To control our congestion avoidance system, which discards a queue when +// resolutions are "taking too long," we need an expected resolution time. +// Common average is in the range of 300-500ms. +static const int kExpectedResolutionTimeMs = 500; + +// To control the congestion avoidance system, we need an estimate of how many +// speculative requests may arrive at once. Since we currently only keep 8 +// subresource names for each frame, we'll use that as our basis. Note that +// when scanning search results lists, we might actually get 10 at a time, and +// wikipedia can often supply (during a page scan) upwards of 50. In those odd +// cases, we may discard some of the later speculative requests mistakenly +// assuming that the resolutions took too long. +static const int kTypicalSpeculativeGroupSize = 8; + +// The next constant specifies an amount of queueing delay that is "too large," +// and indicative of problems with resolutions (perhaps due to an overloaded +// router, or such). When we exceed this delay, congestion avoidance will kick +// in and all speculations in the queue will be discarded. // static -const int PredictorInit::kMaxPrefetchQueueingDelayMs = 500; +const int PredictorInit::kMaxSpeculativeResolveQueueDelayMs = + (kExpectedResolutionTimeMs * kTypicalSpeculativeGroupSize) / + kMaxSpeculativeParallelResolves; // A version number for prefs that are saved. This should be incremented when // we change the format so that we discard old data. @@ -351,8 +375,10 @@ void PredictorGetHtmlInfo(std::string* output) { //------------------------------------------------------------------------------ static void InitNetworkPredictor(TimeDelta max_dns_queue_delay, - size_t max_concurrent, PrefService* user_prefs, PrefService* local_state, - bool preconnect_enabled) { + size_t max_parallel_resolves, + PrefService* user_prefs, + PrefService* local_state, + bool preconnect_enabled) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); bool prefetching_enabled = @@ -367,7 +393,7 @@ static void InitNetworkPredictor(TimeDelta max_dns_queue_delay, local_state->GetMutableList(prefs::kDnsHostReferralList)->DeepCopy()); g_browser_process->io_thread()->InitNetworkPredictor( - prefetching_enabled, max_dns_queue_delay, max_concurrent, urls, + prefetching_enabled, max_dns_queue_delay, max_parallel_resolves, urls, referral_list, preconnect_enabled); } @@ -554,33 +580,35 @@ PredictorInit::PredictorInit(PrefService* user_prefs, if (trial_->group() != disabled_prefetch) { // Initialize the DNS prefetch system. - size_t max_concurrent = kMaxPrefetchConcurrentLookups; - int max_queueing_delay_ms = kMaxPrefetchQueueingDelayMs; - - if (trial_->group() == max_250ms_prefetch) - max_queueing_delay_ms = 250; - else if (trial_->group() == max_500ms_prefetch) - max_queueing_delay_ms = 500; - else if (trial_->group() == max_750ms_prefetch) - max_queueing_delay_ms = 750; - else if (trial_->group() == max_2s_prefetch) - max_queueing_delay_ms = 2000; + size_t max_parallel_resolves = kMaxSpeculativeParallelResolves; + int max_queueing_delay_ms = kMaxSpeculativeResolveQueueDelayMs; + if (trial_->group() == max_2_concurrent_prefetch) - max_concurrent = 2; + max_parallel_resolves = 2; else if (trial_->group() == max_4_concurrent_prefetch) - max_concurrent = 4; + max_parallel_resolves = 4; else if (trial_->group() == max_6_concurrent_prefetch) - max_concurrent = 6; - // Scale acceptable delay so we don't cause congestion limits to fire as - // we modulate max_concurrent (*if* we are modulating it at all). - max_queueing_delay_ms = (kMaxPrefetchQueueingDelayMs * - kMaxPrefetchConcurrentLookups) / max_concurrent; + max_parallel_resolves = 6; + + if (trial_->group() == max_250ms_prefetch) { + max_queueing_delay_ms = + (250 * kTypicalSpeculativeGroupSize) / max_parallel_resolves; + } else if (trial_->group() == max_500ms_prefetch) { + max_queueing_delay_ms = + (500 * kTypicalSpeculativeGroupSize) / max_parallel_resolves; + } else if (trial_->group() == max_750ms_prefetch) { + max_queueing_delay_ms = + (750 * kTypicalSpeculativeGroupSize) / max_parallel_resolves; + } else if (trial_->group() == max_2s_prefetch) { + max_queueing_delay_ms = + (2000 * kTypicalSpeculativeGroupSize) / max_parallel_resolves; + } TimeDelta max_queueing_delay( TimeDelta::FromMilliseconds(max_queueing_delay_ms)); DCHECK(!g_predictor); - InitNetworkPredictor(max_queueing_delay, max_concurrent, user_prefs, + InitNetworkPredictor(max_queueing_delay, max_parallel_resolves, user_prefs, local_state, preconnect_enabled); } } diff --git a/chrome/browser/net/predictor_api.h b/chrome/browser/net/predictor_api.h index 3e04e11..a03f7c9 100644 --- a/chrome/browser/net/predictor_api.h +++ b/chrome/browser/net/predictor_api.h @@ -81,15 +81,14 @@ void SavePredictorStateForNextStartupAndTrim(PrefService* prefs); // Helper class to handle global init and shutdown. class PredictorInit { public: - // Too many concurrent lookups negate benefits of prefetching by trashing - // the OS cache before all resource loading is complete. - // This is the default. - static const size_t kMaxPrefetchConcurrentLookups; + // Too many concurrent lookups performed in parallel may overload a resolver, + // or may cause problems for a local router. The following limits that count. + static const size_t kMaxSpeculativeParallelResolves; // When prefetch requests are queued beyond some period of time, then the // system is congested, and we need to clear all queued requests to get out // of that state. The following is the suggested default time limit. - static const int kMaxPrefetchQueueingDelayMs; + static const int kMaxSpeculativeResolveQueueDelayMs; PredictorInit(PrefService* user_prefs, PrefService* local_state, bool preconnect_enabled); diff --git a/chrome/browser/net/predictor_unittest.cc b/chrome/browser/net/predictor_unittest.cc index a0a0113..38f0281 100644 --- a/chrome/browser/net/predictor_unittest.cc +++ b/chrome/browser/net/predictor_unittest.cc @@ -65,7 +65,7 @@ class PredictorTest : public testing::Test { : io_thread_(BrowserThread::IO, &loop_), host_resolver_(new net::MockCachingHostResolver()), default_max_queueing_delay_(TimeDelta::FromMilliseconds( - PredictorInit::kMaxPrefetchQueueingDelayMs)) { + PredictorInit::kMaxSpeculativeResolveQueueDelayMs)) { } protected: @@ -112,7 +112,7 @@ TEST_F(PredictorTest, StartupShutdownTest) { scoped_refptr<Predictor> testing_master = new Predictor(host_resolver_.get(), default_max_queueing_delay_, - PredictorInit::kMaxPrefetchConcurrentLookups, + PredictorInit::kMaxSpeculativeParallelResolves, false); testing_master->Shutdown(); } @@ -126,7 +126,7 @@ TEST_F(PredictorTest, ShutdownWhenResolutionIsPendingTest) { scoped_refptr<Predictor> testing_master = new Predictor(host_resolver_.get(), default_max_queueing_delay_, - PredictorInit::kMaxPrefetchConcurrentLookups, + PredictorInit::kMaxSpeculativeParallelResolves, false); GURL localhost("http://localhost:80"); @@ -152,7 +152,7 @@ TEST_F(PredictorTest, SingleLookupTest) { scoped_refptr<Predictor> testing_master = new Predictor(host_resolver_.get(), default_max_queueing_delay_, - PredictorInit::kMaxPrefetchConcurrentLookups, + PredictorInit::kMaxSpeculativeParallelResolves, false); GURL goog("http://www.google.com:80"); @@ -184,7 +184,7 @@ TEST_F(PredictorTest, ConcurrentLookupTest) { scoped_refptr<Predictor> testing_master = new Predictor(host_resolver_.get(), default_max_queueing_delay_, - PredictorInit::kMaxPrefetchConcurrentLookups, + PredictorInit::kMaxSpeculativeParallelResolves, false); GURL goog("http://www.google.com:80"), @@ -221,7 +221,6 @@ TEST_F(PredictorTest, ConcurrentLookupTest) { EXPECT_FALSE(testing_master->WasFound(bad1)); EXPECT_FALSE(testing_master->WasFound(bad2)); - EXPECT_GT(testing_master->peak_pending_lookups(), names.size() / 2); EXPECT_LE(testing_master->peak_pending_lookups(), names.size()); EXPECT_LE(testing_master->peak_pending_lookups(), testing_master->max_concurrent_dns_lookups()); @@ -235,7 +234,7 @@ TEST_F(PredictorTest, MassiveConcurrentLookupTest) { scoped_refptr<Predictor> testing_master = new Predictor(host_resolver_.get(), default_max_queueing_delay_, - PredictorInit::kMaxPrefetchConcurrentLookups, + PredictorInit::kMaxSpeculativeParallelResolves, false); UrlList names; @@ -355,7 +354,7 @@ TEST_F(PredictorTest, ReferrerSerializationNilTest) { scoped_refptr<Predictor> predictor = new Predictor(host_resolver_.get(), default_max_queueing_delay_, - PredictorInit::kMaxPrefetchConcurrentLookups, + PredictorInit::kMaxSpeculativeParallelResolves, false); scoped_ptr<ListValue> referral_list(NewEmptySerializationList()); predictor->SerializeReferrers(referral_list.get()); @@ -374,7 +373,7 @@ TEST_F(PredictorTest, ReferrerSerializationSingleReferrerTest) { scoped_refptr<Predictor> predictor = new Predictor(host_resolver_.get(), default_max_queueing_delay_, - PredictorInit::kMaxPrefetchConcurrentLookups, + PredictorInit::kMaxSpeculativeParallelResolves, false); const GURL motivation_url("http://www.google.com:91"); const GURL subresource_url("http://icons.google.com:90"); @@ -402,7 +401,7 @@ TEST_F(PredictorTest, ReferrerSerializationTrimTest) { scoped_refptr<Predictor> predictor = new Predictor(host_resolver_.get(), default_max_queueing_delay_, - PredictorInit::kMaxPrefetchConcurrentLookups, + PredictorInit::kMaxSpeculativeParallelResolves, false); GURL motivation_url("http://www.google.com:110"); diff --git a/net/base/host_resolver_impl.cc b/net/base/host_resolver_impl.cc index 13dce85..3805979 100644 --- a/net/base/host_resolver_impl.cc +++ b/net/base/host_resolver_impl.cc @@ -74,9 +74,11 @@ HostCache* CreateDefaultCache() { HostResolver* CreateSystemHostResolver(size_t max_concurrent_resolves, HostResolverProc* resolver_proc, NetLog* net_log) { - // Maximum of 50 concurrent threads. - // TODO(eroman): Adjust this, do some A/B experiments. - static const size_t kDefaultMaxJobs = 50u; + // Maximum of 8 concurrent resolver threads. + // Some routers (or resolvers) appear to start to provide host-not-found if + // too many simultaneous resolutions are pending. This number needs to be + // further optimized, but 8 is what FF currently does. + static const size_t kDefaultMaxJobs = 8u; if (max_concurrent_resolves == HostResolver::kDefaultParallelism) max_concurrent_resolves = kDefaultMaxJobs; |