Bug 147824 - NetworkProcess: DNS prefetch happens in the Web Process
Summary: NetworkProcess: DNS prefetch happens in the Web Process
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebKit2 (show other bugs)
Version: 528+ (Nightly build)
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Nobody
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2015-08-10 01:10 PDT by Carlos Garcia Campos
Modified: 2015-12-22 13:33 PST (History)
10 users (show)

See Also:


Attachments
Patch (14.37 KB, patch)
2015-08-10 01:18 PDT, Carlos Garcia Campos
ap: review-
Details | Formatted Diff | Diff
Updated patch (13.91 KB, patch)
2015-08-11 02:38 PDT, Carlos Garcia Campos
ap: review+
Details | Formatted Diff | Diff
Updated patch (20.39 KB, patch)
2015-10-05 08:36 PDT, Carlos Garcia Campos
no flags Details | Formatted Diff | Diff
Updated patch (17.00 KB, patch)
2015-10-07 01:08 PDT, Carlos Garcia Campos
no flags Details | Formatted Diff | Diff
Updated patch (21.49 KB, patch)
2015-10-19 01:09 PDT, Carlos Garcia Campos
cdumez: review+
cdumez: commit-queue-
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Carlos Garcia Campos 2015-08-10 01:10:53 PDT
DNS prefetch requests started in the WebProcess should be sent to the network process when it's enabled.
Comment 1 Carlos Garcia Campos 2015-08-10 01:18:12 PDT
Created attachment 258612 [details]
Patch
Comment 2 Alexey Proskuryakov 2015-08-10 09:01:38 PDT
rdar://problem/12760351
Comment 3 Alexey Proskuryakov 2015-08-10 09:09:15 PDT
Comment on attachment 258612 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=258612&action=review

Thank you for tackling this, it's good cleanup! I think that this need to be updated to not use strategies though.

> Source/WebCore/html/HTMLAnchorElement.cpp:259
> +                    platformStrategies()->loaderStrategy()->prefetchDNS(document().completeURL(parsedURL).host());

We are gradually getting rid of strategies in favor of client calls. Strategies were needed for the chromium port, and even then, I wasn't quite sure why.

> Source/WebCore/loader/LoaderStrategy.h:31
>  #include <wtf/Vector.h>
> +#include <wtf/text/WTFString.h>

Can probably use wtf/Forward.h here.
Comment 4 Carlos Garcia Campos 2015-08-11 02:38:48 PDT
Created attachment 258713 [details]
Updated patch

Use FrameLoaderClient instead of PlatformStrategies.
Comment 5 Alexey Proskuryakov 2015-08-11 09:00:56 PDT
Comment on attachment 258713 [details]
Updated patch

View in context: https://bugs.webkit.org/attachment.cgi?id=258713&action=review

> Source/WebCore/html/HTMLAnchorElement.cpp:257
> -                    prefetchDNS(document().completeURL(parsedURL).host());
> +                    document().frame()->loader().client().prefetchDNS(document().completeURL(parsedURL).host());

I think that the document can have a null frame here, in which case we'll crash. Please add a test case for this, and fix the crash if I'm right.

> Source/WebCore/loader/LinkLoader.cpp:101
> +            document.frame()->loader().client().prefetchDNS(href.host());

Ditto.

> Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm:632
> +        m_page->mainFrame().loader().client().prefetchDNS(downcast<Element>(*node).absoluteLinkURL().host());

Can m_page be null here?
Comment 6 Carlos Garcia Campos 2015-08-11 09:06:17 PDT
Comment on attachment 258713 [details]
Updated patch

View in context: https://bugs.webkit.org/attachment.cgi?id=258713&action=review

>> Source/WebCore/html/HTMLAnchorElement.cpp:257
>> +                    document().frame()->loader().client().prefetchDNS(document().completeURL(parsedURL).host());
> 
> I think that the document can have a null frame here, in which case we'll crash. Please add a test case for this, and fix the crash if I'm right.

That's why I added && document().frame() to the previous if condition

>> Source/WebCore/loader/LinkLoader.cpp:101
>> +            document.frame()->loader().client().prefetchDNS(href.host());
> 
> Ditto.

Ditto.

>> Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm:632
>> +        m_page->mainFrame().loader().client().prefetchDNS(downcast<Element>(*node).absoluteLinkURL().host());
> 
> Can m_page be null here?

m_page is created in the constructor, and deleted in ::close(), so if we are here we should have a valid page, I can add an assert
Comment 7 Carlos Garcia Campos 2015-08-11 09:13:30 PDT
(In reply to comment #6)
> Comment on attachment 258713 [details]
> Updated patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=258713&action=review
> 
> >> Source/WebCore/html/HTMLAnchorElement.cpp:257
> >> +                    document().frame()->loader().client().prefetchDNS(document().completeURL(parsedURL).host());
> > 
> > I think that the document can have a null frame here, in which case we'll crash. Please add a test case for this, and fix the crash if I'm right.
> 
> That's why I added && document().frame() to the previous if condition
> 
> >> Source/WebCore/loader/LinkLoader.cpp:101
> >> +            document.frame()->loader().client().prefetchDNS(href.host());
> > 
> > Ditto.
> 
> Ditto.
> 
> >> Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm:632
> >> +        m_page->mainFrame().loader().client().prefetchDNS(downcast<Element>(*node).absoluteLinkURL().host());
> > 
> > Can m_page be null here?
> 
> m_page is created in the constructor, and deleted in ::close(), so if we are
> here we should have a valid page, I can add an assert

I could also use node->document().frame()->loader().client() checking frame() is not null, since we are already checking node is not null.
Comment 8 Alexey Proskuryakov 2015-08-11 11:43:36 PDT
Comment on attachment 258713 [details]
Updated patch

Oops.
Comment 9 Carlos Garcia Campos 2015-08-12 00:13:29 PDT
Committed r188331: <http://trac.webkit.org/changeset/188331>
Comment 10 Chris Dumez 2015-09-30 12:48:39 PDT
Reopening as I did a speculative roll out of this patch in <http://trac.webkit.org/changeset/190364> to address a ~2.5% PLT regression. I am not 100% sure this was caused by this patch but it seems the most likely culprit in the regression range. I'll take care of re-landing the patch myself if the perf bots do not recover after the roll out.
Comment 11 Chris Dumez 2015-09-30 17:18:25 PDT
(In reply to comment #10)
> Reopening as I did a speculative roll out of this patch in
> <http://trac.webkit.org/changeset/190364> to address a ~2.5% PLT regression.
> I am not 100% sure this was caused by this patch but it seems the most
> likely culprit in the regression range. I'll take care of re-landing the
> patch myself if the perf bots do not recover after the roll out.

The roll out was successful. The bots showed a 2 to 2.5% progression on page load time after the roll out.
Comment 12 Carlos Garcia Campos 2015-09-30 22:56:22 PDT
The patch is quite simple, it just sends a message to the network process to do the prefetch there. So, my guess is that we are sending too many messages. We could probably cache in the web process the hosts already prefetched to avoid sending messages to the network process.
Comment 13 Chris Dumez 2015-10-01 09:15:45 PDT
(In reply to comment #12)
> The patch is quite simple, it just sends a message to the network process to
> do the prefetch there. So, my guess is that we are sending too many
> messages. We could probably cache in the web process the hosts already
> prefetched to avoid sending messages to the network process.

Yes, too much IPC can definitely slow down the PLT. This has happened a few months back due to diagnostic logging. Looking at the patch, one other possibility (I haven't looked deeply into it) is that the new document.frame() checks sometimes fail and we end up not doing DNS prefetches in cases where we used to?
Comment 14 Carlos Garcia Campos 2015-10-05 08:36:49 PDT
Created attachment 262440 [details]
Updated patch

This patch caches the hosts already prefetched.
Comment 15 Alexey Proskuryakov 2015-10-05 09:14:06 PDT
Comment on attachment 262440 [details]
Updated patch

View in context: https://bugs.webkit.org/attachment.cgi?id=262440&action=review

> Source/WebKit2/NetworkProcess/NetworkProcess.h:197
> +    HashSet<String> m_dnsPrefetchedHosts;

This HashSet grows indefinitely, and NetworkProcess can exist for weeks or even for months.

> Source/WebKit2/WebProcess/WebProcess.h:368
> +    HashSet<String> m_dnsPrefetchedHosts;

Is it OK to keep hosts here for a long time? I think that this may entirely disable the optimization of prefetching link hosts on hover - the hosts were already prefetched when loading, but the OS resolver may have well invalidated the cache already.

DNS prefetching is a really weird thing. We don't have any way to measure its effectiveness, which is not how we usually go about performance optimizations.
Comment 16 Carlos Garcia Campos 2015-10-05 22:41:34 PDT
(In reply to comment #15)
> Comment on attachment 262440 [details]
> Updated patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=262440&action=review
> 
> > Source/WebKit2/NetworkProcess/NetworkProcess.h:197
> > +    HashSet<String> m_dnsPrefetchedHosts;
> 
> This HashSet grows indefinitely, and NetworkProcess can exist for weeks or
> even for months.

I thought about it, my initial idea was to cache only in the web process, since the real performance problem is likely when the same web process requests the same hosts a lot of times, rather than different web processes requesting the same host. So we could simplify it by only caching in the web process and assuming that we might end up requesting the same host by two different web processes.

> > Source/WebKit2/WebProcess/WebProcess.h:368
> > +    HashSet<String> m_dnsPrefetchedHosts;
> 
> Is it OK to keep hosts here for a long time? I think that this may entirely
> disable the optimization of prefetching link hosts on hover - the hosts were
> already prefetched when loading, but the OS resolver may have well
> invalidated the cache already.

Good point. Maybe we should only ensure we don't ask the same hosts in a short period of time, instead. For example when hovering, we start a lot of requests for the same link when the mouse is moving.

> DNS prefetching is a really weird thing. We don't have any way to measure
> its effectiveness, which is not how we usually go about performance
> optimizations.

Yeah, indeed.
Comment 17 Carlos Garcia Campos 2015-10-07 01:08:15 PDT
Created attachment 262588 [details]
Updated patch

Simpler approach, not caching in the network process and clearing the web process cache after a short period of time of no DNS requests. This should avoid asking for the same host more than once when loading a page or hovering a link.
Comment 18 Carlos Garcia Campos 2015-10-15 23:29:46 PDT
Alexey, what do you think about this simpler approach?
Comment 19 Alexey Proskuryakov 2015-10-16 09:30:58 PDT
It makes sense to me overall (I didn't review in detail).

I think that Chris and Gavin would the the right people to decide when it's a good time to land this, so that they can get accurate performance results quickly.
Comment 20 Chris Dumez 2015-10-16 09:58:48 PDT
Comment on attachment 262588 [details]
Updated patch

View in context: https://bugs.webkit.org/attachment.cgi?id=262588&action=review

I like the approach in general.

> Source/WebKit2/WebProcess/WebProcess.cpp:1462
> +    if (!usesNetworkProcess()) {

I think it should be factored so that we only to the usesNetworkProcess() is ENABLE(NETWORK_PROCESS) is true.

e.g.
#if ENABLE(NETWORK_PROCESS)
    if (usesNetworkProcess()) {
        // do stuff
        return;
    }
#endif
    WebCore::prefetchDNS(hostname);

> Source/WebKit2/WebProcess/WebProcess.cpp:1473
> +    static const double dnsPrefetchedHostsCleanupDelay = 5;

I believe we prefer the following pattern now:
const auto dnsPrefetchedHostsCleanupDelay = 5_s;

> Source/WebKit2/WebProcess/WebProcess.cpp:1474
> +    m_dnsPrefetchedHostsCleanupTimer.stop();

Using a hysteresis activity, these 2 lines would become a call to impulse().

> Source/WebKit2/WebProcess/WebProcess.h:220
> +    void didPrefetchDNS(const String&);

Where is this implemented?

> Source/WebKit2/WebProcess/WebProcess.h:221
> +    void initializeDNSPrefetchedHosts(const Vector<String>&);

ditto.

> Source/WebKit2/WebProcess/WebProcess.h:370
> +    WebCore::Timer m_dnsPrefetchedHostsCleanupTimer;

Seems like a good use for HysteresisActivity instead of Timer.
Comment 21 Darin Adler 2015-10-18 15:18:17 PDT
Comment on attachment 262588 [details]
Updated patch

View in context: https://bugs.webkit.org/attachment.cgi?id=262588&action=review

> Source/WebCore/loader/FrameLoaderClient.h:33
> +#include "DNS.h"

We should keep the implementation of the virtual function out of the header, that way we don’t need to include DNS.h in this header. Generally speaking it’s not so valuable to have implementations of virtual functions in the header rather than a cpp file since it’s not common to be able to expand them in line at the call site.

> Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm:632
> +        m_page->mainFrame().loader().client().prefetchDNS(downcast<Element>(*node).absoluteLinkURL().host());

Are we guaranteed that m_page is non-null?
Comment 22 Carlos Garcia Campos 2015-10-19 00:17:15 PDT
(In reply to comment #20)
> Comment on attachment 262588 [details]
> Updated patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=262588&action=review
> 
> I like the approach in general.
> 
> > Source/WebKit2/WebProcess/WebProcess.cpp:1462
> > +    if (!usesNetworkProcess()) {
> 
> I think it should be factored so that we only to the usesNetworkProcess() is
> ENABLE(NETWORK_PROCESS) is true.
> 
> e.g.
> #if ENABLE(NETWORK_PROCESS)
>     if (usesNetworkProcess()) {
>         // do stuff
>         return;
>     }
> #endif
>     WebCore::prefetchDNS(hostname);

Sure!

> > Source/WebKit2/WebProcess/WebProcess.cpp:1473
> > +    static const double dnsPrefetchedHostsCleanupDelay = 5;
> 
> I believe we prefer the following pattern now:
> const auto dnsPrefetchedHostsCleanupDelay = 5_s;
> 
> > Source/WebKit2/WebProcess/WebProcess.cpp:1474
> > +    m_dnsPrefetchedHostsCleanupTimer.stop();
> 
> Using a hysteresis activity, these 2 lines would become a call to impulse().

Ah, I didn't know HysteresisActivity class, it seems fit well here.

> > Source/WebKit2/WebProcess/WebProcess.h:220
> > +    void didPrefetchDNS(const String&);
> 
> Where is this implemented?

Leftovers from previous iteration.

> > Source/WebKit2/WebProcess/WebProcess.h:221
> > +    void initializeDNSPrefetchedHosts(const Vector<String>&);
> 
> ditto.

Ditto.

> > Source/WebKit2/WebProcess/WebProcess.h:370
> > +    WebCore::Timer m_dnsPrefetchedHostsCleanupTimer;
> 
> Seems like a good use for HysteresisActivity instead of Timer.

Indeed, thanks.
Comment 23 Carlos Garcia Campos 2015-10-19 00:20:06 PDT
(In reply to comment #21)
> Comment on attachment 262588 [details]
> Updated patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=262588&action=review
> 
> > Source/WebCore/loader/FrameLoaderClient.h:33
> > +#include "DNS.h"
> 
> We should keep the implementation of the virtual function out of the header,
> that way we don’t need to include DNS.h in this header. Generally speaking
> it’s not so valuable to have implementations of virtual functions in the
> header rather than a cpp file since it’s not common to be able to expand
> them in line at the call site.

I agree, the advantage in this case that we don't have a common cpp file was to avoid having the same implementation in all FrameLoader clients expect the WebKit2 one. I'll make it pure virtual then, to ensure all clients implement it.

> > Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm:632
> > +        m_page->mainFrame().loader().client().prefetchDNS(downcast<Element>(*node).absoluteLinkURL().host());
> 
> Are we guaranteed that m_page is non-null?

Same question ap made in comment #5, which confirms I should add an assert there :-)
Comment 24 Carlos Garcia Campos 2015-10-19 01:09:45 PDT
Created attachment 263456 [details]
Updated patch

Addressed all review comments
Comment 25 Chris Dumez 2015-10-19 09:49:09 PDT
(In reply to comment #23)
> (In reply to comment #21)
> > Comment on attachment 262588 [details]
> > Updated patch
> > 
> > View in context:
> > https://bugs.webkit.org/attachment.cgi?id=262588&action=review
> > 
> > > Source/WebCore/loader/FrameLoaderClient.h:33
> > > +#include "DNS.h"
> > 
> > We should keep the implementation of the virtual function out of the header,
> > that way we don’t need to include DNS.h in this header. Generally speaking
> > it’s not so valuable to have implementations of virtual functions in the
> > header rather than a cpp file since it’s not common to be able to expand
> > them in line at the call site.
> 
> I agree, the advantage in this case that we don't have a common cpp file was
> to avoid having the same implementation in all FrameLoader clients expect
> the WebKit2 one. I'll make it pure virtual then, to ensure all clients
> implement it.
> 
> > > Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm:632
> > > +        m_page->mainFrame().loader().client().prefetchDNS(downcast<Element>(*node).absoluteLinkURL().host());
> > 
> > Are we guaranteed that m_page is non-null?
> 
> Same question ap made in comment #5, which confirms I should add an assert
> there :-)

m_page can be null after WebPage::close() is called so I think we should null-check.
Comment 26 Carlos Garcia Campos 2015-10-19 23:00:52 PDT
(In reply to comment #25)
> (In reply to comment #23)
> > (In reply to comment #21)
> > > Comment on attachment 262588 [details]
> > > Updated patch
> > > 
> > > View in context:
> > > https://bugs.webkit.org/attachment.cgi?id=262588&action=review
> > > 
> > > > Source/WebCore/loader/FrameLoaderClient.h:33
> > > > +#include "DNS.h"
> > > 
> > > We should keep the implementation of the virtual function out of the header,
> > > that way we don’t need to include DNS.h in this header. Generally speaking
> > > it’s not so valuable to have implementations of virtual functions in the
> > > header rather than a cpp file since it’s not common to be able to expand
> > > them in line at the call site.
> > 
> > I agree, the advantage in this case that we don't have a common cpp file was
> > to avoid having the same implementation in all FrameLoader clients expect
> > the WebKit2 one. I'll make it pure virtual then, to ensure all clients
> > implement it.
> > 
> > > > Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm:632
> > > > +        m_page->mainFrame().loader().client().prefetchDNS(downcast<Element>(*node).absoluteLinkURL().host());
> > > 
> > > Are we guaranteed that m_page is non-null?
> > 
> > Same question ap made in comment #5, which confirms I should add an assert
> > there :-)
> 
> m_page can be null after WebPage::close() is called so I think we should
> null-check.

Yes, but I guess you can't tap on a closed page. This is a private method called from two WebPage::potentialTapAtPosition() and WebPage::tapHighlightAtPosition(). Both methods are message handlers, that aren't called if the page is closed, and both methods use m_page without any null check before calling sendTapHighlightForNodeIfNecessary().
Comment 27 Chris Dumez 2015-10-20 10:02:24 PDT
Comment on attachment 263456 [details]
Updated patch

View in context: https://bugs.webkit.org/attachment.cgi?id=263456&action=review

r=me

> Source/WebKit2/WebProcess/WebProcess.cpp:167
> +    , m_dnsPrefetchedHostsCleanupHysteresis(std::bind(&WebProcess::dnsPrefetchedHostsCleanupHysteresisUpdated, this, std::placeholders::_1))

Could have also been all in one line:
, m_dnsPrefetchHysteresis([this] (HystererisState state) { if (state == HysteresisState::Stopped) m_dnsPrefetchedHosts.clear(); });

> Source/WebKit2/WebProcess/WebProcess.h:368
> +    WebCore::HysteresisActivity m_dnsPrefetchedHostsCleanupHysteresis;

I prefer the naming: m_dnsPrefetchHystereris. This way when the activity stops, it is clearer that what stopped is a DNS prefetching activity.
Comment 28 Carlos Garcia Campos 2015-10-20 23:29:03 PDT
(In reply to comment #27)
> Comment on attachment 263456 [details]
> Updated patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=263456&action=review
> 
> r=me

Thanks!

> > Source/WebKit2/WebProcess/WebProcess.cpp:167
> > +    , m_dnsPrefetchedHostsCleanupHysteresis(std::bind(&WebProcess::dnsPrefetchedHostsCleanupHysteresisUpdated, this, std::placeholders::_1))
> 
> Could have also been all in one line:
> , m_dnsPrefetchHysteresis([this] (HystererisState state) { if (state ==
> HysteresisState::Stopped) m_dnsPrefetchedHosts.clear(); });

Yes, I thought about that, but discarded it to avoid the single line if, but being a single statement if it doesn't look that bad, I'll change it.

> > Source/WebKit2/WebProcess/WebProcess.h:368
> > +    WebCore::HysteresisActivity m_dnsPrefetchedHostsCleanupHysteresis;
> 
> I prefer the naming: m_dnsPrefetchHystereris. This way when the activity
> stops, it is clearer that what stopped is a DNS prefetching activity.

Agree
Comment 29 Carlos Garcia Campos 2015-10-21 01:10:03 PDT
Committed r191381: <http://trac.webkit.org/changeset/191381>
Comment 30 Chris Dumez 2015-10-21 09:27:19 PDT
(In reply to comment #29)
> Committed r191381: <http://trac.webkit.org/changeset/191381>

No regression of Mac page load time :)
I'll report on the iOS status later.
Comment 31 Carlos Garcia Campos 2015-10-21 23:53:20 PDT
(In reply to comment #30)
> (In reply to comment #29)
> > Committed r191381: <http://trac.webkit.org/changeset/191381>
> 
> No regression of Mac page load time :)
> I'll report on the iOS status later.

Phew, great, thanks!
Comment 32 Chris Dumez 2015-10-22 09:27:47 PDT
(In reply to comment #31)
> (In reply to comment #30)
> > (In reply to comment #29)
> > > Committed r191381: <http://trac.webkit.org/changeset/191381>
> > 
> > No regression of Mac page load time :)
> > I'll report on the iOS status later.
> 
> Phew, great, thanks!

No page load time regression on iOS either, congrats :)
Comment 33 Ryosuke Niwa 2015-12-17 14:19:30 PST
It looks like this patch regressed Dromaeo by 2-4%.
Comment 34 Alexey Proskuryakov 2015-12-22 12:57:17 PST
> It looks like this patch regressed Dromaeo by 2-4%.

Was that the original, or the latest patch? 

Original: <http://trac.webkit.org/changeset/188331>
Rollout: <http://trac.webkit.org/changeset/190364>
Re-landed: <http://trac.webkit.org/changeset/191381>

If it's the latest, where do we track the regression?
Comment 35 Ryosuke Niwa 2015-12-22 13:33:06 PST
(In reply to comment #34)
> > It looks like this patch regressed Dromaeo by 2-4%.
> 
> Was that the original, or the latest patch? 
> 
> Original: <http://trac.webkit.org/changeset/188331>
> Rollout: <http://trac.webkit.org/changeset/190364>
> Re-landed: <http://trac.webkit.org/changeset/191381>
> 
> If it's the latest, where do we track the regression?

Sorry, I forgot to comment. Looks like the rollout fixed the regression and re-land didn't cause a new regression.