Bug 238654

Summary: devirtualize EventTarget::{ref,deref,eventTargetData}
Product: WebKit Reporter: Cameron McCormack (:heycam) <heycam>
Component: DOMAssignee: Cameron McCormack (:heycam) <heycam>
Status: RESOLVED CONFIGURATION CHANGED    
Severity: Normal CC: cdumez, darin, esprehn+autocc, ews-watchlist, ggaren, kangil.han, webkit-bug-importer, ysuzuki
Priority: P2 Keywords: InRadar
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   
Attachments:
Description Flags
Patch
none
Patch
none
Patch
ews-feeder: commit-queue-
Patch (v2)
none
Patch (v2)
ews-feeder: commit-queue-
Patch (v3.1) none

Cameron McCormack (:heycam)
Reported 2022-03-31 23:28:13 PDT
These functions are called very often during Speedometer 2. Most EventTargets are Nodes, so we can avoid virtual calls (and inline a bit more) by being able to know whether they are Nodes. We can do that by moving the Node::m_refCountAndParentSet field up to EventTarget and using that to tell whether the object is probably a Node. I'm seeing a ~0.4% improvement on Apple Silicon for the benchmark with this.
Attachments
Patch (15.87 KB, patch)
2022-03-31 23:31 PDT, Cameron McCormack (:heycam)
no flags
Patch (15.94 KB, patch)
2022-03-31 23:32 PDT, Cameron McCormack (:heycam)
no flags
Patch (17.42 KB, patch)
2022-04-01 20:37 PDT, Cameron McCormack (:heycam)
ews-feeder: commit-queue-
Patch (v2) (19.88 KB, patch)
2022-04-03 00:21 PDT, Cameron McCormack (:heycam)
no flags
Patch (v2) (21.17 KB, patch)
2022-04-03 00:24 PDT, Cameron McCormack (:heycam)
ews-feeder: commit-queue-
Patch (v3.1) (18.52 KB, patch)
2022-06-12 15:03 PDT, Cameron McCormack (:heycam)
no flags
Radar WebKit Bug Importer
Comment 1 2022-03-31 23:28:57 PDT
Cameron McCormack (:heycam)
Comment 2 2022-03-31 23:31:38 PDT
Cameron McCormack (:heycam)
Comment 3 2022-03-31 23:32:35 PDT
Cameron McCormack (:heycam)
Comment 4 2022-04-01 20:37:34 PDT
Chris Dumez
Comment 5 2022-04-01 20:45:46 PDT
Comment on attachment 456435 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=456435&action=review > Source/WebCore/dom/EventTarget.h:146 > + mutable uint32_t m_nodeRefCountAndParentBit; It is really not pretty to have a Node-specific data member on EventTarget. Not only is it weird encapsulation but it also makes every subclass object that is not a Node unnecessarily larger. Maybe an alternative would be to have a boolean data member indicating that this EventTarget is a Node and then use downcast<Node>(*this).ref() / deref() ? It would look less odd IMHO.
Cameron McCormack (:heycam)
Comment 6 2022-04-01 21:16:06 PDT
(In reply to Chris Dumez from comment #5) > Maybe an alternative would be to have a boolean data member indicating that > this EventTarget is a Node and then use downcast<Node>(*this).ref() / > deref() ? It would look less odd IMHO. This also makes non-Node EventTargets larger, although adding just a boolean does mean that there is some opportunity for non-Node EventTargets to pack some of their own data up against the boolean. Node doesn't have any 1 or 2 byte members to pack in there, though, so this would make all Nodes bigger. I think we want to avoid that. Another alternative could be instead to move the flags word up to EventTarget. Only one flag bit would be relevant for EventTarget, and others would be relevant for Node and descendants. Similar to how NodeFlags has Element etc. specific flags on it. Would that be acceptable?
Chris Dumez
Comment 7 2022-04-01 21:31:35 PDT
(In reply to Cameron McCormack (:heycam) from comment #6) > (In reply to Chris Dumez from comment #5) > > Maybe an alternative would be to have a boolean data member indicating that > > this EventTarget is a Node and then use downcast<Node>(*this).ref() / > > deref() ? It would look less odd IMHO. > > This also makes non-Node EventTargets larger, although adding just a boolean > does mean that there is some opportunity for non-Node EventTargets to pack > some of their own data up against the boolean. Node doesn't have any 1 or 2 > byte members to pack in there, though, so this would make all Nodes bigger. > I think we want to avoid that. > > Another alternative could be instead to move the flags word up to > EventTarget. Only one flag bit would be relevant for EventTarget, and > others would be relevant for Node and descendants. Similar to how NodeFlags > has Element etc. specific flags on it. > > Would that be acceptable? Hmm, I don't think moving the node flags up to EventTarget is any better factoring than moving the NodeRefCount. In my opinion, It is just too confusing to have Node-specific data members on EventTarget. It is true that increasing the size of Node would be unfortunate. That said, we did so recently when we started subclassing CanMakeWeakPtr<> and no-one noticed. For now, I do not have a better proposal than the one I made but I cc'd a couple of people who might. A 0.4% progression is nice but we have to weight it against the how bad it makes the code look.
Cameron McCormack (:heycam)
Comment 8 2022-04-02 00:54:44 PDT
(In reply to Chris Dumez from comment #7) > Hmm, I don't think moving the node flags up to EventTarget is any better > factoring than moving the NodeRefCount. In my opinion, It is just too > confusing to have Node-specific data members on EventTarget. I don't want to belabor the point too much, but that's kind of the situation that NodeFlag is already in. It has a bunch of flags relevant to all Nodes, but also ones that are only relevant to subclasses. Maybe conceptually the distance between EventTarget and Node is greater than between Node and {Element,Text,DocumentFragment}, which makes it seem worse. FWIW if the flags word lived on EventTarget, we could move EventTargetData::isFiringEventListeners there, so there would be two flags relevant to all EventTargets. That would also shrink all non-Node EventTargets (they all inherit from EventTargetWithInlineData). > It is true that increasing the size of Node would be unfortunate. That said, > we did so recently when we started subclassing CanMakeWeakPtr<> and no-one > noticed. If it's truly not an issue to grow all EventTargets by another word, then I will go with that. Interested to hear others' opinions too.
Yusuke Suzuki
Comment 9 2022-04-02 02:04:10 PDT
(In reply to Cameron McCormack (:heycam) from comment #8) > (In reply to Chris Dumez from comment #7) > > Hmm, I don't think moving the node flags up to EventTarget is any better > > factoring than moving the NodeRefCount. In my opinion, It is just too > > confusing to have Node-specific data members on EventTarget. > > I don't want to belabor the point too much, but that's kind of the situation > that NodeFlag is already in. It has a bunch of flags relevant to all Nodes, > but also ones that are only relevant to subclasses. Maybe conceptually the > distance between EventTarget and Node is greater than between Node and > {Element,Text,DocumentFragment}, which makes it seem worse. > > FWIW if the flags word lived on EventTarget, we could move > EventTargetData::isFiringEventListeners there, so there would be two flags > relevant to all EventTargets. That would also shrink all non-Node > EventTargets (they all inherit from EventTargetWithInlineData). > > > It is true that increasing the size of Node would be unfortunate. That said, > > we did so recently when we started subclassing CanMakeWeakPtr<> and no-one > > noticed. > > If it's truly not an issue to grow all EventTargets by another word, then I > will go with that. Interested to hear others' opinions too. I think we should avoid increasing the size. WeakPtr one can fix a lot of bugs, but in this case, benefit is readability. I think reducing these object size and keeping them small are very important and worth doing.
Geoffrey Garen
Comment 10 2022-04-02 08:45:46 PDT
Comment on attachment 456435 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=456435&action=review >> Source/WebCore/dom/EventTarget.h:146 >> + mutable uint32_t m_nodeRefCountAndParentBit; > > It is really not pretty to have a Node-specific data member on EventTarget. Not only is it weird encapsulation but it also makes every subclass object that is not a Node unnecessarily larger. > > Maybe an alternative would be to have a boolean data member indicating that this EventTarget is a Node and then use downcast<Node>(*this).ref() / deref() ? It would look less odd IMHO. Especially given the existing complexity of node refcounting, I agree with the goal of retaining encapsulation. Checking a bit and down casting sounds good to me. That's still pretty well encapsulated. The only thing that leaks out is knowing that we have a subclass, but not any of its implementation details.
Cameron McCormack (:heycam)
Comment 11 2022-04-03 00:21:03 PDT
Created attachment 456501 [details] Patch (v2) Here's a version that moves the flags word up to EventTarget, but keeps all of the Node-specific flag definitions on Node. I like this a lot more than the previous patch.
Cameron McCormack (:heycam)
Comment 12 2022-04-03 00:24:43 PDT
Created attachment 456502 [details] Patch (v2)
Geoffrey Garen
Comment 13 2022-04-04 13:53:07 PDT
I thought of a way to put an "isNode" bit in EventTarget, without needing to move all the other bits for space. 1. EventTarget removes its CanMakeWeakPtr base class 2. EventTarget adds a WeakPtrFactory data member and re-exports the createWeakPtr() function 3. WeakPtrFactory uses PackedRefPtr<T> or CompactPointerTuple or whatever to make the low bits of its RefPtr usable, and offers an interface for getting/setting those bits, and EventTarget uses one bit to store the "isNode" state.
Cameron McCormack (:heycam)
Comment 14 2022-04-04 14:27:44 PDT
That sounds good. I will probably still want to cram those three bits my v2 patch adds in there. But we avoid the funky "reinterpret EventTargetFlags and NodeFlags".
Cameron McCormack (:heycam)
Comment 15 2022-06-12 15:03:51 PDT
Created attachment 460191 [details] Patch (v3.1) This is the latest refinement of the patch I tried. But it didn't seem to help much.
Darin Adler
Comment 16 2022-06-12 16:04:44 PDT
*** Bug 241542 has been marked as a duplicate of this bug. ***
Darin Adler
Comment 17 2022-06-12 22:04:51 PDT
I started a new A/B test of Speedometer 2 with a rebased/tweaked version of Cameron’s latest attempt.
Cameron McCormack (:heycam)
Comment 18 2023-05-29 19:55:52 PDT
Devirtualizing ref() and deref() was done in bug 254113. Devirtualizing eventTargetData() was done in bug 244710.
Note You need to log in before you can comment on or make changes to this bug.