Skip to content

perf: shrink generated wrapper source#264

Merged
timfish merged 1 commit into
nodejs:mainfrom
BridgeAR:BridgeAR/2026-06-30-shrink-wrapper-source
Jun 30, 2026
Merged

perf: shrink generated wrapper source#264
timfish merged 1 commit into
nodejs:mainfrom
BridgeAR:BridgeAR/2026-06-30-shrink-wrapper-source

Conversation

@BridgeAR

Copy link
Copy Markdown
Member

Summary

Every wrapped module makes Node compile a generated wrapper, and that compile is the dominant iitm startup cost once the parser is no longer Acorn. Each export inlined ~25 lines of identical read / defer / override / set / get wiring, so a realistic 396-module / 3258-export graph emitted 2.85 MiB of wrapper source for V8 to parse. This moves that wiring into a single __bind helper emitted once per wrapper; each export keeps only the live let binding and static export the module system requires, plus a one-line __bind call.

Behavior is unchanged: live bindings, deferred resolution for circular imports, the Hook set/get proxy, and the Node 23+ module.exports fallback all keep their existing semantics.

Numbers

396-module / 3258-export graph, synchronous in-thread loader, Node 24.16, --cpu-prof --cpu-prof-interval=50:

  • generated wrapper source: 2.85 MiB -> 1.25 MiB (-56%)
  • compileSourceTextModule self time: 47.1 ms -> 24.8 ms
  • total wrap+load: 192.8 ms -> 144.4 ms (-25%)

Test plan

  • Existing suite green (sync + off-thread): npm test
  • Sync in-thread hooks on Node >= 24.11.1 (test/register/v22.15-sync-register-hooks*.mjs)
  • Circular-import deferral and the module.exports builtin fallback still resolve

Stacked on #259 (es-module-lexer); the compile win is what remains after the parser swap, so review/merge after that.

@BridgeAR BridgeAR force-pushed the BridgeAR/2026-06-30-shrink-wrapper-source branch from 5ea8d97 to a9ffdcc Compare June 30, 2026 01:45
@BridgeAR BridgeAR force-pushed the BridgeAR/2026-06-30-shrink-wrapper-source branch from a9ffdcc to e31b60b Compare June 30, 2026 16:27
This reduces iitm's per-module compile cost by emitting the export read /
defer / override / set / get wiring once per wrapper rather than per export.

On a 396-module / 3258-export graph (synchronous in-thread loader, Node
24.16): wrapper source 2.85 MiB -> 1.25 MiB, compileSourceTextModule 47.1 ms
-> 24.8 ms, total wrap+load 192.8 ms -> 144.4 ms.
@BridgeAR BridgeAR force-pushed the BridgeAR/2026-06-30-shrink-wrapper-source branch from e31b60b to f38d8e6 Compare June 30, 2026 17:08
@BridgeAR BridgeAR marked this pull request as ready for review June 30, 2026 17:11
Comment thread create-hook.mjs
__pending = next
}

function __bind (key, write, read, useFallback) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These double-underscores to hide things are going to break at some point.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likely yes!

@timfish timfish merged commit 491d968 into nodejs:main Jun 30, 2026
30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants