OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <set> | 5 #include <set> |
6 | 6 |
7 #include "content/browser/loader/resource_scheduler.h" | |
8 | |
9 #include "base/metrics/field_trial.h" | 7 #include "base/metrics/field_trial.h" |
10 #include "base/metrics/histogram.h" | 8 #include "base/metrics/histogram.h" |
11 #include "base/stl_util.h" | 9 #include "base/stl_util.h" |
12 #include "base/strings/string_number_conversions.h" | 10 #include "base/strings/string_number_conversions.h" |
13 #include "base/strings/string_piece.h" | 11 #include "base/strings/string_piece.h" |
14 #include "base/time/time.h" | 12 #include "base/time/time.h" |
15 #include "content/common/resource_messages.h" | 13 #include "content/common/resource_messages.h" |
16 #include "content/browser/loader/resource_message_delegate.h" | 14 #include "content/browser/loader/resource_message_delegate.h" |
15 #include "content/browser/loader/resource_scheduler.h" | |
mmenke
2015/08/07 16:55:10
This should actually go at the top, before the <se
Pat Meenan
2015/08/07 20:48:10
Done.
| |
17 #include "content/public/browser/resource_controller.h" | 16 #include "content/public/browser/resource_controller.h" |
18 #include "content/public/browser/resource_request_info.h" | 17 #include "content/public/browser/resource_request_info.h" |
19 #include "content/public/browser/resource_throttle.h" | 18 #include "content/public/browser/resource_throttle.h" |
20 #include "ipc/ipc_message_macros.h" | 19 #include "ipc/ipc_message_macros.h" |
21 #include "net/base/host_port_pair.h" | 20 #include "net/base/host_port_pair.h" |
22 #include "net/base/load_flags.h" | 21 #include "net/base/load_flags.h" |
23 #include "net/base/request_priority.h" | 22 #include "net/base/request_priority.h" |
24 #include "net/http/http_server_properties.h" | 23 #include "net/http/http_server_properties.h" |
25 #include "net/url_request/url_request.h" | 24 #include "net/url_request/url_request.h" |
26 #include "net/url_request/url_request_context.h" | 25 #include "net/url_request/url_request_context.h" |
27 | 26 |
28 namespace content { | 27 namespace content { |
29 | 28 |
30 namespace { | 29 namespace { |
31 | 30 |
32 // Field trial constants | 31 // Field trial constants |
33 const char kThrottleCoalesceFieldTrial[] = "RequestThrottlingAndCoalescing"; | 32 const char kThrottleCoalesceFieldTrial[] = "RequestThrottlingAndCoalescing"; |
34 const char kThrottleCoalesceFieldTrialThrottle[] = "Throttle"; | 33 const char kThrottleCoalesceFieldTrialThrottle[] = "Throttle"; |
35 const char kThrottleCoalesceFieldTrialCoalesce[] = "Coalesce"; | 34 const char kThrottleCoalesceFieldTrialCoalesce[] = "Coalesce"; |
36 | 35 |
37 const char kRequestLimitFieldTrial[] = "OutstandingRequestLimiting"; | 36 const char kRequestLimitFieldTrial[] = "OutstandingRequestLimiting"; |
38 const char kRequestLimitFieldTrialGroupPrefix[] = "Limit"; | 37 const char kRequestLimitFieldTrialGroupPrefix[] = "Limit"; |
39 | 38 |
39 const char kResourcePrioritiesFieldTrial[] = "ResourcePriorities"; | |
40 | |
40 // Post ResourceScheduler histograms of the following forms: | 41 // Post ResourceScheduler histograms of the following forms: |
41 // If |histogram_suffix| is NULL or the empty string: | 42 // If |histogram_suffix| is NULL or the empty string: |
42 // ResourceScheduler.base_name.histogram_name | 43 // ResourceScheduler.base_name.histogram_name |
43 // Else: | 44 // Else: |
44 // ResourceScheduler.base_name.histogram_name.histogram_suffix | 45 // ResourceScheduler.base_name.histogram_name.histogram_suffix |
45 void PostHistogram(const char* base_name, | 46 void PostHistogram(const char* base_name, |
46 const char* histogram_name, | 47 const char* histogram_name, |
47 const char* histogram_suffix, | 48 const char* histogram_suffix, |
48 base::TimeDelta time) { | 49 base::TimeDelta time) { |
49 std::string histogram = | 50 std::string histogram = |
(...skipping 19 matching lines...) Expand all Loading... | |
69 else if (num_clients <= 30) | 70 else if (num_clients <= 30) |
70 return "Max30Clients"; | 71 return "Max30Clients"; |
71 return "Over30Clients"; | 72 return "Over30Clients"; |
72 } | 73 } |
73 | 74 |
74 } // namespace | 75 } // namespace |
75 | 76 |
76 static const size_t kCoalescedTimerPeriod = 5000; | 77 static const size_t kCoalescedTimerPeriod = 5000; |
77 static const size_t kMaxNumDelayableRequestsPerClient = 10; | 78 static const size_t kMaxNumDelayableRequestsPerClient = 10; |
78 static const size_t kMaxNumDelayableRequestsPerHost = 6; | 79 static const size_t kMaxNumDelayableRequestsPerHost = 6; |
79 static const size_t kMaxNumThrottledRequestsPerClient = 1; | 80 static const size_t kMaxNumThrottledRequestsPerClient = 1; |
mmenke
2015/08/07 16:55:10
+Default
Pat Meenan
2015/08/07 20:48:10
Done.
| |
81 static const size_t kMaxNumDelayableWhileLayoutBlocking = 1; | |
mmenke
2015/08/07 16:55:11
+Default
Pat Meenan
2015/08/07 20:48:09
Done.
| |
82 static const net::RequestPriority kLayoutBlockingPriorityThreshold = net::LOW; | |
mmenke
2015/08/07 16:55:11
+Default
Pat Meenan
2015/08/07 20:48:10
Done.
| |
80 | 83 |
81 struct ResourceScheduler::RequestPriorityParams { | 84 struct ResourceScheduler::RequestPriorityParams { |
82 RequestPriorityParams() | 85 RequestPriorityParams() |
83 : priority(net::DEFAULT_PRIORITY), | 86 : priority(net::DEFAULT_PRIORITY), |
84 intra_priority(0) { | 87 intra_priority(0) { |
85 } | 88 } |
86 | 89 |
87 RequestPriorityParams(net::RequestPriority priority, int intra_priority) | 90 RequestPriorityParams(net::RequestPriority priority, int intra_priority) |
88 : priority(priority), | 91 : priority(priority), |
89 intra_priority(intra_priority) { | 92 intra_priority(intra_priority) { |
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
307 : is_audible_(is_audible), | 310 : is_audible_(is_audible), |
308 is_visible_(is_visible), | 311 is_visible_(is_visible), |
309 is_loaded_(false), | 312 is_loaded_(false), |
310 is_paused_(false), | 313 is_paused_(false), |
311 has_body_(false), | 314 has_body_(false), |
312 using_spdy_proxy_(false), | 315 using_spdy_proxy_(false), |
313 load_started_time_(base::TimeTicks::Now()), | 316 load_started_time_(base::TimeTicks::Now()), |
314 scheduler_(scheduler), | 317 scheduler_(scheduler), |
315 in_flight_delayable_count_(0), | 318 in_flight_delayable_count_(0), |
316 total_layout_blocking_count_(0), | 319 total_layout_blocking_count_(0), |
317 throttle_state_(ResourceScheduler::THROTTLED) {} | 320 throttle_state_(ResourceScheduler::THROTTLED), |
321 max_num_delayable_requests_(kMaxNumDelayableRequestsPerClient), | |
322 max_num_delayable_while_layout_blocking_( | |
323 kMaxNumDelayableWhileLayoutBlocking), | |
324 enable_layout_blocking_threshold_(false), | |
325 in_flight_layout_blocking_threshold_(0), | |
326 layout_blocking_priority_threshold_(kLayoutBlockingPriorityThreshold) { | |
327 // The field trial parameters are also encoded into the group name since | |
328 // the variations component is not available from here and plumbing the | |
329 // options through the code is overkill for a short experiment. | |
330 // | |
331 // The group name encoding looks like this: | |
332 // <descriptiveName>_ABCDE_E2_F_G | |
333 // A - fetchDeferLateScripts (1 for true, 0 for false) | |
334 // B - fetchIncreaseFontPriority (1 for true, 0 for false) | |
335 // C - fetchIncreaseAsyncScriptPriority (1 for true, 0 for false) | |
336 // D - fetchIncreasePriorities (1 for true, 0 for false) | |
337 // E - fetchEnableLayoutBlockingThreshold (1 for true, 0 for false) | |
338 // E2 - fetchLayoutBlockingThreshold (Numeric) | |
339 // F - fetchMaxNumDelayableWhileLayoutBlocking (Numeric) | |
340 // G - fetchMaxNumDelayableRequests (Numeric) | |
341 std::string resource_priorities_trial_group = | |
342 base::FieldTrialList::FindFullName(kResourcePrioritiesFieldTrial); | |
343 std::vector<std::string> split_group( | |
344 base::SplitString(resource_priorities_trial_group, "_", | |
345 base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL)); | |
346 if (split_group.size() == 5 && split_group[1].length() == 5) { | |
347 // fetchIncreasePriorities | |
348 if (split_group[1].at(3) == '1') | |
349 layout_blocking_priority_threshold_ = net::MEDIUM; | |
350 enable_layout_blocking_threshold_ = split_group[1].at(4) == '1'; | |
351 size_t numeric_value = 0; | |
352 if (base::StringToSizeT(split_group[2], &numeric_value)) | |
353 in_flight_layout_blocking_threshold_ = numeric_value; | |
354 if (base::StringToSizeT(split_group[3], &numeric_value)) | |
355 max_num_delayable_while_layout_blocking_ = numeric_value; | |
356 if (base::StringToSizeT(split_group[4], &numeric_value)) | |
357 max_num_delayable_requests_ = numeric_value; | |
358 } | |
mmenke
2015/08/07 16:55:11
Why are these per-client variables, and not in the
Pat Meenan
2015/08/07 20:48:10
Moved to the scheduler itself and pass into each c
| |
359 } | |
318 | 360 |
319 ~Client() { | 361 ~Client() { |
320 // Update to default state and pause to ensure the scheduler has a | 362 // Update to default state and pause to ensure the scheduler has a |
321 // correct count of relevant types of clients. | 363 // correct count of relevant types of clients. |
322 is_visible_ = false; | 364 is_visible_ = false; |
323 is_audible_ = false; | 365 is_audible_ = false; |
324 is_paused_ = true; | 366 is_paused_ = true; |
325 UpdateThrottleState(); | 367 UpdateThrottleState(); |
326 } | 368 } |
327 | 369 |
(...skipping 290 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
618 CountRequestsWithClassification(IN_FLIGHT_DELAYABLE_REQUEST, false), | 660 CountRequestsWithClassification(IN_FLIGHT_DELAYABLE_REQUEST, false), |
619 in_flight_delayable_count_); | 661 in_flight_delayable_count_); |
620 DCHECK_EQ(CountRequestsWithClassification(LAYOUT_BLOCKING_REQUEST, true), | 662 DCHECK_EQ(CountRequestsWithClassification(LAYOUT_BLOCKING_REQUEST, true), |
621 total_layout_blocking_count_); | 663 total_layout_blocking_count_); |
622 } | 664 } |
623 | 665 |
624 RequestClassification ClassifyRequest(ScheduledResourceRequest* request) { | 666 RequestClassification ClassifyRequest(ScheduledResourceRequest* request) { |
625 // If a request is already marked as layout-blocking make sure to keep the | 667 // If a request is already marked as layout-blocking make sure to keep the |
626 // classification across redirects unless the priority was lowered. | 668 // classification across redirects unless the priority was lowered. |
627 if (request->classification() == LAYOUT_BLOCKING_REQUEST && | 669 if (request->classification() == LAYOUT_BLOCKING_REQUEST && |
628 request->url_request()->priority() > net::LOW) { | 670 request->url_request()->priority() > |
671 layout_blocking_priority_threshold_) { | |
629 return LAYOUT_BLOCKING_REQUEST; | 672 return LAYOUT_BLOCKING_REQUEST; |
630 } | 673 } |
631 | 674 |
632 if (!has_body_ && request->url_request()->priority() > net::LOW) | 675 if (!has_body_ && |
676 request->url_request()->priority() > | |
677 layout_blocking_priority_threshold_) | |
633 return LAYOUT_BLOCKING_REQUEST; | 678 return LAYOUT_BLOCKING_REQUEST; |
mmenke
2015/08/07 16:55:11
Add braces
mmenke
2015/08/07 16:55:11
Perhaps we should rename this? Either a request b
Pat Meenan
2015/08/07 20:48:09
There are effectively 3 levels of groupings that w
Pat Meenan
2015/08/07 20:48:09
Done.
| |
634 | 679 |
635 if (request->url_request()->priority() < net::LOW) { | 680 if (request->url_request()->priority() < |
636 net::HostPortPair host_port_pair = | 681 layout_blocking_priority_threshold_ && |
Bryan McQuade
2015/08/07 18:02:26
why did the hostportpair logic get removed?
Pat Meenan
2015/08/07 20:48:09
It was doing more harm than good. If the resource
| |
637 net::HostPortPair::FromURL(request->url_request()->url()); | 682 ContainsKey(in_flight_requests_, request)) { |
638 net::HttpServerProperties& http_server_properties = | 683 return IN_FLIGHT_DELAYABLE_REQUEST; |
639 *request->url_request()->context()->http_server_properties(); | |
640 if (!http_server_properties.SupportsRequestPriority(host_port_pair) && | |
641 ContainsKey(in_flight_requests_, request)) { | |
642 return IN_FLIGHT_DELAYABLE_REQUEST; | |
643 } | |
644 } | 684 } |
645 return NORMAL_REQUEST; | 685 return NORMAL_REQUEST; |
646 } | 686 } |
647 | 687 |
648 bool ShouldKeepSearching( | 688 bool ShouldKeepSearching( |
649 const net::HostPortPair& active_request_host) const { | 689 const net::HostPortPair& active_request_host) const { |
650 size_t same_host_count = 0; | 690 size_t same_host_count = 0; |
651 for (RequestSet::const_iterator it = in_flight_requests_.begin(); | 691 for (RequestSet::const_iterator it = in_flight_requests_.begin(); |
652 it != in_flight_requests_.end(); ++it) { | 692 it != in_flight_requests_.end(); ++it) { |
653 net::HostPortPair host_port_pair = | 693 net::HostPortPair host_port_pair = |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
762 } | 802 } |
763 | 803 |
764 if (throttle_state_ == THROTTLED && | 804 if (throttle_state_ == THROTTLED && |
765 in_flight_requests_.size() >= kMaxNumThrottledRequestsPerClient) { | 805 in_flight_requests_.size() >= kMaxNumThrottledRequestsPerClient) { |
766 // There may still be request-priority-capable requests that should be | 806 // There may still be request-priority-capable requests that should be |
767 // issued. | 807 // issued. |
768 return DO_NOT_START_REQUEST_AND_KEEP_SEARCHING; | 808 return DO_NOT_START_REQUEST_AND_KEEP_SEARCHING; |
769 } | 809 } |
770 | 810 |
771 // High-priority and layout-blocking requests. | 811 // High-priority and layout-blocking requests. |
772 if (url_request.priority() >= net::LOW) { | 812 if (url_request.priority() >= layout_blocking_priority_threshold_) { |
773 return START_REQUEST; | 813 return START_REQUEST; |
774 } | 814 } |
775 | 815 |
776 if (in_flight_delayable_count_ >= kMaxNumDelayableRequestsPerClient) { | 816 if (in_flight_delayable_count_ >= max_num_delayable_requests_) { |
777 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; | 817 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; |
778 } | 818 } |
779 | 819 |
780 if (ShouldKeepSearching(host_port_pair)) { | 820 if (ShouldKeepSearching(host_port_pair)) { |
781 // There may be other requests for other hosts we'd allow, | 821 // There may be other requests for other hosts we'd allow, |
782 // so keep checking. | 822 // so keep checking. |
783 return DO_NOT_START_REQUEST_AND_KEEP_SEARCHING; | 823 return DO_NOT_START_REQUEST_AND_KEEP_SEARCHING; |
784 } | 824 } |
785 | 825 |
786 bool have_immediate_requests_in_flight = | 826 if (!has_body_ || total_layout_blocking_count_ != 0) { |
787 in_flight_requests_.size() > in_flight_delayable_count_; | 827 // Layout-blocking phase of resource loading |
mmenke
2015/08/07 16:55:10
Add period (More consistent with other comments he
mmenke
2015/08/07 16:55:11
Suggest comment above the if (I find it easier to
Pat Meenan
2015/08/07 20:48:09
Done.
| |
788 if (have_immediate_requests_in_flight && | 828 size_t immediate_requests_in_flight_count = |
mmenke
2015/08/07 16:55:11
non_delayable_...?
Pat Meenan
2015/08/07 20:48:09
Done.
| |
789 (!has_body_ || total_layout_blocking_count_ != 0) && | 829 in_flight_requests_.size() - in_flight_delayable_count_; |
790 // Do not allow a low priority request through in parallel if | 830 if (enable_layout_blocking_threshold_) { |
791 // we are in a limit field trial. | 831 if (immediate_requests_in_flight_count > |
792 (scheduler_->limit_outstanding_requests() || | 832 in_flight_layout_blocking_threshold_) { |
793 in_flight_delayable_count_ != 0)) { | 833 // Too many higher priority in-flight requests to allow lower priority |
794 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; | 834 // requests through. |
835 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; | |
836 } | |
837 if (in_flight_requests_.size() > 0 && | |
mmenke
2015/08/07 16:55:11
The other branch only does this if immediate_reque
Pat Meenan
2015/08/07 20:48:09
The difference between the two is pretty significa
| |
838 // Allow the request if nothing is in flight or if the limit of | |
839 // concurrent lower priority requests has not been hit. | |
840 (scheduler_->limit_outstanding_requests() || | |
841 in_flight_delayable_count_ >= | |
842 max_num_delayable_while_layout_blocking_)) { | |
843 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; | |
844 } | |
845 } else if (immediate_requests_in_flight_count > 0 && | |
mmenke
2015/08/07 16:55:11
if "total_layout_blocking_count_" is greater than
Pat Meenan
2015/08/07 20:48:09
Sadly, no. This is where the middle group of requ
mmenke
2015/08/07 20:56:02
Oh, I missed that this block is "!has_body_ || tot
| |
846 // If there are no high-priority requests in flight the floodgates | |
847 // open. | |
848 // If there are high-priority requests in-flight then limit the number | |
849 // of lower-priority requests (or zero if a limit field trial is | |
850 // active). | |
851 (scheduler_->limit_outstanding_requests() || | |
852 in_flight_delayable_count_ >= | |
853 max_num_delayable_while_layout_blocking_)) { | |
854 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; | |
855 } | |
795 } | 856 } |
796 | 857 |
797 return START_REQUEST; | 858 return START_REQUEST; |
798 } | 859 } |
799 | 860 |
800 void LoadAnyStartablePendingRequests() { | 861 void LoadAnyStartablePendingRequests() { |
801 // We iterate through all the pending requests, starting with the highest | 862 // We iterate through all the pending requests, starting with the highest |
802 // priority one. For each entry, one of three things can happen: | 863 // priority one. For each entry, one of three things can happen: |
803 // 1) We start the request, remove it from the list, and keep checking. | 864 // 1) We start the request, remove it from the list, and keep checking. |
804 // 2) We do NOT start the request, but ShouldStartRequest() signals us that | 865 // 2) We do NOT start the request, but ShouldStartRequest() signals us that |
(...skipping 26 matching lines...) Expand all Loading... | |
831 DCHECK(query_result == DO_NOT_START_REQUEST_AND_STOP_SEARCHING); | 892 DCHECK(query_result == DO_NOT_START_REQUEST_AND_STOP_SEARCHING); |
832 break; | 893 break; |
833 } | 894 } |
834 } | 895 } |
835 } | 896 } |
836 | 897 |
837 bool is_audible_; | 898 bool is_audible_; |
838 bool is_visible_; | 899 bool is_visible_; |
839 bool is_loaded_; | 900 bool is_loaded_; |
840 bool is_paused_; | 901 bool is_paused_; |
841 bool has_body_; | 902 bool has_body_; |
mmenke
2015/08/07 16:55:10
Please add a description here, and maybe rename it
Pat Meenan
2015/08/07 20:48:10
Done.
| |
842 bool using_spdy_proxy_; | 903 bool using_spdy_proxy_; |
843 RequestQueue pending_requests_; | 904 RequestQueue pending_requests_; |
844 RequestSet in_flight_requests_; | 905 RequestSet in_flight_requests_; |
845 base::TimeTicks load_started_time_; | 906 base::TimeTicks load_started_time_; |
846 // The last time the client switched state between active and background. | 907 // The last time the client switched state between active and background. |
847 base::TimeTicks last_active_switch_time_; | 908 base::TimeTicks last_active_switch_time_; |
848 ResourceScheduler* scheduler_; | 909 ResourceScheduler* scheduler_; |
849 // The number of delayable in-flight requests. | 910 // The number of delayable in-flight requests. |
850 size_t in_flight_delayable_count_; | 911 size_t in_flight_delayable_count_; |
851 // The number of layout-blocking in-flight requests. | 912 // The number of layout-blocking in-flight requests. |
852 size_t total_layout_blocking_count_; | 913 size_t total_layout_blocking_count_; |
853 ResourceScheduler::ClientThrottleState throttle_state_; | 914 ResourceScheduler::ClientThrottleState throttle_state_; |
915 size_t max_num_delayable_requests_; | |
916 size_t max_num_delayable_while_layout_blocking_; | |
917 bool enable_layout_blocking_threshold_; | |
918 size_t in_flight_layout_blocking_threshold_; | |
919 net::RequestPriority layout_blocking_priority_threshold_; | |
mmenke
2015/08/07 16:55:10
These really need docs, and could use clearer name
Pat Meenan
2015/08/07 20:48:10
Added docs. Let me know if threshold still doesn'
| |
854 }; | 920 }; |
855 | 921 |
856 ResourceScheduler::ResourceScheduler() | 922 ResourceScheduler::ResourceScheduler() |
857 : should_coalesce_(false), | 923 : should_coalesce_(false), |
858 should_throttle_(false), | 924 should_throttle_(false), |
859 active_clients_loading_(0), | 925 active_clients_loading_(0), |
860 coalesced_clients_(0), | 926 coalesced_clients_(0), |
861 limit_outstanding_requests_(false), | 927 limit_outstanding_requests_(false), |
862 outstanding_request_limit_(0), | 928 outstanding_request_limit_(0), |
863 coalescing_timer_(new base::Timer(true /* retain_user_task */, | 929 coalescing_timer_(new base::Timer(true /* retain_user_task */, |
(...skipping 338 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1202 client->ReprioritizeRequest( | 1268 client->ReprioritizeRequest( |
1203 request, old_priority_params, new_priority_params); | 1269 request, old_priority_params, new_priority_params); |
1204 } | 1270 } |
1205 | 1271 |
1206 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( | 1272 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( |
1207 int child_id, int route_id) { | 1273 int child_id, int route_id) { |
1208 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; | 1274 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; |
1209 } | 1275 } |
1210 | 1276 |
1211 } // namespace content | 1277 } // namespace content |
OLD | NEW |