GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — lib: load fewer builtins when bootstrapping without a snapshot by codebytere · Pull Request #65329 · nodejs/node

Conversation

@codebytere

@codebytere codebytere commented Aug 16, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Worker startup gets ~6 % faster and a snapshot-less main-thread bootstrap (embedders that create their own isolate, --no-node-snapshot) ~10 % faster, by not eagerly loading builtins those paths never use. Startup with the snapshot is unchanged.

new Worker() → 'online' → terminate                       21.3 → 20.1 ms   ***   +5.9 %  ±1.4%
misc/startup-core.js mode='worker' script='semicolon.js'                   ***   +5.4 %  ±0.4%
misc/startup-core.js mode='worker' script='empty.mjs'                      ***   +3.1 %  ±0.3%
node --no-node-snapshot semicolon.js (start→exit)         58.9 → 53.4 ms   ***  +10.3 %  ±1.1%
node --no-node-snapshot empty.mjs                                          ***   +2.5 %  ±1.1%
misc/startup-core.js mode='process' (snapshot), all scripts                       ±0.5 %  n.s.

Builtins compiled: bare worker 95 → 83; --no-node-snapshot empty CJS entry 76 → 59, empty ESM entry 76 → 69.

Contexts that aren't deserialized from the snapshot compile every builtin the bootstrap touches, so each eager require there is startup time (~0.15–0.4 ms apiece). Several are eager only so that they land in the snapshot, or for features the bootstrap doesn't use. This makes them lazy without changing the snapshot's contents:

  • is_main_thread.js: util, url, the ESM loader chain, internal/blob and internal/dns/utils are preloaded only if (isBuildingSnapshot()); otherwise they load on first use.
  • fs → internal/blob: only for fs.openAsBlob(). internal/url → internal/data_url: only for the Buffer-returning file-URL helpers. execution, cjs loader, esm/translators, esm/load → TypeScript stripper / data: helpers: only for those inputs.
  • pre_execution → internal/dns/utils: only for an explicit --dns-result-order (still validated at startup) or a snapshot build; 'verbatim' becomes the variable's initializer, so a snapshot-time setDefaultResultOrder() still survives deserialization.
  • internal/worker → ELU / error_serdes on demand; worker_threads.locks via defineLazyProperties (as util does).

An intermediate version that didn't re-add these to the snapshot regressed node empty.mjs by 2–4 %, which is why the isBuildingSnapshot() block lists them explicitly. test-bootstrap-modules is adjusted for the worker-side list.

Tests: test-bootstrap-modules plus worker, url, fs, dns, process, cli, vm, snapshot, blob, esm, inspector, module, util, test-runner and single-executable suites pass.


Disclosure: the code, test, measurements and this description were written by Claude Code, directed and reviewed by @codebytere.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

ShenHongFei reacted with confused emoji mcollina reacted with heart emoji
All reactions
  • 😕 1 reaction
  • ❤️ 1 reaction
@nodejs-github-bot

nodejs-github-bot commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/loaders
  • @nodejs/net
  • @nodejs/startup
  • @nodejs/url
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@nodejs-github-bot nodejs-github-bot added lib / src Issues and PRs involving general changes in the lib/ or src/ directories. needs-ci PRs that need a full CI run. labels Aug 16, 2026
@codebytere
codebytere requested review from jasnell and joyeecheung August 16, 2026 16:44
@codebytere codebytere added request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. and removed needs-ci PRs that need a full CI run. labels Aug 16, 2026
@codecov

codecov Bot commented Aug 16, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.66667% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.09%. Comparing base (30bff4a) to head (0cc532a).
⚠️ Report is 105 commits behind head on main.

Files with missing lines Patch % Lines
lib/internal/bootstrap/switches/is_main_thread.js 0.00% 18 Missing ⚠️
lib/internal/url.js 85.71% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #65329      +/-   ##
==========================================
- Coverage   90.13%   90.09%   -0.05%     
==========================================
  Files         752      752              
  Lines      251568   252348     +780     
  Branches    47270    47462     +192     
==========================================
+ Hits       226759   227355     +596     
- Misses      16168    16292     +124     
- Partials     8641     8701      +60     
Files with missing lines Coverage Δ
lib/fs.js 98.39% <100.00%> (+0.02%) ⬆️
lib/internal/dns/utils.js 100.00% <100.00%> (+0.55%) ⬆️
lib/internal/modules/cjs/loader.js 98.14% <100.00%> (ø)
lib/internal/modules/esm/load.js 91.32% <100.00%> (-0.08%) ⬇️
lib/internal/modules/esm/translators.js 97.56% <100.00%> (+0.01%) ⬆️
lib/internal/process/execution.js 94.76% <100.00%> (+0.21%) ⬆️
lib/internal/process/pre_execution.js 97.75% <100.00%> (+0.60%) ⬆️
lib/internal/worker.js 96.75% <100.00%> (-0.02%) ⬇️
lib/worker_threads.js 100.00% <100.00%> (ø)
lib/internal/url.js 93.28% <85.71%> (+0.01%) ⬆️
... and 1 more

... and 76 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

jasnell
jasnell approved these changes Aug 17, 2026
View reviewed changes
@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Aug 17, 2026
@nodejs-github-bot

nodejs-github-bot commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

CI: https://ci.nodejs.org/job/node-test-pull-request/75905/

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@codebytere
codebytere force-pushed the perf/bootstrap-fewer-builtins-without-snapshot branch from 5d49228 to 088f769 Compare August 17, 2026 08:01
@targos targos added the needs-benchmark-ci PRs that need a benchmark CI run. label Aug 17, 2026
aduh95
aduh95 approved these changes Aug 17, 2026
View reviewed changes
@github-actions

github-actions Bot commented Aug 17, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Benchmark GHA (misc / startup-core): https://github.com/nodejs/node/actions/runs/32009241408

Results

[!WARNING]
Do not take GHA benchmark results as face value, always confirm them
using a dedicated machine, e.g. Jenkins CI.

Benchmark results:

                                                                                         confidence improvement accuracy (*)    (**)   (***)
misc/startup-core.js n=30 mode='process' script='benchmark/fixtures/empty.mjs'                           0.95 %      ±16.58% ±21.85% ±28.04%
misc/startup-core.js n=30 mode='process' script='benchmark/fixtures/import-builtins.mjs'                 0.60 %      ±11.57% ±15.25% ±19.56%
misc/startup-core.js n=30 mode='process' script='benchmark/fixtures/require-builtins.js'                 0.19 %      ±13.51% ±17.81% ±22.85%
misc/startup-core.js n=30 mode='process' script='test/fixtures/semicolon.js'                            -0.02 %      ±16.55% ±21.81% ±27.99%
misc/startup-core.js n=30 mode='process' script='test/fixtures/snapshot/typescript.js'                  -0.54 %       ±8.97% ±11.82% ±15.17%
misc/startup-core.js n=30 mode='worker' script='benchmark/fixtures/empty.mjs'                            3.74 %       ±9.02% ±11.89% ±15.26%
misc/startup-core.js n=30 mode='worker' script='benchmark/fixtures/import-builtins.mjs'                  1.08 %       ±8.87% ±11.69% ±15.00%
misc/startup-core.js n=30 mode='worker' script='benchmark/fixtures/require-builtins.js'                  1.37 %       ±8.86% ±11.67% ±14.98%
misc/startup-core.js n=30 mode='worker' script='test/fixtures/semicolon.js'                              4.08 %       ±9.20% ±12.13% ±15.56%
misc/startup-core.js n=30 mode='worker' script='test/fixtures/snapshot/typescript.js'                   -0.84 %       ±8.49% ±11.19% ±14.36%

Be aware that when doing many comparisons the risk of a false-positive
result increases. In this case, there are 10 comparisons, you can thus
expect the following amount of false-positive results:
  0.50 false positives, when considering a   5% risk acceptance (*, **, ***),
  0.10 false positives, when considering a   1% risk acceptance (**, ***),
  0.01 false positives, when considering a 0.1% risk acceptance (***)

[!WARNING]
Do not take GHA benchmark results as face value, always confirm them
using a dedicated machine, e.g. Jenkins CI.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

mcollina
mcollina approved these changes Aug 17, 2026
View reviewed changes

@mcollina mcollina left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

lgtm

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@codebytere codebytere added request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. and removed needs-benchmark-ci PRs that need a benchmark CI run. labels Aug 17, 2026
@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Aug 17, 2026
@nodejs-github-bot

nodejs-github-bot commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

CI: https://ci.nodejs.org/job/node-test-pull-request/75945/

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@nodejs-github-bot

nodejs-github-bot commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

CI: https://ci.nodejs.org/job/node-test-pull-request/75963/

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@aduh95

aduh95 commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

The modified test is failing on Windows, indicating this PR is adding flakiness. Can you have a look?

---
duration_ms: 293.997
exitcode: 1
severity: fail
stack: |-
  node:internal/assert/utils:146
    throw error;
    ^

  AssertionError [ERR_ASSERTION]: These builtins are now no longer loaded before pre-execution.
  If this is intentional, remove them from `expected.beforePreExec`.

  --- These could be removed from expected.beforePreExec ---
  [
    'Internal Binding cjs_lexer',
    'Internal Binding encoding_binding',
    'NativeModule internal/blob',
    'NativeModule internal/data_url',
    'NativeModule internal/dns/utils',
    'NativeModule internal/encoding',
    'NativeModule internal/encoding/single-byte',
    'NativeModule internal/encoding/util',
    'NativeModule internal/mime',
    'NativeModule internal/modules/esm/assert',
    'NativeModule internal/modules/esm/get_format',
    'NativeModule internal/modules/esm/load',
    'NativeModule internal/modules/esm/loader',
    'NativeModule internal/modules/esm/module_job',
    'NativeModule internal/modules/esm/module_map',
    'NativeModule internal/modules/esm/resolve',
    'NativeModule internal/modules/esm/translators',
    'NativeModule internal/modules/typescript',
    'NativeModule internal/net',
    'NativeModule url'
  ]

  These builtins are now unexpectedly loaded at run time.
  If this is intentional, add them to `expected.atRunTime`.

  # Note: loading more builtins at run time can lead to startup performance regression.
  - Consider lazy loading builtins that are not used universally.

  --- These could be added to expected.atRunTime ---
  [
    'NativeModule internal/modules/esm/get_format'
  ]


  12 !== 0

      at Object.<anonymous> (d:\workspace\node-test-binary-windows-js-suites\node\test\parallel\test-bootstrap-modules.js:264:8)
      at Module._compile (node:internal/modules/cjs/loader:1937:14)
      at Module._extensions..js (node:internal/modules/cjs/loader:2077:10)
      at Module.load (node:internal/modules/cjs/loader:1659:32)
      at Module._load (node:internal/modules/cjs/loader:1451:12)
      at wrapModuleLoad (node:internal/modules/cjs/loader:261:19)
      at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5)
      at node:internal/main/run_main_module:33:47 {
    generatedMessage: false,
    code: 'ERR_ASSERTION',
    actual: 12,
    expected: 0,
    operator: 'strictEqual',
    diff: 'simple'
  }

  Node.js v27.0.0-pre
...

https://ci.nodejs.org/job/node-test-binary-windows-js-suites/42385/RUN_SUBSET=2,nodes=win11-arm64-COMPILED_BY-vs2022_clang-arm64/testReport/junit/(root)/parallel/test_bootstrap_modules/

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@codebytere
codebytere force-pushed the perf/bootstrap-fewer-builtins-without-snapshot branch from 088f769 to 0d3b8a7 Compare August 18, 2026 09:18
@nodejs nodejs deleted a comment from codebytere-ant Aug 18, 2026
@codebytere

codebytere commented Aug 18, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

@aduh95 fixed!

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

aduh95
aduh95 reviewed Aug 18, 2026
View reviewed changes
Comment thread test/parallel/test-bootstrap-modules.js Resolved

Uh oh!

There was an error while loading. Please reload this page.

@codebytere
codebytere force-pushed the perf/bootstrap-fewer-builtins-without-snapshot branch from 0d3b8a7 to f95b3c7 Compare August 18, 2026 14:13
@codebytere
codebytere requested review from aduh95, jasnell and mcollina August 19, 2026 08:52
@codebytere codebytere added the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Aug 19, 2026
jasnell
jasnell approved these changes Aug 20, 2026
View reviewed changes
@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Aug 20, 2026
@nodejs-github-bot

nodejs-github-bot commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

CI: https://ci.nodejs.org/job/node-test-pull-request/76025/

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@nodejs-github-bot

nodejs-github-bot commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

CI: https://ci.nodejs.org/job/node-test-pull-request/76043/

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@codebytere

codebytere commented Aug 20, 2026

Copy link
Copy Markdown
Member Author

@mcollina mind re-reviewing?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

aduh95
aduh95 reviewed Aug 20, 2026
View reviewed changes
Comment thread test/parallel/test-bootstrap-modules.js Resolved

Uh oh!

There was an error while loading. Please reload this page.

aduh95
aduh95 reviewed Aug 20, 2026
View reviewed changes
Comment thread test/parallel/test-bootstrap-modules.js Resolved

Uh oh!

There was an error while loading. Please reload this page.

@codebytere
lib: load fewer builtins when bootstrapping without a snapshot
0cc532a
Contexts that are not deserialized from the built-in snapshot -- worker
threads, and the main context of embedders that create their own
isolate or of `node --no-node-snapshot` -- compile (with the code cache
at best) every builtin the bootstrap touches, so each eagerly required
builtin is startup time (~0.15-0.4 ms apiece). A number of them are only
required eagerly so that they end up in the snapshot, or for features
the bootstrap path never uses.

Load lazily what those paths do not need:

- is_main_thread.js: preload util, url, the ESM loader (translators,
  resolver, module_job/map, source maps, node:module, vm modules, mime,
  data_url, the TypeScript stripper), internal/blob and
  internal/dns/utils only while building a snapshot; they load on first
  use otherwise.
- fs: internal/blob (+ internal/encoding and its tables) is only used
  by fs.openAsBlob().
- internal/url: internal/data_url (+ internal/mime) is only used by the
  Buffer-returning file URL helpers.
- internal/process/execution, the CommonJS loader, esm/translators and
  esm/load: the TypeScript stripper and data: URL helpers are only
  needed for TypeScript sources / data: URLs.
- pre_execution: internal/dns/utils (+ internal/net) is only needed up
  front to validate an explicit --dns-result-order or to register the
  resolver's snapshot serializer; the default order becomes the
  variable's initializer.
- internal/worker: event_loop_utilization and error_serdes are only
  needed once a sub-worker's ELU is read or it reports an error.
- worker_threads: `locks` is defined lazily, like util's lazy exports.

Main-thread startup with the snapshot is unchanged (the same modules
are preloaded into it; the bootstrap-modules test lists are adjusted).
A bare worker compiles 95 -> 83 builtins (cold start -5%); without the
snapshot an empty CommonJS entry point compiles 76 -> 59 builtins and an
empty ES module entry point 76 -> 69.

Signed-off-by: Shelley Vohr <[email protected]>
@codebytere
codebytere force-pushed the perf/bootstrap-fewer-builtins-without-snapshot branch from f95b3c7 to 0cc532a Compare August 20, 2026 12:39
@codebytere codebytere added the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Aug 20, 2026
legendecas
legendecas approved these changes Aug 20, 2026
View reviewed changes
@joyeecheung

joyeecheung commented Aug 20, 2026

Copy link
Copy Markdown
Member

I am in favor of "for snapshot less mode, try not to eager load that much" in general, although some of the removals seem somewhat too conservative (e.g. the url and util modules are commonly used by most applications) and may only favor the startup of empty scripts but penalise the startup of a real application that actually does something. So I think we should check the performance impact of the scripts that does something beyond being empty as well. If the number

misc/startup-core.js mode='process' (snapshot), all scripts                       ±0.5 %  n.s.

Comes from that scenario then LGTM, otherwise can you run the benchmark across the startup-core and startup-cli-version data sets?

codebytere reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Aug 20, 2026
@nodejs-github-bot

nodejs-github-bot commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

CI: https://ci.nodejs.org/job/node-test-pull-request/76078/

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@codebytere

codebytere commented Aug 21, 2026

Copy link
Copy Markdown
Member Author

@joyeecheung yes, that row is startup-core mode=process with the snapshot, all five scripts including require-builtins.js and import-builtins.mjs; the benchmark CI run above (https://github.com/nodejs/node/actions/runs/32009241408) shows the same on all four machines: process rows within ±1 % n.s., worker rows +1…5 %.

for the "app that actually does something" case i ran startup-cli-version and a builtin-heavy entry point both with the snapshot and with --no-node-snapshot (n=30, linux x64):

                                               with snapshot     --no-node-snapshot
startup-cli-version  eslint --version         -0.14 %  n.s.     +1.70 %  ***
                     npm --version            -0.00 %  n.s.     +0.69 %  ***
                     npx --version            +0.11 %  n.s.     +0.80 %  **
                     corepack --version       -0.19 %  n.s.     +3.21 %  ***
require-builtins.js (start -> exit)           -0.08 %  n.s.     +1.97 %  ***
import-builtins.mjs                           +0.19 %  n.s.     +0.13 %  n.s.
empty CJS entry                               +0.04 %  n.s.    +10.59 %  ***

so nothing gets penalized: with the snapshot the preloaded set is unchanged (they sit behind isBuildingSnapshot() rather than being removed), and without it util/url/the esm loader are loaded on first require at the same cost as before, just not up front for entry points that never touch them. the win shrinks as the app uses more of core, as you'd expect, but it doesn't go negative.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@nodejs-github-bot

nodejs-github-bot commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

CI: https://ci.nodejs.org/job/node-test-pull-request/76138/

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@nodejs-github-bot

nodejs-github-bot commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

CI: https://ci.nodejs.org/job/node-test-pull-request/76142/

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

avivkeller
avivkeller approved these changes Aug 21, 2026
View reviewed changes
@nodejs-github-bot

nodejs-github-bot commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

CI: https://ci.nodejs.org/job/node-test-pull-request/76174/

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@nodejs-github-bot

nodejs-github-bot commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

CI: https://ci.nodejs.org/job/node-test-pull-request/76222/

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@codebytere codebytere added the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 22, 2026
@nodejs-github-bot
nodejs-github-bot merged commit e38eede into nodejs:main Aug 22, 2026
71 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@nodejs-github-bot

nodejs-github-bot commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Landed in e38eede

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@nodejs-github-bot nodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 22, 2026
aduh95 pushed a commit that referenced this pull request Aug 25, 2026
@codebytere @aduh95
lib: load fewer builtins when bootstrapping without a snapshot
e275b95
Contexts that are not deserialized from the built-in snapshot -- worker
threads, and the main context of embedders that create their own
isolate or of `node --no-node-snapshot` -- compile (with the code cache
at best) every builtin the bootstrap touches, so each eagerly required
builtin is startup time (~0.15-0.4 ms apiece). A number of them are only
required eagerly so that they end up in the snapshot, or for features
the bootstrap path never uses.

Load lazily what those paths do not need:

- is_main_thread.js: preload util, url, the ESM loader (translators,
  resolver, module_job/map, source maps, node:module, vm modules, mime,
  data_url, the TypeScript stripper), internal/blob and
  internal/dns/utils only while building a snapshot; they load on first
  use otherwise.
- fs: internal/blob (+ internal/encoding and its tables) is only used
  by fs.openAsBlob().
- internal/url: internal/data_url (+ internal/mime) is only used by the
  Buffer-returning file URL helpers.
- internal/process/execution, the CommonJS loader, esm/translators and
  esm/load: the TypeScript stripper and data: URL helpers are only
  needed for TypeScript sources / data: URLs.
- pre_execution: internal/dns/utils (+ internal/net) is only needed up
  front to validate an explicit --dns-result-order or to register the
  resolver's snapshot serializer; the default order becomes the
  variable's initializer.
- internal/worker: event_loop_utilization and error_serdes are only
  needed once a sub-worker's ELU is read or it reports an error.
- worker_threads: `locks` is defined lazily, like util's lazy exports.

Main-thread startup with the snapshot is unchanged (the same modules
are preloaded into it; the bootstrap-modules test lists are adjusted).
A bare worker compiles 95 -> 83 builtins (cold start -5%); without the
snapshot an empty CommonJS entry point compiles 76 -> 59 builtins and an
empty ES module entry point 76 -> 69.

Signed-off-by: Shelley Vohr <[email protected]>
PR-URL: #65329
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Chengzhong Wu <[email protected]>
Reviewed-By: Aviv Keller <[email protected]>
aduh95 pushed a commit that referenced this pull request Aug 25, 2026
@codebytere @aduh95
lib: load fewer builtins when bootstrapping without a snapshot
e8e2abc
Contexts that are not deserialized from the built-in snapshot -- worker
threads, and the main context of embedders that create their own
isolate or of `node --no-node-snapshot` -- compile (with the code cache
at best) every builtin the bootstrap touches, so each eagerly required
builtin is startup time (~0.15-0.4 ms apiece). A number of them are only
required eagerly so that they end up in the snapshot, or for features
the bootstrap path never uses.

Load lazily what those paths do not need:

- is_main_thread.js: preload util, url, the ESM loader (translators,
  resolver, module_job/map, source maps, node:module, vm modules, mime,
  data_url, the TypeScript stripper), internal/blob and
  internal/dns/utils only while building a snapshot; they load on first
  use otherwise.
- fs: internal/blob (+ internal/encoding and its tables) is only used
  by fs.openAsBlob().
- internal/url: internal/data_url (+ internal/mime) is only used by the
  Buffer-returning file URL helpers.
- internal/process/execution, the CommonJS loader, esm/translators and
  esm/load: the TypeScript stripper and data: URL helpers are only
  needed for TypeScript sources / data: URLs.
- pre_execution: internal/dns/utils (+ internal/net) is only needed up
  front to validate an explicit --dns-result-order or to register the
  resolver's snapshot serializer; the default order becomes the
  variable's initializer.
- internal/worker: event_loop_utilization and error_serdes are only
  needed once a sub-worker's ELU is read or it reports an error.
- worker_threads: `locks` is defined lazily, like util's lazy exports.

Main-thread startup with the snapshot is unchanged (the same modules
are preloaded into it; the bootstrap-modules test lists are adjusted).
A bare worker compiles 95 -> 83 builtins (cold start -5%); without the
snapshot an empty CommonJS entry point compiles 76 -> 59 builtins and an
empty ES module entry point 76 -> 69.

Signed-off-by: Shelley Vohr <[email protected]>
PR-URL: #65329
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Chengzhong Wu <[email protected]>
Reviewed-By: Aviv Keller <[email protected]>
@github-actions github-actions Bot mentioned this pull request Aug 25, 2026
2026-08-26, Version 26.8.0 (Current) #65551
Merged
@panva panva mentioned this pull request Aug 26, 2026
RuntimeError: unreachable #64226
Closed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@aduh95 aduh95 aduh95 approved these changes
@jasnell jasnell jasnell approved these changes
@legendecas legendecas legendecas approved these changes
@avivkeller avivkeller avivkeller approved these changes
@joyeecheung joyeecheung Awaiting requested review from joyeecheung
@mcollina mcollina Awaiting requested review from mcollina

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

lib / src Issues and PRs involving general changes in the lib/ or src/ directories.

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

9 participants

@codebytere @nodejs-github-bot @aduh95 @joyeecheung @mcollina @jasnell @legendecas @avivkeller @targos
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.