From 627499177f3a69646c352dede4926db12b03002e Mon Sep 17 00:00:00 2001 From: Marcos Casagrande Date: Sun, 20 Aug 2023 11:30:57 +0200 Subject: [PATCH] perf(ext/event): optimize addEventListener options converter (#20203) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR optimizes `addEventListener` by replacing `webidl.createDictionaryConverter("AddEventListenerOptions", ...)` with a custom options parsing function to avoid the overhead of `webidl` methods **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 ---------------------------------------------------------------------------------------------------- ----------------------------- addEventListener options converter (undefined) 4.87 ns/iter 205,248,660.8 (4.7 ns … 13.18 ns) 4.91 ns 5.4 ns 5.6 ns addEventListener options converter (signal) 13.02 ns/iter 76,782,031.2 (11.74 ns … 18.84 ns) 13.08 ns 16.22 ns 16.57 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 ---------------------------------------------------------------------------------------------------- ----------------------------- addEventListener options converter (undefined) 108.36 ns/iter 9,228,688.6 (103.5 ns … 129.88 ns) 109.69 ns 115.61 ns 125.28 ns addEventListener options converter (signal) 134.03 ns/iter 7,460,878.1 (129.14 ns … 144.54 ns) 135.68 ns 141.13 ns 144.1 ns ``` ```js const tg = new EventTarget(); const signal = new AbortController().signal; Deno.bench("addEventListener options converter (undefined)", () => { tg.addEventListener("foo", null); // null callback to only bench options converter }); Deno.bench("addEventListener options converter (signal)", () => { tg.addEventListener("foo", null, { signal }); }); ``` Towards https://github.com/denoland/deno/issues/20167 --- ext/web/02_event.js | 62 +++++++++++++++------------------------------ 1 file changed, 21 insertions(+), 41 deletions(-) diff --git a/ext/web/02_event.js b/ext/web/02_event.js index 4d3f23a022..2be8003674 100644 --- a/ext/web/02_event.js +++ b/ext/web/02_event.js @@ -896,44 +896,28 @@ function getDefaultTargetData() { }; } -// This is lazy loaded because there is a circular dependency with AbortSignal. -let addEventListenerOptionsConverter; - -function lazyAddEventListenerOptionsConverter() { - addEventListenerOptionsConverter ??= webidl.createDictionaryConverter( - "AddEventListenerOptions", - [ - { - key: "capture", - defaultValue: false, - converter: webidl.converters.boolean, - }, - { - key: "passive", - defaultValue: false, - converter: webidl.converters.boolean, - }, - { - key: "once", - defaultValue: false, - converter: webidl.converters.boolean, - }, - { - key: "signal", - converter: webidl.converters.AbortSignal, - }, - ], - ); -} - -webidl.converters.AddEventListenerOptions = (V, prefix, context, opts) => { - if (webidl.type(V) !== "Object" || V === null) { - V = { capture: Boolean(V) }; +function addEventListenerOptionsConverter(V, prefix) { + if (webidl.type(V) !== "Object") { + return { capture: !!V, once: false, passive: false }; } - lazyAddEventListenerOptionsConverter(); - return addEventListenerOptionsConverter(V, prefix, context, opts); -}; + const options = { + capture: !!V.capture, + once: !!V.once, + passive: !!V.passive, + }; + + const signal = V.signal; + if (signal !== undefined) { + options.signal = webidl.converters.AbortSignal( + signal, + prefix, + "'signal' of 'AddEventListenerOptions' (Argument 3)", + ); + } + + return options; +} class EventTarget { constructor() { @@ -952,11 +936,7 @@ class EventTarget { webidl.requiredArguments(arguments.length, 2, prefix); - options = webidl.converters.AddEventListenerOptions( - options, - prefix, - "Argument 3", - ); + options = addEventListenerOptionsConverter(options, prefix); if (callback === null) { return;