Bug 231283

Summary: [Build-time perf] Forward-declare more things in Element.h
Product: WebKit Reporter: Jer Noble <jer.noble>
Component: New BugsAssignee: Jer Noble <jer.noble>
Status: RESOLVED FIXED    
Severity: Normal CC: aboxhall, allan.jensen, andresg_22, annulen, apinheiro, berto, calvaris, cdumez, cfleizach, cgarcia, changseok, cmarcelo, darin, dbarton, dino, dmazzoni, eric.carlson, esprehn+autocc, ews-watchlist, fmalita, fred.wang, glenn, gustavo, gyuyoung.kim, hi, jamesr, japhet, jcraig, jdiggs, joepeck, kangil.han, keith_miller, kondapallykalyan, luiz, macpherson, mark.lam, menard, mifenton, mmaxfield, msaboff, pangle, pdr, philipj, ryuan.choi, saam, sabouhallawa, samuel_white, schenney, sergio, simon.fraser, tonikitoo, tzagallo, webkit-bug-importer
Priority: P2 Keywords: InRadar
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   
Attachments:
Description Flags
Patch
none
Patch
ews-feeder: commit-queue-
Patch
none
Patch
ews-feeder: commit-queue-
Patch
none
Patch
none
Patch
none
Patch
none
Patch
ews-feeder: commit-queue-
Patch
none
Patch
none
Patch
ews-feeder: commit-queue-
Patch
none
Patch
none
Patch
ews-feeder: commit-queue-
Patch for landing none

Description Jer Noble 2021-10-05 23:46:47 PDT
[Build-time perf] Forward-declare more things in Element.h
Comment 1 Jer Noble 2021-10-06 00:31:48 PDT
Created attachment 440341 [details]
Patch
Comment 2 Jer Noble 2021-10-06 01:02:15 PDT
Created attachment 440343 [details]
Patch
Comment 3 Jer Noble 2021-10-06 01:21:32 PDT
Created attachment 440345 [details]
Patch
Comment 4 Jer Noble 2021-10-06 09:31:58 PDT
Created attachment 440377 [details]
Patch
Comment 5 Jer Noble 2021-10-06 09:41:23 PDT
Created attachment 440379 [details]
Patch
Comment 6 Jer Noble 2021-10-06 11:51:37 PDT
Created attachment 440401 [details]
Patch
Comment 7 EWS Watchlist 2021-10-06 11:53:00 PDT
Thanks for the patch. If this patch contains new public API please make sure it follows the guidelines for new WebKit2 GTK+ API. See https://trac.webkit.org/wiki/WebKitGTK/AddingNewWebKit2API
Comment 8 Darin Adler 2021-10-06 12:12:59 PDT
Comment on attachment 440401 [details]
Patch

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

Can we measure a build speed-up? I love these types of changes, and want to make sure we do them based on evidence.

> Source/WebCore/ChangeLog:9
> +        Replace as many #includes as possible with forward type declarations. To do so, split out the inline function
> +        definitions into their own ElementInlines.h header.

How do we know when to add includes of ElementInines.h?

Do we get compilation failures when we forget them?

> Source/JavaScriptCore/heap/HandleForward.h:32
> +template <class T> class Handle;

Better syntax:

    template<typename> class Handle;

No need to include a name if it’s just "T".

> Source/JavaScriptCore/heap/StrongForward.h:32
> +template <typename T, ShouldStrongDestructorGrabLock = ShouldStrongDestructorGrabLock::No> class Strong;

I prefer this syntax:

    template<typename, ShouldStrongDestructorGrabLock = ShouldStrongDestructorGrabLock::No> class Strong;

> Source/WebCore/html/HTMLMediaElement.h:588
> +    void privateBrowsingStateDidChange(const PAL::SessionID&);

This is an unfortunate change. Since a session ID is just a scalar, we would like to pass by value. The change to use const& should not be necessary. We should be able to compile a declaration without the class being defined, although not calls to the function. Did that not work?

> Source/WebCore/html/HTMLMediaElement.h:953
> +    void setControllerJSProperty(const char*, JSC::JSValue&&);

This change doesn’t makes sense. JSValue is a scalar, and it doesn’t make sense to pass it by rvalue reference.
Comment 9 Jer Noble 2021-10-06 12:32:16 PDT
Created attachment 440415 [details]
Patch
Comment 10 Joseph Pecoraro 2021-10-06 12:39:15 PDT
Comment on attachment 440415 [details]
Patch

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

> Source/WebCore/ChangeLog:22
> +        With these changes in place, the net cost of Element.h during a typical build has gone from ~750s to ~57s.

Nice!
Comment 11 Jer Noble 2021-10-06 12:47:43 PDT
Created attachment 440417 [details]
Patch
Comment 12 Darin Adler 2021-10-06 12:55:55 PDT
Comment on attachment 440417 [details]
Patch

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

> Source/WebCore/dom/EventOptions.h:33
> +enum class EventIsTrusted : uint8_t { No, Yes };
> +enum class EventCanBubble : uint8_t { No, Yes };
> +enum class EventIsCancelable : uint8_t { No, Yes };
> +enum class EventIsComposed : uint8_t { No, Yes };

How about bool instead of uint8_t for these?
Comment 13 Jer Noble 2021-10-06 13:34:23 PDT
Created attachment 440432 [details]
Patch
Comment 14 Jer Noble 2021-10-06 13:50:27 PDT
(Sorry Darin, not ignoring your comments; just trying to fix all the build errors first.)
Comment 15 Darin Adler 2021-10-06 14:00:41 PDT
(In reply to Jer Noble from comment #14)
> (Sorry Darin, not ignoring your comments; just trying to fix all the build
> errors first.)

No rush.
Comment 16 Jer Noble 2021-10-06 14:01:42 PDT
(In reply to Darin Adler from comment #8)
> Comment on attachment 440401 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=440401&action=review
> 
> Can we measure a build speed-up? I love these types of changes, and want to
> make sure we do them based on evidence.

Clang recently added support for doing time-based profiling. Passing in the -ftime-trace flag to clang will generate .json files containing time information on a per-source-file, per-header, per-function, and per-template basis. I'm using a utility called ClangBuildAnalyzer <https://github.com/aras-p/ClangBuildAnalyzer> to pull all the .json files together for net timing information.

Before these changes, that tool reported the following for Element.h:

*** Expensive headers:
754617 ms: dom/Element.h (included 285 times, avg 2647 ms), included via:
  AttributeChangeInvalidation.cpp AttributeChangeInvalidation.h  (6086 ms)
  RenderTreeUpdater.cpp RenderTreeUpdater.h RenderTreeBuilder.h RenderTreePosition.h RenderElement.h RenderObject.h  (5736 ms)
  RenderTreeBuilderInline.cpp RenderTreeBuilderInline.h RenderTreeBuilder.h RenderTreePosition.h RenderElement.h RenderObject.h  (5583 ms)
  PropertyCascade.cpp PropertyCascade.h ElementRuleCollector.h RuleSet.h RuleData.h SelectorFilter.h  (5129 ms)
  SVGTextQuery.cpp LegacyInlineFlowBox.h LegacyInlineBox.h RenderBoxModelObject.h RenderLayerModelObject.h RenderElement.h RenderObject.h  (4900 ms)
  StyleResolver.cpp StyleResolver.h ElementRuleCollector.h RuleSet.h RuleData.h SelectorFilter.h  (4847 ms)

Afterward:

57889 ms: dom/Element.h (included 287 times, avg 201 ms), included via:
  SelectorCompiler.cpp SelectorCompiler.h SelectorChecker.h  (1171 ms)
  VisitedLinkState.cpp VisitedLinkState.h  (1029 ms)
  AttributeChangeInvalidation.cpp AttributeChangeInvalidation.h  (1013 ms)
  CachedScriptFetcher.cpp CachedResourceLoader.h CachedResourceRequest.h  (1001 ms)
  JSHTMLOptionsCollection.cpp JSHTMLOptionsCollection.h HTMLOptionsCollection.h CachedHTMLCollection.h HTMLCollection.h LiveNodeList.h CollectionTraversal.h ElementIterator.h ElementTraversal.h  (949 ms)
  PseudoElement.cpp PseudoElement.h  (945 ms)
  ...

> > Source/WebCore/ChangeLog:9
> > +        Replace as many #includes as possible with forward type declarations. To do so, split out the inline function
> > +        definitions into their own ElementInlines.h header.
> 
> How do we know when to add includes of ElementInines.h?
> 
> Do we get compilation failures when we forget them?

Unfortunately no; if you don't include ElementInlines.h you'll get a linker error, not a compile error. I'm searching for a mechanism to annotate these function declarations to generate a compiler error if you don't also include ElementInlines.h, but I haven't found it yet.

> > Source/JavaScriptCore/heap/HandleForward.h:32
> > +template <class T> class Handle;
> 
> Better syntax:
> 
>     template<typename> class Handle;
> 
> No need to include a name if it’s just "T".

Ok.

> > Source/JavaScriptCore/heap/StrongForward.h:32
> > +template <typename T, ShouldStrongDestructorGrabLock = ShouldStrongDestructorGrabLock::No> class Strong;
> 
> I prefer this syntax:
> 
>     template<typename, ShouldStrongDestructorGrabLock =
> ShouldStrongDestructorGrabLock::No> class Strong;

Ok.

> > Source/WebCore/html/HTMLMediaElement.h:588
> > +    void privateBrowsingStateDidChange(const PAL::SessionID&);
> 
> This is an unfortunate change. Since a session ID is just a scalar, we would
> like to pass by value. The change to use const& should not be necessary. We
> should be able to compile a declaration without the class being defined,
> although not calls to the function. Did that not work?

Let me try that; I was under the impression that all function parameters had to be fully defined (but not function return types), but I absolutely could be mistaken.

> > Source/WebCore/html/HTMLMediaElement.h:953
> > +    void setControllerJSProperty(const char*, JSC::JSValue&&);
> 
> This change doesn’t makes sense. JSValue is a scalar, and it doesn’t make
> sense to pass it by rvalue reference.

Same here. Will try.
Comment 17 Darin Adler 2021-10-06 14:06:47 PDT
(In reply to Jer Noble from comment #16)
> I'm using a utility called ClangBuildAnalyzer
> <https://github.com/aras-p/ClangBuildAnalyzer> to pull all the .json files
> together for net timing information.

One of the WebKit team best practices has been to put these kinds of analyses up where people can see them, leads to competition and teamwork to fix the problems. For example, long ago Kling put up a list of the largest functions, and we cut them down to size.

I think you should consider putting this somewhere people can see it so they don’t need to learn to run the tool.
Comment 18 Darin Adler 2021-10-06 14:07:56 PDT
(In reply to Jer Noble from comment #16)
> Unfortunately no; if you don't include ElementInlines.h you'll get a linker
> error, not a compile error.

Linker error in release builds only? Or linker error even in debug builds?
Comment 19 Jer Noble 2021-10-06 14:11:47 PDT
(In reply to Jer Noble from comment #16)
> Unfortunately no; if you don't include ElementInlines.h you'll get a linker
> error, not a compile error. I'm searching for a mechanism to annotate these
> function declarations to generate a compiler error if you don't also include
> ElementInlines.h, but I haven't found it yet.

Oh duh. I was searching for things like __attribute__(always_inline), but the answer is much simpler:

class Foo {
public:
    inline void bar();
};

void test(Foo foo)
{
    foo.bar();
}

<source>:3:17: warning: inline function 'Foo::bar' is not defined [-Wundefined-inline]
    inline void bar();
                ^
<source>:8:9: note: used here
    foo.bar();
        ^
1 warning generated.

We just need to decorate all the inlined functions in ElementInlines.h with `inline` in Element.h.
Comment 20 Darin Adler 2021-10-06 14:15:18 PDT
(In reply to Jer Noble from comment #19)
> We just need to decorate all the inlined functions in ElementInlines.h with
> `inline` in Element.h.

Seems like it would also be a big win to do this in the many places in JavaScriptCore that use this same idiom. And for functions where we define them in one header, but implement in another (Node.h vs. Element.h). This may not work quite as well for templates, but it sure does seem like it will help us a lot!
Comment 21 Jer Noble 2021-10-06 14:16:57 PDT
(In reply to Darin Adler from comment #17)
> (In reply to Jer Noble from comment #16)
> > I'm using a utility called ClangBuildAnalyzer
> > <https://github.com/aras-p/ClangBuildAnalyzer> to pull all the .json files
> > together for net timing information.
> 
> One of the WebKit team best practices has been to put these kinds of
> analyses up where people can see them, leads to competition and teamwork to
> fix the problems. For example, long ago Kling put up a list of the largest
> functions, and we cut them down to size.
> 
> I think you should consider putting this somewhere people can see it so they
> don’t need to learn to run the tool.

That's a great idea. I'll put together a Wiki.
Comment 22 Jer Noble 2021-10-06 14:27:15 PDT
(In reply to Jer Noble from comment #16)
> Let me try that; I was under the impression that all function parameters had
> to be fully defined (but not function return types), but I absolutely could
> be mistaken.

And indeed I was mistaken; reverting that change (to just pass in a scalar) works just fine with a forward-definition. Will revert the other as well.
Comment 23 Jer Noble 2021-10-06 14:48:46 PDT
Decorating with `inline` revealed this fun warning/error:

Source/WebCore/dom/Element.h:130:32: error: inline function 'WebCore::Element::hasAttributes' is not defined [-Werror,-Wundefined-inline]
    inline WEBCORE_EXPORT bool hasAttributes() const;
                               ^
In file included from WebKitBuild/Debug/DerivedSources/WebCore/unified-sources/UnifiedSource5-mm.mm:7:
In file included from ./accessibility/mac/AccessibilityObjectMac.mm:30:
In file included from Source/WebCore/dom/ElementAncestorIterator.h:28:
In file included from Source/WebCore/dom/ElementIterator.h:28:
Source/WebCore/dom/ElementInlines.h:54:59: note: used here
    return is<Element>(*this) && downcast<Element>(*this).hasAttributes();

Looks like (A) this decoration works and (B) I have to make sure hasAttributes() appears first in ElementInlines.h.
Comment 24 Jer Noble 2021-10-06 14:49:45 PDT
(In reply to Jer Noble from comment #23)
> Looks like (A) this decoration works and (B) I have to make sure
> hasAttributes() appears first in ElementInlines.h.

Disregard. (A) works but I mistakenly decorated Element::hasAttributes() instead of Node::hasAttributes().
Comment 25 Jer Noble 2021-10-06 14:58:43 PDT
Created attachment 440442 [details]
Patch
Comment 26 Jer Noble 2021-10-06 22:01:12 PDT
Created attachment 440467 [details]
Patch
Comment 27 Jer Noble 2021-10-07 14:46:37 PDT
Created attachment 440540 [details]
Patch
Comment 28 Jer Noble 2021-10-07 15:36:30 PDT
Created attachment 440548 [details]
Patch
Comment 29 Jer Noble 2021-10-07 15:50:33 PDT
Created attachment 440549 [details]
Patch
Comment 30 Jer Noble 2021-10-07 15:51:08 PDT
FYI: here's the Wiki which I've been updating as I go: <https://trac.webkit.org/wiki/AnalyzingBuildPerformance>
Comment 31 Jer Noble 2021-10-08 09:19:54 PDT
Created attachment 440630 [details]
Patch
Comment 32 Jer Noble 2021-10-08 10:01:43 PDT
Created attachment 440635 [details]
Patch for landing
Comment 33 EWS 2021-10-08 16:32:03 PDT
Committed r283851 (242730@main): <https://commits.webkit.org/242730@main>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 440635 [details].
Comment 34 Radar WebKit Bug Importer 2021-10-08 16:34:01 PDT
<rdar://problem/84048751>