child_process: build the default env block in one native pass - #65325
Open
codebytere wants to merge 1 commit into
Open
child_process: build the default env block in one native pass#65325codebytere wants to merge 1 commit into
codebytere wants to merge 1 commit into
Conversation
When spawn()/spawnSync() are called without options.env, normalizeSpawnArguments() copied process.env with a spread and then walked the copy to build the KEY=value array uv_spawn() takes. Spreading the process.env proxy costs one enumerator callback plus a query and a getter interceptor per variable, each doing a linear getenv() scan and allocating; with a couple of hundred variables that was the single largest JS-side cost of spawning a process. Add KVStore::Pairs() (Enumerate() + Get() by default, one uv_os_environ() pass for the real environment, skipping hidden variables on Windows exactly like Enumerate() does), expose it as process_wrap.getEnvPairs(), and use it for the default-environment case. A user supplied options.env and the permission model case keep the existing code. On Windows the same sort/first-wins-case-insensitive filter is applied to the pairs. The variables copyProcessEnvToEnv() propagates are part of the real environment by definition, and its entries cannot contain null bytes, so those steps only remain on the options.env path. Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
codebytere
force-pushed
the
perf/child_process-default-env-pairs
branch
from
August 16, 2026 15:51
40fcf9b to
a53bee9
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #65325 +/- ##
==========================================
- Coverage 90.13% 90.09% -0.04%
==========================================
Files 752 752
Lines 251568 251664 +96
Branches 47270 47282 +12
==========================================
- Hits 226759 226748 -11
- Misses 16168 16241 +73
- Partials 8641 8675 +34
🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
spawn(),spawnSync()and everything built on them get ~20–24 % faster when nooptions.envis passed (the commoncase), by not going through the
process.envproxy once per variable to build the child's environment block.(Linux x64, 183-variable environment - the effect scales with environment size; 30 runs.)
normalizeSpawnArguments()did{ ...process.env }and walked the copy to build theKEY=valuearray foruv_spawn().Spreading the proxy hits a query and a getter interceptor per variable, each a linear scan of the environment under the env
mutex plus an allocation - O(n²), and the largest JS-side cost of a spawn (~190 µs of 1.7 ms here).
This adds
KVStore::Pairs()(RealEnvStore: oneuv_os_environ()pass, skipping hidden=X:entries on Windows likeEnumerate()does; default:Enumerate()+Get()), exposes it asinternalBinding('process_wrap').getEnvPairs(),and uses it when no
options.envis given and the permission model is off. Everything else keeps the existing path.Notes:
process.envchanges). Windows keeps itscase-insensitive de-duplication.
123) were hoisted tothe front by object key ordering. That is the only observable difference for a normal environment.
Object.prototypeno longer leak into the default child environment.options.envobjects keep their documented prototype-inclusive behavior.
Tests:
test-child-process-default-env.js(new): the child sees exactly the parent's current environment (contentsand, on POSIX, order) via
spawnSync,execFileSyncandspawn, including variables added/deleted/emptied at runtime,values with
=and non-ASCII; a userenvis passed through unmerged; a variable set after one spawn is seen by the next.Disclosure: the code, test, measurements and this description were written by Claude Code, directed and reviewed by @codebytere.