From 348287825cdefecdd6dda6ce5c8652fdfc69837c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Wed, 14 Jun 2023 17:04:49 +0200 Subject: [PATCH] perf(web): optimize timer resolution (#19493) Closes https://github.com/denoland/deno/issues/19348 This changes benchmark from the issue from: ``` deno run -A https://raw.githubusercontent.com/nats-io/nats.deno/deno-transport-changes/examples/bench.js --subject a --payload 3500 --pub --count 650000 pub 7,636 msgs/sec - [85.13 secs] ~ 25.49 MB/sec 85127.8765/85127.8765 ``` to: ``` > ./target/release/deno run -A https://raw.githubusercontent.com/nats-io/nats.deno/deno-transport-changes/examples/bench.js --subject a --payload 3500 --pub --count 650000 pub 176,840 msgs/sec - [3.68 secs] ~ 590.27 MB/sec 3675.646833/3675.646833 > ./target/release/deno run -A https://raw.githubusercontent.com/nats-io/nats.deno/deno-transport-changes/examples/bench.js --subject a --payload 3500 --pub --count 650000 pub 174,589 msgs/sec - [3.72 secs] ~ 582.76 MB/sec 3723.01925/3723.01925 ``` --- ext/web/02_timers.js | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/ext/web/02_timers.js b/ext/web/02_timers.js index 27e2e953db..cfabdeb98b 100644 --- a/ext/web/02_timers.js +++ b/ext/web/02_timers.js @@ -15,7 +15,6 @@ const { MapPrototypeSet, Uint8Array, Uint32Array, - NumberPOSITIVE_INFINITY, PromisePrototypeThen, SafeArrayIterator, SafeMap, @@ -190,7 +189,7 @@ function initializeTimer( // 13. Run steps after a timeout given global, "setTimeout/setInterval", // timeout, completionStep, and id. runAfterTimeout( - () => ArrayPrototypePush(timerTasks, task), + task, timeout, timerInfo, ); @@ -203,7 +202,7 @@ function initializeTimer( /** * @typedef ScheduledTimer * @property {number} millis - * @property {() => void} cb + * @property { {action: () => void, nestingLevel: number}[] } task * @property {boolean} resolved * @property {ScheduledTimer | null} prev * @property {ScheduledTimer | null} next @@ -216,12 +215,12 @@ function initializeTimer( const scheduledTimers = { head: null, tail: null }; /** - * @param {() => void} cb Will be run after the timeout, if it hasn't been - * cancelled. + * @param { {action: () => void, nestingLevel: number}[] } task Will be run + * after the timeout, if it hasn't been cancelled. * @param {number} millis * @param {{ cancelRid: number, isRef: boolean, promiseId: number }} timerInfo */ -function runAfterTimeout(cb, millis, timerInfo) { +function runAfterTimeout(task, millis, timerInfo) { const cancelRid = timerInfo.cancelRid; let sleepPromise; // If this timeout is scheduled for 0ms it means we want it to run at the @@ -241,7 +240,6 @@ function runAfterTimeout(cb, millis, timerInfo) { /** @type {ScheduledTimer} */ const timerObject = { millis, - cb, resolved: false, prev: scheduledTimers.tail, next: null, @@ -260,6 +258,10 @@ function runAfterTimeout(cb, millis, timerInfo) { PromisePrototypeThen( sleepPromise, (cancelled) => { + if (timerObject.resolved) { + return; + } + // "op_void_async_deferred" returns null if (cancelled !== null && !cancelled) { // The timer was cancelled. @@ -280,18 +282,15 @@ function runAfterTimeout(cb, millis, timerInfo) { // b) its timeout is lower than the lowest unresolved timeout found so // far in the list. - timerObject.resolved = true; - - let lowestUnresolvedTimeout = NumberPOSITIVE_INFINITY; - let currentEntry = scheduledTimers.head; while (currentEntry !== null) { - if (currentEntry.millis < lowestUnresolvedTimeout) { - if (currentEntry.resolved) { - currentEntry.cb(); - removeFromScheduledTimers(currentEntry); - } else { - lowestUnresolvedTimeout = currentEntry.millis; + if (currentEntry.millis <= timerObject.millis) { + currentEntry.resolved = true; + ArrayPrototypePush(timerTasks, task); + removeFromScheduledTimers(currentEntry); + + if (currentEntry === timerObject) { + break; } }