RESOLVED FIXED 216208
Move all remaining flags from ElementRareData to Node to reduce the frequency
https://bugs.webkit.org/show_bug.cgi?id=216208
Summary Move all remaining flags from ElementRareData to Node to reduce the frequency
Ryosuke Niwa
Reported 2020-09-04 17:49:11 PDT
Move the remaining bit flags from ElementRareData to Node::m_nodeFlags to avoid the creation of ElementRareData in more cases.
Attachments
Patch (19.79 KB, patch)
2020-09-04 18:41 PDT, Ryosuke Niwa
no flags
Fixed builds (19.79 KB, patch)
2020-09-04 19:59 PDT, Ryosuke Niwa
no flags
Fixed builds 2 (20.39 KB, patch)
2020-09-05 00:43 PDT, Ryosuke Niwa
no flags
Updated per Darin's review comments (22.14 KB, patch)
2020-09-05 16:31 PDT, Ryosuke Niwa
no flags
Patch for landing (22.10 KB, patch)
2020-09-07 20:02 PDT, Ryosuke Niwa
no flags
Ryosuke Niwa
Comment 1 2020-09-04 17:50:03 PDT
We can also dramatically reduce the frequency of the creation of ElementRareData when tabIndex is set if we just special case 0 and -1. I've done some study on various poplar websites, and they also almost use 0 and -1 as follows: d = {}; document.querySelectorAll('[tabindex]').forEach((e) => { if (e.tabIndex in d) d[e.tabIndex]++; else d[e.tabIndex]=0; }); Gmail {0: 176, -1: 1300} = $5 Slack {0: 4, 1: 0, -1: 130} = Facebook {0: 151, -1: 12} Twitter {0: 31} Quip {0: 53} CNN {0: 7, -1: 10} NYTimes {0: 1, 99: 0, 100: 0, -1: 0} YouTube {0: 59, -1: 186}
Ryosuke Niwa
Comment 2 2020-09-04 18:41:34 PDT
Yusuke Suzuki
Comment 3 2020-09-04 19:03:34 PDT
Awesome!
Ryosuke Niwa
Comment 4 2020-09-04 19:59:27 PDT
Created attachment 408063 [details] Fixed builds
Darin Adler
Comment 5 2020-09-04 21:21:03 PDT
Comment on attachment 408063 [details] Fixed builds ElementRareData size assertion is failing on Windows
Ryosuke Niwa
Comment 6 2020-09-05 00:43:19 PDT
Created attachment 408069 [details] Fixed builds 2
Darin Adler
Comment 7 2020-09-05 08:33:39 PDT
Comment on attachment 408069 [details] Fixed builds 2 View in context: https://bugs.webkit.org/attachment.cgi?id=408069&action=review Great! > Source/WebCore/dom/Element.cpp:247 > + clearFlag(TabIndexIsZeroOrInRareDataFlag); I think it would be easier to reason about this if it was treated as a 4-value enumeration rather than two flags. The technique is great but the logic is confusing this way. > Source/WebCore/dom/Element.cpp:269 > + bool isOne = getFlag(TabIndexIsZeroOrInRareDataFlag); Did you mean to name this “isZero”? > Source/WebCore/dom/ElementRareData.h:60 > + int tabIndex() const { return m_tabIndex; } The name here isn’t great because this is not the tab index in the 0, -1, or “no tab index” cases. And also 0 is a special value meaning “not stored here”, not a tab index of 0. I think a different name could make some of that clearer. Like maybe “unusual tab index”. And a comment somewhere explaining the special meaning of 0? Maybe an assertion that the getter isn’t called when m_tabIndex is 0? > Source/WebCore/dom/ElementRareData.h:61 > + void setTabIndex(int); Ditto on the name. > Source/WebCore/dom/ElementRareData.h:161 > + int m_tabIndex { 0 }; Ditto. > Source/WebCore/dom/Node.cpp:137 > + case NodeRareData::UseType::PseudoElements: Sorting the cases makes sense when an enumeration gets long like this. > Source/WebCore/dom/Node.h:548 > +#if ENABLE(FULLSCREEN_API) Would it be better to not put this inside an #if? I can never tell; certainly more conditionals make the code messier.
Ryosuke Niwa
Comment 8 2020-09-05 15:03:54 PDT
(In reply to Darin Adler from comment #7) > Comment on attachment 408069 [details] > Fixed builds 2 > > View in context: > https://bugs.webkit.org/attachment.cgi?id=408069&action=review > > Great! > > > Source/WebCore/dom/Element.cpp:247 > > + clearFlag(TabIndexIsZeroOrInRareDataFlag); > > I think it would be easier to reason about this if it was treated as a > 4-value enumeration rather than two flags. The technique is great but the > logic is confusing this way. Okay, I was debating between those two options myself. Let's add an enum class like this: static constexpr unsigned s_tabIndexStateBitOffset = 30; ... TabIndexStateFirstBit = 1 << s_tabIndexStateBitOffset, DefaultNodeFlags = IsParsingChildrenFinishedFlag }; enum class TabIndexState : uint8_t { NotSet = 0, Zero = 1, NegativeOne = 2, SetInRareData = 3, }; ... TabIndexState tabIndexState() { return static_cast<TabIndexState>(m_nodeFlags >> s_tabIndexStateBitOffset); } void setTabIndexState(TabIndexState state) { return m_nodeFlags = static_cast<uint32_t>(state) << s_tabIndexStateBitOffset; } > > Source/WebCore/dom/Element.cpp:269 > > + bool isOne = getFlag(TabIndexIsZeroOrInRareDataFlag); > > Did you mean to name this “isZero”? Oops, yeah, but this code is rewritten now. > > Source/WebCore/dom/ElementRareData.h:60 > > + int tabIndex() const { return m_tabIndex; } > > The name here isn’t great because this is not the tab index in the 0, -1, or > “no tab index” cases. And also 0 is a special value meaning “not stored > here”, not a tab index of 0. I think a different name could make some of > that clearer. Like maybe “unusual tab index”. And a comment somewhere > explaining the special meaning of 0? Maybe an assertion that the getter > isn’t called when m_tabIndex is 0? Sure. This was another thing I was thinking of doing anyway. I was thinking of "uncommon" but "unusual" works too. And let's add an assertion as you say in the getter since I already have an assertion & comment in the setter. > > Source/WebCore/dom/Node.h:548 > > +#if ENABLE(FULLSCREEN_API) > > Would it be better to not put this inside an #if? I can never tell; > certainly more conditionals make the code messier. I'd leave this for now but I agree with this. There is no point in build-guarding every code like this.
Darin Adler
Comment 9 2020-09-05 15:20:58 PDT
Comment on attachment 408069 [details] Fixed builds 2 View in context: https://bugs.webkit.org/attachment.cgi?id=408069&action=review >>> Source/WebCore/dom/Element.cpp:247 >>> + clearFlag(TabIndexIsZeroOrInRareDataFlag); >> >> I think it would be easier to reason about this if it was treated as a 4-value enumeration rather than two flags. The technique is great but the logic is confusing this way. > > Okay, I was debating between those two options myself. Let's add an enum class like this: > > static constexpr unsigned s_tabIndexStateBitOffset = 30; > > ... > > TabIndexStateFirstBit = 1 << s_tabIndexStateBitOffset, > > DefaultNodeFlags = IsParsingChildrenFinishedFlag > }; > > enum class TabIndexState : uint8_t { > NotSet = 0, > Zero = 1, > NegativeOne = 2, > SetInRareData = 3, > }; > > ... > > TabIndexState tabIndexState() { return static_cast<TabIndexState>(m_nodeFlags >> s_tabIndexStateBitOffset); } > void setTabIndexState(TabIndexState state) { return m_nodeFlags = static_cast<uint32_t>(state) << s_tabIndexStateBitOffset; } Looks like there is some masking missing from the code you wrote there.
Ryosuke Niwa
Comment 10 2020-09-05 16:31:17 PDT
(In reply to Darin Adler from comment #9) > Comment on attachment 408069 [details] > Fixed builds 2 > > > TabIndexState tabIndexState() { return static_cast<TabIndexState>(m_nodeFlags >> s_tabIndexStateBitOffset); } > > void setTabIndexState(TabIndexState state) { return m_nodeFlags = static_cast<uint32_t>(state) << s_tabIndexStateBitOffset; } > > Looks like there is some masking missing from the code you wrote there. Indeed. This will obviously not work LOL. I'm uploading a new patch for another round of review since I've changed a lot of things.
Ryosuke Niwa
Comment 11 2020-09-05 16:31:21 PDT
Created attachment 408104 [details] Updated per Darin's review comments
Darin Adler
Comment 12 2020-09-05 18:28:18 PDT
Comment on attachment 408104 [details] Updated per Darin's review comments View in context: https://bugs.webkit.org/attachment.cgi?id=408104&action=review > Source/WebCore/dom/Element.cpp:244 > +void Element::setTabIndexExplicitly(Optional<int> tabIndex) This function can leave behind a stale leftover unusual tab index stored in the rare data that should never be looked at. I suppose that’s OK. > Source/WebCore/dom/Element.cpp:274 > + RELEASE_ASSERT(hasRareData()); > + return elementRareData()->unusualTabIndex(); Why is this RELEASE_ASSERT needed? Doesn’t seem valuable to me. > Source/WebCore/dom/ElementRareData.h:233 > + ASSERT(m_unusualTabIndex && m_unusualTabIndex != -1); // Common values of 0 and -1 are stored as TabIndexState in Node. The main purpose of this assertion is to catch cases where we are getting the index before it was set. Otherwise, it’s a waste to assert the same thing in both the setter and getter. So it could assert only the 0 case; it’s not so much checking for an invalid index as checking that we don’t ask for an index when one is not there. > Source/WebCore/dom/Node.h:557 > + static constexpr uint32_t s_tabIndexStateBitMask = 3 << s_tabIndexStateBitOffset; I think this needs to be 3U to get rid of the warning (treated as an error) that is causing the build failure on Windows. Hope that works! > Source/WebCore/dom/Node.h:563 > + enum class TabIndexState : uint8_t { > + NotSet = 0, > + Zero = 1, > + NegativeOne = 2, > + InRareData = 3, > + }; I don’t think you need the four explicit numeric values here.
Ryosuke Niwa
Comment 13 2020-09-07 20:02:28 PDT
(In reply to Darin Adler from comment #12) > Comment on attachment 408104 [details] > Updated per Darin's review comments > > View in context: > https://bugs.webkit.org/attachment.cgi?id=408104&action=review > > > Source/WebCore/dom/Element.cpp:244 > > +void Element::setTabIndexExplicitly(Optional<int> tabIndex) > > This function can leave behind a stale leftover unusual tab index stored in > the rare data that should never be looked at. I suppose that’s OK. Yeah, TabIndexState will tell us whether to look into NodeRareData or not. > > Source/WebCore/dom/Element.cpp:274 > > + RELEASE_ASSERT(hasRareData()); > > + return elementRareData()->unusualTabIndex(); > > Why is this RELEASE_ASSERT needed? Doesn’t seem valuable to me. Oh, right. This used to be a security bug but I've made NodeRareData & RenderObject no longer share the same pointer so this would be a safe nullptr crash now. Converted to ASSERT now. > > Source/WebCore/dom/ElementRareData.h:233 > > + ASSERT(m_unusualTabIndex && m_unusualTabIndex != -1); // Common values of 0 and -1 are stored as TabIndexState in Node. > > The main purpose of this assertion is to catch cases where we are getting > the index before it was set. Otherwise, it’s a waste to assert the same > thing in both the setter and getter. > > So it could assert only the 0 case; it’s not so much checking for an invalid > index as checking that we don’t ask for an index when one is not there. Let's a good point. Let's just assert for 0. > > Source/WebCore/dom/Node.h:557 > > + static constexpr uint32_t s_tabIndexStateBitMask = 3 << s_tabIndexStateBitOffset; > > I think this needs to be 3U to get rid of the warning (treated as an error) > that is causing the build failure on Windows. Hope that works! Yup. I have that local change but I guess I forgot to re-upload the patch. > > Source/WebCore/dom/Node.h:563 > > + enum class TabIndexState : uint8_t { > > + NotSet = 0, > > + Zero = 1, > > + NegativeOne = 2, > > + InRareData = 3, > > + }; > > I don’t think you need the four explicit numeric values here. It's true but I think it would make it clear how many bits are needed.
Ryosuke Niwa
Comment 14 2020-09-07 20:02:58 PDT
Created attachment 408206 [details] Patch for landing
Ryosuke Niwa
Comment 15 2020-09-07 20:03:19 PDT
Comment on attachment 408206 [details] Patch for landing Wait for EWS
Ryosuke Niwa
Comment 16 2020-09-07 21:30:16 PDT
Comment on attachment 408206 [details] Patch for landing Clearing flags on attachment: 408206 Committed r266714: <https://trac.webkit.org/changeset/266714>
Ryosuke Niwa
Comment 17 2020-09-07 21:30:18 PDT
All reviewed patches have been landed. Closing bug.
Radar WebKit Bug Importer
Comment 18 2020-09-07 21:31:53 PDT
Note You need to log in before you can comment on or make changes to this bug.