src: skip the async id stack when nobody sees it by nigrosimone · Pull Request #66511 · nodejs/node · GitHub
Skip to content

src: skip the async id stack when nobody sees it - #66511

Open
nigrosimone wants to merge 2 commits into
nodejs:mainfrom
nigrosimone:lazy-async-ids
Open

nigrosimone wants to merge 2 commits into
nodejs:mainfrom
nigrosimone:lazy-async-ids

Conversation

@nigrosimone

@nigrosimone nigrosimone commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

With no async hook and no executionAsyncResource() user, every InternalCallbackScope pushes and pops the async ids on the stack, only for executionAsyncId() and triggerAsyncId() to read the top. This swaps the two ids in place instead: they are all that those two and process.nextTick() read, so the readers pay nothing.

executionAsyncResource() is the only reader of the stack. The scopes that skip it form a chain in AsyncHooks, counted in a new field kLazyScopes, and JS asks C++ for the innermost one only while that count is not zero. The checks for a corrupted stack stay: test/async-hooks test-improper-order and test-emit-*-on-destroyed fail without them, because the main script runs in a scope that starts before any hook.

compare.js on napi/make_callback, 30 runs, Linux x64, one core, on main (3e5c8af, with #66395):

                                                     confidence improvement accuracy (*)   (**)  (***)
napi/make_callback n=1000000 type='AsyncResource'            **      6.57 %  ±4.56% ±6.07% ±7.91%
napi/make_callback n=1000000 type='Call'                            -2.91 %  ±5.61% ±7.51% ±9.86%
napi/make_callback n=1000000 type='MakeCallback'              *      7.47 %  ±5.64% ±7.50% ±9.77%
napi/make_callback n=10000000 type='AsyncResource'          ***      8.54 %  ±3.42% ±4.55% ±5.93%
napi/make_callback n=10000000 type='Call'                           -1.02 %  ±4.35% ±5.79% ±7.54%
napi/make_callback n=10000000 type='MakeCallback'           ***      7.30 %  ±2.85% ±3.80% ±4.96%

MakeCallback goes from 128.0 to 119.3 ns at n=1e7. test/async-hooks, the async hooks, AsyncLocalStorage, AsyncResource and nextTick tests in test/parallel, and the new addon test pass.

The new field and the chain are the part to look at first, the design was discussed in nodejs/performance#24.

Disclosure: I used Opus 5.5 (Max) as coding assistant

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. needs-ci PRs that need a full CI run. labels Oct 4, 2026
With no async hook and no executionAsyncResource() user, every
InternalCallbackScope pushed and popped the async ids on the stack, only
for executionAsyncId() and triggerAsyncId() to read the top. Swap the
two ids in place instead: they are all that those two and
process.nextTick() read. executionAsyncResource() is the only reader of
the stack: the scopes that skip it form a chain in AsyncHooks, counted
in a new field, kLazyScopes, and JS asks C++ for the innermost one only
while that count is not zero. The checks for a corrupted stack stay.

Refs: nodejs/performance#24
Signed-off-by: Nigro Simone <nigro.simone@gmail.com>
@nigrosimone
nigrosimone marked this pull request as ready for review October 21, 2026 03:22
@codecov

codecov Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

executionAsyncResource() asked first inside a native callback, where the
scope skipped the id stack and holds its resource as a Global, and with
a context pushed on the stack above that scope.

Signed-off-by: Nigro Simone <nigro.simone@gmail.com>
@nigrosimone

Copy link
Copy Markdown
Contributor Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants