module: synchronously load most ES modules#62530
module: synchronously load most ES modules#62530GeoffreyBooth wants to merge 5 commits intonodejs:mainfrom
Conversation
|
Review requested:
|
2bb88f9 to
9a7728c
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #62530 +/- ##
==========================================
+ Coverage 89.69% 89.73% +0.03%
==========================================
Files 695 695
Lines 214417 214505 +88
Branches 41059 41071 +12
==========================================
+ Hits 192321 192476 +155
+ Misses 14156 14071 -85
- Partials 7940 7958 +18
🚀 New features to boost your workflow:
|
|
The PR description says there is an improvement but the number shows a regression? Although I don't think "a flat graph importing hundreds/thousands of modules" is a representative use case, so a regression probably doesn't matter all that much anyway. A more typical graph probably consists of a lot of nodes each with a dozen or so imports.. |
lib/internal/modules/run_main.js
Outdated
| const mainPath = resolvedMain || main; | ||
| const mainURL = getOptionValue('--entry-url') ? new URL(mainPath, getCWDURL()) : pathToFileURL(mainPath); | ||
|
|
||
| // When no async hooks or --inspect-brk are needed, try the fully synchronous path first. |
There was a problem hiding this comment.
Why is --inspect-brk an exception?
There was a problem hiding this comment.
In module_job.js there’s special handling for --inspect-brk:
node/lib/internal/modules/esm/module_job.js
Lines 321 to 326 in a9ac9b1
But further down in the file in the equivalent ModuleJobSync, there’s no such handling that I can see.
There was a problem hiding this comment.
I mimicked the pattern so that --inspect-brk is supported now.
9a7728c to
0aa5399
Compare
My apologies, I ran the benchmark where the new binary was built with
I updated the benchmark to create a tree with 10 imports per node, as large as necessary to match the desired size of the graph. I updated the PR description with the new results. Basically, they’re inconclusive, as you might expect for such a small change. Promises just don’t add much overhead. |
…th a branching factor of 10
2958720 to
e5294fb
Compare
Building on #55782, this PR uses the path @joyeecheung created for
require(esm)to synchronously resolve and load all ES modules that lack top-levelawait, which is the vast majority of modules. The sync path is used when no async loader hooks,--importflags, or--inspect-brkare active; it falls back to the existing async path otherwise. Top-levelawaitpresence can only be determined after the module graph is instantiated, so if TLA is detected the already-instantiated graph falls back to async evaluation. In all cases the behavior is identical to the existing async path.On current
main, an ES module graph generates 14 + 5N promises for N modules; so 19 promises for a single module graph (one entry point that doesn’t import anything), 24 promises if that entry point imports one file, 29 promises for a three-module graph and so on.In this PR, only one promise is created regardless of graph size: the low-level V8
module.evaluate()call that happens withinmodule.evaluateSync(), where an immediately-resolved promise is created even for modules that don’t have top-levelawait. But still, it’s only one promise for an entire application, no matter how big the app is.This PR adds a benchmark that focuses on the module loading flow that this PR improves:
So basically it’s within the margin of error.