Changeset 282252 in webkit
- Timestamp:
- Sep 9, 2021, 5:28:37 PM (5 years ago)
- Location:
- trunk/Source/WebKit
- Files:
-
- 7 edited
-
ChangeLog (modified) (1 diff)
-
NetworkProcess/NetworkLoad.cpp (modified) (1 diff)
-
NetworkProcess/NetworkLoadScheduler.cpp (modified) (3 diffs)
-
NetworkProcess/NetworkLoadScheduler.h (modified) (4 diffs)
-
NetworkProcess/NetworkProcess.cpp (modified) (2 diffs)
-
NetworkProcess/PreconnectTask.cpp (modified) (3 diffs)
-
NetworkProcess/PreconnectTask.h (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebKit/ChangeLog
r282247 r282252 1 2021-09-09 Ben Nham <nham@apple.com> 2 3 Preconnected socket is sometimes not used for initial request 4 https://bugs.webkit.org/show_bug.cgi?id=229686 5 6 Reviewed by Alex Christensen. 7 8 In the previous release, we preconnected to the main resource origin early in page load from 9 UIProcess to save ~10-15 ms per page load (https://bugs.webkit.org/show_bug.cgi?id=204992). 10 The savings comes from the fact that there's some latency in initiating the request in 11 WebProcess and waiting for the nav delegate reply in UIProcess. We hide that latency by 12 having the eventual main resource request reuse the early preconnected socket. 13 14 However, this optimization doesn't work for connections to known HTTP/1.1 origins due to 15 CFNetwork's in-memory cache of known HTTP/1.1 origins. If an origin is in this cache, and 16 a preconnect is still in flight, then the network stack will go wide immediately and issue 17 the main resource request on a new socket rather than trying to wait for the preconnect to 18 finish and reuse the preconnected socket. Thus the preconneted socket just goes to waste. 19 This can also cause issues with single-threaded web servers that serve only one request at 20 a time, as the main resource request can get head-of-line blocked by the preconnect that 21 is wasted. 22 23 To work around this, this patch tracks a small number of known HTTP/1.1 origins in memory. 24 If there is a pending preconnect to an HTTP/1.1 origin and a main resource request to that 25 origin comes in, then we block the request from going out until the preconnect finishes. 26 This allows the main resource request to utilize the preconnected socket. 27 28 This is worth ~1.25%-2.5% on PLT5 depending on device. 29 30 * NetworkProcess/NetworkLoad.cpp: 31 (WebKit::NetworkLoad::didCompleteWithError): 32 * NetworkProcess/NetworkLoadScheduler.cpp: 33 (WebKit::NetworkLoadScheduler::schedule): 34 (WebKit::NetworkLoadScheduler::unschedule): 35 (WebKit::NetworkLoadScheduler::scheduleLoad): 36 (WebKit::NetworkLoadScheduler::unscheduleLoad): 37 (WebKit::NetworkLoadScheduler::scheduleMainResourceLoad): 38 (WebKit::NetworkLoadScheduler::unscheduleMainResourceLoad): 39 (WebKit::NetworkLoadScheduler::startedPreconnectForMainResource): 40 (WebKit::NetworkLoadScheduler::finishedPreconnectForMainResource): 41 (WebKit::NetworkLoadScheduler::maybePrunePreconnectInfo): 42 (WebKit::NetworkLoadScheduler::isOriginHTTP1X): 43 (WebKit::NetworkLoadScheduler::updateOriginProtocolInfo): 44 * NetworkProcess/NetworkLoadScheduler.h: 45 * NetworkProcess/NetworkProcess.cpp: 46 (WebKit::NetworkProcess::preconnectTo): 47 * NetworkProcess/PreconnectTask.cpp: 48 (WebKit::PreconnectTask::PreconnectTask): 49 (WebKit::PreconnectTask::setTimeout): 50 (WebKit::PreconnectTask::start): 51 * NetworkProcess/PreconnectTask.h: 52 1 53 2021-09-09 Wenson Hsieh <wenson_hsieh@apple.com> 2 54 -
trunk/Source/WebKit/NetworkProcess/NetworkLoad.cpp
r276880 r282252 246 246 { 247 247 if (m_scheduler) { 248 m_scheduler->unschedule(*this );248 m_scheduler->unschedule(*this, &networkLoadMetrics); 249 249 m_scheduler = nullptr; 250 250 } -
trunk/Source/WebKit/NetworkProcess/NetworkLoadScheduler.cpp
r274707 r282252 27 27 #include "NetworkLoadScheduler.h" 28 28 29 #include "Logging.h" 29 30 #include "NetworkLoad.h" 30 #include < wtf/ListHashSet.h>31 #include <WebCore/ResourceError.h> 31 32 32 33 namespace WebKit { 33 34 34 35 static constexpr size_t maximumActiveCountForLowPriority = 2; 36 static constexpr size_t maximumTrackedHTTP1XOrigins = 128; 35 37 36 38 class NetworkLoadScheduler::HostContext { … … 118 120 void NetworkLoadScheduler::schedule(NetworkLoad& load) 119 121 { 122 bool isMainFrameMainResource = load.currentRequest().isTopSite(); 123 if (isMainFrameMainResource) 124 scheduleMainResourceLoad(load); 125 else 126 scheduleLoad(load); 127 } 128 129 void NetworkLoadScheduler::unschedule(NetworkLoad& load, const WebCore::NetworkLoadMetrics* metrics) 130 { 131 bool isMainFrameMainResource = load.currentRequest().isTopSite(); 132 if (isMainFrameMainResource) 133 unscheduleMainResourceLoad(load, metrics); 134 else 135 unscheduleLoad(load); 136 } 137 138 void NetworkLoadScheduler::scheduleLoad(NetworkLoad& load) 139 { 120 140 auto* context = contextForLoad(load); 121 141 … … 128 148 } 129 149 130 void NetworkLoadScheduler::unschedule (NetworkLoad& load)150 void NetworkLoadScheduler::unscheduleLoad(NetworkLoad& load) 131 151 { 132 152 if (auto* context = contextForLoad(load)) 133 153 context->unschedule(load); 154 } 155 156 void NetworkLoadScheduler::scheduleMainResourceLoad(NetworkLoad& load) 157 { 158 String protocolHostAndPort = load.url().protocolHostAndPort(); 159 if (!isOriginHTTP1X(protocolHostAndPort)) { 160 load.start(); 161 return; 162 } 163 164 auto iter = m_pendingMainResourcePreconnects.find(protocolHostAndPort); 165 if (iter == m_pendingMainResourcePreconnects.end()) { 166 load.start(); 167 return; 168 } 169 170 PendingMainResourcePreconnectInfo& info = iter->value; 171 if (!info.pendingPreconnects) { 172 load.start(); 173 return; 174 } 175 176 --info.pendingPreconnects; 177 info.pendingLoads.add(&load); 178 RELEASE_LOG(Network, "%p - NetworkLoadScheduler::scheduleMainResourceLoad deferring load %p; %u pending preconnects; %u pending loads", this, &load, info.pendingPreconnects, info.pendingLoads.size()); 179 } 180 181 void NetworkLoadScheduler::unscheduleMainResourceLoad(NetworkLoad& load, const WebCore::NetworkLoadMetrics* metrics) 182 { 183 String protocolHostAndPort = load.url().protocolHostAndPort(); 184 185 if (metrics) 186 updateOriginProtocolInfo(protocolHostAndPort, metrics->protocol); 187 188 auto iter = m_pendingMainResourcePreconnects.find(protocolHostAndPort); 189 if (iter == m_pendingMainResourcePreconnects.end()) 190 return; 191 192 PendingMainResourcePreconnectInfo& info = iter->value; 193 if (info.pendingLoads.remove(&load)) 194 maybePrunePreconnectInfo(iter); 195 } 196 197 void NetworkLoadScheduler::startedPreconnectForMainResource(const URL& url) 198 { 199 auto iter = m_pendingMainResourcePreconnects.find(url.protocolHostAndPort()); 200 if (iter != m_pendingMainResourcePreconnects.end()) { 201 PendingMainResourcePreconnectInfo& info = iter->value; 202 info.pendingPreconnects++; 203 return; 204 } 205 206 PendingMainResourcePreconnectInfo info; 207 m_pendingMainResourcePreconnects.add(url.protocolHostAndPort(), WTFMove(info)); 208 } 209 210 void NetworkLoadScheduler::finishedPreconnectForMainResource(const URL& url, const WebCore::ResourceError& error) 211 { 212 auto iter = m_pendingMainResourcePreconnects.find(url.protocolHostAndPort()); 213 if (iter == m_pendingMainResourcePreconnects.end()) 214 return; 215 216 PendingMainResourcePreconnectInfo& info = iter->value; 217 if (!info.pendingLoads.isEmpty()) { 218 NetworkLoad* load = info.pendingLoads.takeFirst(); 219 RELEASE_LOG(Network, "%p - NetworkLoadScheduler::finishedPreconnectForMainResource (error: %d) starting delayed main resource load %p; %u pending preconnects; %u total pending loads", this, static_cast<int>(error.type()), load, info.pendingPreconnects, info.pendingLoads.size()); 220 load->start(); 221 } else 222 --info.pendingPreconnects; 223 224 maybePrunePreconnectInfo(iter); 225 } 226 227 void NetworkLoadScheduler::maybePrunePreconnectInfo(PendingPreconnectMap::iterator& iter) 228 { 229 PendingMainResourcePreconnectInfo& info = iter->value; 230 if (!info.pendingPreconnects && info.pendingLoads.isEmpty()) 231 m_pendingMainResourcePreconnects.remove(iter); 232 } 233 234 235 bool NetworkLoadScheduler::isOriginHTTP1X(const String& protocolHostAndPort) 236 { 237 return m_http1XOrigins.contains(protocolHostAndPort); 238 } 239 240 void NetworkLoadScheduler::updateOriginProtocolInfo(const String& protocolHostAndPort, const String& alpnProtocolID) 241 { 242 if (alpnProtocolID != "http/1.1"_s) { 243 m_http1XOrigins.remove(protocolHostAndPort); 244 return; 245 } 246 247 if (m_http1XOrigins.size() >= maximumTrackedHTTP1XOrigins) 248 m_http1XOrigins.remove(m_http1XOrigins.random()); 249 250 m_http1XOrigins.add(protocolHostAndPort); 134 251 } 135 252 -
trunk/Source/WebKit/NetworkProcess/NetworkLoadScheduler.h
r271946 r282252 27 27 28 28 #include <WebCore/LoadSchedulingMode.h> 29 #include <WebCore/NetworkLoadMetrics.h> 29 30 #include <WebCore/PageIdentifier.h> 30 31 #include <wtf/HashMap.h> 32 #include <wtf/HashSet.h> 33 #include <wtf/ListHashSet.h> 31 34 #include <wtf/WeakPtr.h> 35 36 namespace WebCore { 37 class ResourceError; 38 } 32 39 33 40 namespace WebKit { … … 42 49 43 50 void schedule(NetworkLoad&); 44 void unschedule(NetworkLoad&); 51 void unschedule(NetworkLoad&, const WebCore::NetworkLoadMetrics* = nullptr); 52 53 void startedPreconnectForMainResource(const URL&); 54 void finishedPreconnectForMainResource(const URL&, const WebCore::ResourceError&); 45 55 46 56 void setResourceLoadSchedulingMode(WebCore::PageIdentifier, WebCore::LoadSchedulingMode); … … 49 59 50 60 private: 61 void scheduleLoad(NetworkLoad&); 62 void unscheduleLoad(NetworkLoad&); 63 64 void scheduleMainResourceLoad(NetworkLoad&); 65 void unscheduleMainResourceLoad(NetworkLoad&, const WebCore::NetworkLoadMetrics*); 66 67 bool isOriginHTTP1X(const String&); 68 void updateOriginProtocolInfo(const String&, const String&); 69 51 70 class HostContext; 52 71 HostContext* contextForLoad(const NetworkLoad&); … … 54 73 using PageContext = HashMap<String, std::unique_ptr<HostContext>>; 55 74 HashMap<WebCore::PageIdentifier, std::unique_ptr<PageContext>> m_pageContexts; 75 76 struct PendingMainResourcePreconnectInfo { 77 unsigned pendingPreconnects {1}; 78 ListHashSet<NetworkLoad *> pendingLoads; 79 }; 80 using PendingPreconnectMap = HashMap<String, PendingMainResourcePreconnectInfo>; 81 PendingPreconnectMap m_pendingMainResourcePreconnects; 82 83 void maybePrunePreconnectInfo(PendingPreconnectMap::iterator&); 84 85 HashSet<String> m_http1XOrigins; 56 86 }; 57 87 -
trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp
r282170 r282252 1388 1388 parameters.request.setIsAppInitiated(lastNavigationWasAppInitiated == LastNavigationWasAppInitiated::Yes); 1389 1389 parameters.request.setFirstPartyForCookies(url); 1390 parameters.request.setPriority(WebCore::ResourceLoadPriority::VeryHigh); 1390 1391 parameters.webPageProxyID = webPageProxyID; 1391 1392 parameters.webPageID = webPageID; … … 1399 1400 parameters.shouldPreconnectOnly = PreconnectOnly::Yes; 1400 1401 1401 (new PreconnectTask(*networkSession, WTFMove(parameters), [](const WebCore::ResourceError&) { }))->start(); 1402 networkSession->networkLoadScheduler().startedPreconnectForMainResource(url); 1403 auto task = new PreconnectTask(*networkSession, WTFMove(parameters), [networkSession, url](const WebCore::ResourceError& error) { 1404 networkSession->networkLoadScheduler().finishedPreconnectForMainResource(url, error); 1405 }); 1406 task->setTimeout(10_s); 1407 task->start(); 1402 1408 #else 1403 1409 UNUSED_PARAM(url); -
trunk/Source/WebKit/NetworkProcess/PreconnectTask.cpp
r273183 r282252 42 42 PreconnectTask::PreconnectTask(NetworkSession& networkSession, NetworkLoadParameters&& parameters, CompletionHandler<void(const ResourceError&)>&& completionHandler) 43 43 : m_completionHandler(WTFMove(completionHandler)) 44 , m_timeout(60_s) 44 45 , m_timeoutTimer([this] { didFinish(ResourceError { String(), 0, m_networkLoad->parameters().request.url(), "Preconnection timed out"_s, ResourceError::Type::Timeout }); }) 45 46 { … … 48 49 ASSERT(parameters.shouldPreconnectOnly == PreconnectOnly::Yes); 49 50 m_networkLoad = makeUnique<NetworkLoad>(*this, nullptr, WTFMove(parameters), networkSession); 50 m_timeoutTimer.startOneShot(60000_s);51 51 } 52 52 … … 56 56 } 57 57 58 void PreconnectTask::setTimeout(Seconds timeout) 59 { 60 m_timeout = timeout; 61 } 62 58 63 void PreconnectTask::start() 59 64 { 65 m_timeoutTimer.startOneShot(m_timeout); 60 66 m_networkLoad->start(); 61 67 } -
trunk/Source/WebKit/NetworkProcess/PreconnectTask.h
r277858 r282252 46 46 47 47 void setH2PingCallback(const URL&, CompletionHandler<void(Expected<WTF::Seconds, WebCore::ResourceError>&&)>&&); 48 void setTimeout(Seconds); 48 49 void start(); 49 50 … … 63 64 std::unique_ptr<NetworkLoad> m_networkLoad; 64 65 CompletionHandler<void(const WebCore::ResourceError&)> m_completionHandler; 66 Seconds m_timeout; 65 67 WebCore::Timer m_timeoutTimer; 66 68 };
Note:
See TracChangeset
for help on using the changeset viewer.