Skip to content

perf: avoid loading full node:stream module in internal modules - #27400

Draft
sosukesuzuki wants to merge 5 commits into
mainfrom
claude/stream-lazy-init
Draft

sosukesuzuki wants to merge 5 commits into
mainfrom
claude/stream-lazy-init

Conversation

@sosukesuzuki

Copy link
Copy Markdown
Member

Summary

Replace require("node:stream") with direct submodule requires (internal/streams/readable, internal/streams/writable, etc.) in files that only use a subset of the stream module. This avoids eagerly loading unused submodules (Transform, Duplex, PassThrough, pipeline, operators, etc.) at startup.

Changed files

  • src/js/internal/fs/streams.ts — Readable, Writable, finished
  • src/js/node/zlib.ts — Transform, finished
  • src/js/node/_http_server.ts — Duplex, Stream
  • src/js/node/_http2_upgrade.ts — Duplex
  • src/js/node/http2.ts — Readable, Duplex, Stream
  • src/js/thirdparty/undici.js — Readable
  • src/js/builtins/CompressionStream.ts — Readable, Writable
  • src/js/builtins/DecompressionStream.ts — Readable, Writable

Benchmark (process.stdin startup cost, release build, median of 50 runs × 3 rounds)

Case Before (median) After (median) Improvement
process.stdin 4.42 / 4.55 / 4.42 ms 4.05 / 4.08 / 3.97 ms ~0.5ms (−10%)
require("node:stream") 4.27 / 4.33 / 4.18 ms 4.25 / 4.24 / 4.40 ms No change (expected)
fs.createReadStream 4.36 / 4.27 / 4.21 ms 3.86 / 3.88 / 3.83 ms ~0.5ms (−11%)

require("node:stream") itself is unchanged, confirming the improvement comes from avoiding unnecessary submodule loading.

Replace require('node:stream') with direct submodule requires:
- require('internal/streams/readable')
- require('internal/streams/writable')
- require('internal/streams/end-of-stream')

This avoids eagerly loading unused submodules (Transform, Duplex,
PassThrough, pipeline, operators, etc.) when accessing process.stdin,
fs.createReadStream, or fs.createWriteStream.

Benchmark shows ~0.5ms improvement in process.stdin startup cost.
Apply the same optimization to:
- node/zlib.ts: Transform, finished
- node/_http_server.ts: Duplex, Stream
- node/_http2_upgrade.ts: Duplex
- node/http2.ts: Readable, Duplex, Stream
- thirdparty/undici.js: Readable
- builtins/CompressionStream.ts: Readable, Writable
- builtins/DecompressionStream.ts: Readable, Writable

Each file now requires only the specific internal/streams/*
submodules it needs instead of the full node:stream module.
@robobun

robobun commented Feb 24, 2026 •

Copy link
Copy Markdown
Collaborator
Updated 11:20 AM PT - Feb 27th, 2026

❌ @sosukesuzuki, your commit 2529997 has 6 failures in Build #38282 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 27400

That installs a local version of the PR into your bun-27400 executable, so you can run:

bun-27400 --bun

end-of-stream exports the eos function as default export, not as
a named 'finished' export. Using { finished } destructuring caused
ERR_INVALID_ARG_TYPE because the callback was passed as options.

This branch has not been deployed

No deployments
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.

2 participants