From d88d4a271e80afbdafaadfc28082c325364edf66 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Wed, 30 Sep 2026 15:11:46 +0200 Subject: [PATCH] process: use a class for nextTick resources The resource created for each `process.nextTick()` call was an object literal with three computed symbol keys. V8 builds such literals from a boilerplate that only covers the static keys, so the symbol-keyed properties end up in a separate property array allocated per tick, and each computed-key store goes through the runtime. Construct the resource through a class instead, so that every tick object shares one map with all five fields in-object. process/next-tick-breadth-args.js: ~1.58M -> ~1.95M ops/s (+23%). Signed-off-by: Matteo Collina --- lib/internal/process/task_queues.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/internal/process/task_queues.js b/lib/internal/process/task_queues.js index 484fa4ea2f9d..c3f935cae389 100644 --- a/lib/internal/process/task_queues.js +++ b/lib/internal/process/task_queues.js @@ -110,6 +110,19 @@ function processTicksAndRejections() { // `nextTick()` will not enqueue any callback when the process is about to // exit since the callback would not have a chance to be executed. +// The async resource behind each `process.nextTick()` call. A class keeps +// every instance on one map with all fields in-object; an object literal +// with computed symbol keys spills them into a separate property array. +class TickObject { + constructor(callback, args, asyncId, triggerAsyncId) { + this[async_id_symbol] = asyncId; + this[trigger_async_id_symbol] = triggerAsyncId; + this[async_context_frame] = AsyncContextFrame.current(); + this.callback = callback; + this.args = args; + } +} + function nextTick(callback) { validateFunction(callback, 'callback'); @@ -132,13 +145,7 @@ function nextTick(callback) { setHasTickScheduled(true); const asyncId = newAsyncId(); const triggerAsyncId = getDefaultTriggerAsyncId(); - const tickObject = { - [async_id_symbol]: asyncId, - [trigger_async_id_symbol]: triggerAsyncId, - [async_context_frame]: AsyncContextFrame.current(), - callback, - args, - }; + const tickObject = new TickObject(callback, args, asyncId, triggerAsyncId); if (initHooksExist()) emitInit(asyncId, 'TickObject', triggerAsyncId, tickObject); queue.push(tickObject);