From 25af820e250ac316b3d78c15e229c88f3bccdc29 Mon Sep 17 00:00:00 2001 From: Anatoli Papirovski Date: Thu, 6 Jun 2019 09:18:41 +0200 Subject: [PATCH 1/3] process: improve queueMicrotask performance Optimize the hot code paths of queueMicrotask by not creating unnecessary objects, not looking up properties on frozen primordials, etc. --- lib/async_hooks.js | 27 +++++++++++++++------------ lib/internal/process/task_queues.js | 11 +++++------ 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/lib/async_hooks.js b/lib/async_hooks.js index 3a12a241ea5d5f..3315f360e1dddf 100644 --- a/lib/async_hooks.js +++ b/lib/async_hooks.js @@ -1,6 +1,7 @@ 'use strict'; const { Reflect } = primordials; +const ReflectApply = Reflect.apply; const { ERR_ASYNC_CALLBACK, @@ -134,15 +135,16 @@ class AsyncResource { constructor(type, opts = {}) { validateString(type, 'type'); - if (typeof opts === 'number') { - opts = { triggerAsyncId: opts, requireManualDestroy: false }; - } else if (opts.triggerAsyncId === undefined) { - opts.triggerAsyncId = getDefaultTriggerAsyncId(); + let triggerAsyncId = opts; + let requireManualDestroy = false; + if (typeof opts !== 'number') { + triggerAsyncId = opts.triggerAsyncId === undefined ? + getDefaultTriggerAsyncId() : opts.triggerAsyncId; + requireManualDestroy = !!opts.requireManualDestroy; } // Unlike emitInitScript, AsyncResource doesn't supports null as the // triggerAsyncId. - const triggerAsyncId = opts.triggerAsyncId; if (!Number.isSafeInteger(triggerAsyncId) || triggerAsyncId < -1) { throw new ERR_INVALID_ASYNC_ID('triggerAsyncId', triggerAsyncId); } @@ -151,15 +153,14 @@ class AsyncResource { this[async_id_symbol] = asyncId; this[trigger_async_id_symbol] = triggerAsyncId; - // This prop name (destroyed) has to be synchronized with C++ - const destroyed = { destroyed: false }; - this[destroyedSymbol] = destroyed; - if (initHooksExist()) { emitInit(asyncId, type, triggerAsyncId, this); } - if (!opts.requireManualDestroy) { + if (!requireManualDestroy) { + // This prop name (destroyed) has to be synchronized with C++ + const destroyed = { destroyed: false }; + this[destroyedSymbol] = destroyed; registerDestroyHook(this, asyncId, destroyed); } } @@ -168,14 +169,16 @@ class AsyncResource { const asyncId = this[async_id_symbol]; emitBefore(asyncId, this[trigger_async_id_symbol]); try { - return Reflect.apply(fn, thisArg, args); + return ReflectApply(fn, thisArg, args); } finally { emitAfter(asyncId); } } emitDestroy() { - this[destroyedSymbol].destroyed = true; + if (this[destroyedSymbol] !== undefined) { + this[destroyedSymbol].destroyed = true; + } emitDestroy(this[async_id_symbol]); return this; } diff --git a/lib/internal/process/task_queues.js b/lib/internal/process/task_queues.js index 51486284578bce..ff0713b71c0d4d 100644 --- a/lib/internal/process/task_queues.js +++ b/lib/internal/process/task_queues.js @@ -1,6 +1,7 @@ 'use strict'; const { FunctionPrototype } = primordials; +const FunctionPrototypeBind = FunctionPrototype.bind; const { // For easy access to the nextTick state in the C++ land, @@ -136,15 +137,13 @@ function nextTick(callback) { } let AsyncResource; +const defaultMicrotaskResourceOpts = { requireManualDestroy: true }; function createMicrotaskResource() { // Lazy load the async_hooks module - if (!AsyncResource) { + if (AsyncResource === undefined) { AsyncResource = require('async_hooks').AsyncResource; } - return new AsyncResource('Microtask', { - triggerAsyncId: getDefaultTriggerAsyncId(), - requireManualDestroy: true, - }); + return new AsyncResource('Microtask', defaultMicrotaskResourceOpts); } function runMicrotask() { @@ -172,7 +171,7 @@ function queueMicrotask(callback) { const asyncResource = createMicrotaskResource(); asyncResource.callback = callback; - enqueueMicrotask(FunctionPrototype.bind(runMicrotask, asyncResource)); + enqueueMicrotask(FunctionPrototypeBind(runMicrotask, asyncResource)); } module.exports = { From dcb367596cdc310376c9f629de2c782576438fc9 Mon Sep 17 00:00:00 2001 From: Anatoli Papirovski Date: Thu, 6 Jun 2019 09:50:36 +0200 Subject: [PATCH 2/3] fixup: avoid ReflectApply ugliness --- lib/async_hooks.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/async_hooks.js b/lib/async_hooks.js index 3315f360e1dddf..0c177055364e41 100644 --- a/lib/async_hooks.js +++ b/lib/async_hooks.js @@ -1,7 +1,6 @@ 'use strict'; const { Reflect } = primordials; -const ReflectApply = Reflect.apply; const { ERR_ASYNC_CALLBACK, @@ -169,7 +168,9 @@ class AsyncResource { const asyncId = this[async_id_symbol]; emitBefore(asyncId, this[trigger_async_id_symbol]); try { - return ReflectApply(fn, thisArg, args); + if (thisArg === undefined) + return fn(...args); + return Reflect.apply(fn, thisArg, args); } finally { emitAfter(asyncId); } From 76e1185e3879bf8951a73db62a08cb15fc5195d4 Mon Sep 17 00:00:00 2001 From: Anatoli Papirovski Date: Thu, 6 Jun 2019 10:04:07 +0200 Subject: [PATCH 3/3] fixup: remove the other weirdness --- lib/internal/process/task_queues.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/internal/process/task_queues.js b/lib/internal/process/task_queues.js index ff0713b71c0d4d..6b78f8b387449e 100644 --- a/lib/internal/process/task_queues.js +++ b/lib/internal/process/task_queues.js @@ -1,7 +1,6 @@ 'use strict'; const { FunctionPrototype } = primordials; -const FunctionPrototypeBind = FunctionPrototype.bind; const { // For easy access to the nextTick state in the C++ land, @@ -171,7 +170,7 @@ function queueMicrotask(callback) { const asyncResource = createMicrotaskResource(); asyncResource.callback = callback; - enqueueMicrotask(FunctionPrototypeBind(runMicrotask, asyncResource)); + enqueueMicrotask(FunctionPrototype.bind(runMicrotask, asyncResource)); } module.exports = {