-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
perf(ext/event): replace ReflectHas with object lookup #20190
Conversation
ext/web/02_event.js
Outdated
@@ -113,7 +112,7 @@ function setStopImmediatePropagation( | |||
function hasRelatedTarget( | |||
event, | |||
) { | |||
return ReflectHas(event, "relatedTarget"); | |||
return !!event.relatedTarget; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not the same thing. If relatedTarget
coerces to null
, this falsely returns false. Use "relatedTarget" in event
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just return false here? We don't have any DOM events.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know. I used in
initially but there's a lint rule against in
usage. So went with object lookup because in these changes there wasn't any difference given the possible values and usage of hasRelatedTarget
.
Being honest, couldn't find any reasoning as to why Reflect.has
was being used. Looked in Node.js implementation and the spec. relatedTarget
as far as I know should be either null
or a target. So the current implementation with Reflect.has
was returning true
for relatedTarget === null
which IMO is not correct, and later asigning null
to eventRelatedTarget
, which is the same thing is doing now but is doing it by using the other branch of the ternary operator
const eventRelatedTarget = hasRelatedTarget(eventImpl)
? eventImpl.relatedTarget
: null;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just return false here? We don't have any DOM events.
I was actually going to change this later with
const eventRelatedTarget = eventImpl.relatedTarget ?? null;
And most likely ?? null
is not even needed.
There are a lot of properties and paths that are most likely never used currently, but can't find examples of code that is supposed to used those branches so it is hard to know whether it'll break things or not, which is why I'm only making small changes for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If no WPTs break, it is safe to change for sure. If they do, it may be safe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also: relatedTarget should always be the EventTarget that the event was dispatched on, I think.
This check can be !== null
I think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check is actually not needed given possible relatedTarget
values. Removed hasRelatedTarget
,
@@ -972,7 +974,7 @@ class EventTarget { | |||
|
|||
const { listeners } = self[eventTargetData]; | |||
|
|||
if (!(ReflectHas(listeners, type))) { | |||
if (!listeners[type]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here, use in
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self[eventTargetData]
is only accessible internally so we can probably use whatever benchmarks fastest here. There shouldn't be a risk of improper boolean coercion if we know the field is null | array
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
listeners
is an internal value which is only set by us, and listeners[type]
it's either undefined or []
. There's no change in using object lookup or in
, and since we have a lint rule against in
I used object lookup.
Could we also add another bench call with an |
You mean with
|
@marcosc90 I believe Matt meant for a benchmark like that:
|
Correct, though it requires a call to (from the websocket code) defineEventHandler(WebSocket.prototype, "message");
defineEventHandler(WebSocket.prototype, "error");
defineEventHandler(WebSocket.prototype, "close");
defineEventHandler(WebSocket.prototype, "open"); |
|
@mmastrac I exposed class WS extends EventTarget{}
defineEventHandler(WS.prototype, "message");
const ws = new WS();
ws.onmessage = (e) => {};
const event = new MessageEvent("message", {
data: 'foo',
origin: 'localhost',
});
Deno.bench("event dispatch onmessage", () => {
ws.dispatchEvent(event);
}); this PR
main
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks Marcos! Once we run our internal benches we'll let you know how that impact real world benchmarks.
I'm making improvements on |
This PR optimizes event dispatch by replacing `ReflectHas` with object lookup. I also made `isSlottable` return `false` since AFAIK there aren't any slottables nodes in Deno **This PR** ``` cpu: 13th Gen Intel(R) Core(TM) i9-13900H runtime: deno 1.36.1 (x86_64-unknown-linux-gnu) benchmark time (avg) iter/s (min … max) p75 p99 p995 --------------------------------------------------------------------- ----------------------------- event dispatch 80.46 ns/iter 12,428,739.4 (73.84 ns … 120.07 ns) 81.82 ns 86.34 ns 91.18 ns ``` **main** ``` cpu: 13th Gen Intel(R) Core(TM) i9-13900H runtime: deno 1.36.1 (x86_64-unknown-linux-gnu) benchmark time (avg) iter/s (min … max) p75 p99 p995 --------------------------------------------------------------------- ----------------------------- event dispatch 102.66 ns/iter 9,741,319.6 (96.66 ns … 132.88 ns) 104.18 ns 114.58 ns 118.45 ns ``` ```js const tg = new EventTarget(); const ev = new Event("foo"); const listener = () => {}; tg.addEventListener("foo", listener); Deno.bench("event dispatch ", () => { tg.dispatchEvent(ev); }); ``` towards denoland#20167
This PR optimizes event dispatch by replacing `ReflectHas` with object lookup. I also made `isSlottable` return `false` since AFAIK there aren't any slottables nodes in Deno **This PR** ``` cpu: 13th Gen Intel(R) Core(TM) i9-13900H runtime: deno 1.36.1 (x86_64-unknown-linux-gnu) benchmark time (avg) iter/s (min … max) p75 p99 p995 --------------------------------------------------------------------- ----------------------------- event dispatch 80.46 ns/iter 12,428,739.4 (73.84 ns … 120.07 ns) 81.82 ns 86.34 ns 91.18 ns ``` **main** ``` cpu: 13th Gen Intel(R) Core(TM) i9-13900H runtime: deno 1.36.1 (x86_64-unknown-linux-gnu) benchmark time (avg) iter/s (min … max) p75 p99 p995 --------------------------------------------------------------------- ----------------------------- event dispatch 102.66 ns/iter 9,741,319.6 (96.66 ns … 132.88 ns) 104.18 ns 114.58 ns 118.45 ns ``` ```js const tg = new EventTarget(); const ev = new Event("foo"); const listener = () => {}; tg.addEventListener("foo", listener); Deno.bench("event dispatch ", () => { tg.dispatchEvent(ev); }); ``` towards #20167
This PR optimizes event dispatch by replacing
ReflectHas
with object lookup. I also madeisSlottable
returnfalse
since AFAIK there aren't any slottables nodes in DenoThis PR
main
towards #20167