-
Notifications
You must be signed in to change notification settings - Fork 29.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
module: eliminate performance cost of ESM syntax detection for CommonJS entry points #52093
module: eliminate performance cost of ESM syntax detection for CommonJS entry points #52093
Conversation
Review requested:
|
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
Can we flip the flag in v22?
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.
Definitely prefer this falling back from CJS loader behavior than the “check syntax first then choose the loader” approach before 👍 I think this approach was what I suggested at that time, not sure why it ended up the way it was. Though it would still be nicer if we could handle it via V8 caching but that can be a follow up.
Can you add the typescript.js bundle into the startup-core or startup-cli-version benchmark and run it in the benchmark CI? |
I think we should only flip the flag if we print a warning when the fallback is used (at least when it happens in non-entrypoints, or when the entry point is small enough). Users should mark the ESM explicitly wherever possible and be aware of the performance cost of the fallback. You probably don’t want thousands of your implicit .js ESMs to all get parsed as CJS first before falling back to ESM silently and wonder why your module loading is so slow…(and that could’ve been avoided if you just add a type filed to your package.json) |
Agreed on the warning. It would be great to ship it given it only simplifies dx. |
278b15f
to
5f7526c
Compare
Regarding the warning, I think we definitely want to avoid warning for two scenarios:
The user can’t add explicitness for the former without breaking their desired UX, and the user can’t fix the latter without patching dependencies. The case I’m unsure on is How about we warn only on ambiguous entry points where there is a |
5f7526c
to
f4cd945
Compare
Isn't that already the case? Right now this would not work anyway (it errors), why should we allow it without a warning? It seems to be encouraging package authors to publish their I agree we can do that for extension less files. Though I'd still prefer we have something better for indicating ESM for them (e.g. magical comments or directives - the use of magical comments isn't new anyway, e.g. for source maps or V8 compilation hints). |
I discovered that the warning was being printed incorrectly, because I was calling the error flow even when we were retrying as ESM. I fixed it so that the error flow is only used when we’re actually going to throw the CommonJS error, because we’re not retrying as ESM. So now the warning behavior is unchanged from before, which I think is fine for this PR. If we want to discuss warnings further that can be a separate PR. |
0fd062c
to
737e831
Compare
Done for startup-core: https://ci.nodejs.org/view/Node.js%20benchmark/job/benchmark-node-micro-benchmarks/1495/console The fixture isn’t the full TypeScript CLI. I don’t think there’s a way to get it to print the TypeScript version, so I didn’t add it to the startup-cli-version one. |
There were some failing tests until I moved the try/catch inside the check for The few tests that fail when the flag is enabled by default seem mostly to do with error output, because per this PR with the flag we’re catching and rethrowing the attempt to run the entry point. I think I might have a solution for the error output, though it’ll take some work to get working with source maps; or it might not be necessary at all if we unflag this at the same time as #51977. |
f62d92c
to
7a29330
Compare
Commit Queue failed- Loading data for nodejs/node/pull/52093 ✔ Done loading data for nodejs/node/pull/52093 ----------------------------------- PR info ------------------------------------ Title module: eliminate performance cost of ESM syntax detection for CommonJS entry points (#52093) ⚠ Could not retrieve the email or name of the PR author's from user's GitHub profile! Branch GeoffreyBooth:optimize-cjs-detection -> nodejs:main Labels c++, module, lib / src, performance, esm, author ready, needs-ci, commit-queue-squash Commits 16 - module: eliminate performance cost of detection for cjs entry - DRY the C++ functions - restore warning to be printed only on error - add TypeScript fixture to benchmark - put try/catch inside our flag, to postpone for now having to deal wit… - Apply suggestions from code review - Remove cjsRetryAsESMCache - ScriptCompiler::kNoCompileOptions - export error messages from c++ as constants - USE() - format - review notes - Use string approach - simplify array conversion - lint - nit Committers 1 - Geoffrey Booth PR-URL: https://github.com/nodejs/node/pull/52093 Reviewed-By: Matteo Collina Reviewed-By: Yagiz Nizipli Reviewed-By: Joyee Cheung Reviewed-By: Jacob Smith ------------------------------ Generated metadata ------------------------------ PR-URL: https://github.com/nodejs/node/pull/52093 Reviewed-By: Matteo Collina Reviewed-By: Yagiz Nizipli Reviewed-By: Joyee Cheung Reviewed-By: Jacob Smith -------------------------------------------------------------------------------- ⚠ Commits were pushed since the last approving review: ⚠ - lint ⚠ - nit ℹ This PR was created on Fri, 15 Mar 2024 05:07:50 GMT ✔ Approvals: 4 ✔ - Matteo Collina (@mcollina) (TSC): https://github.com/nodejs/node/pull/52093#pullrequestreview-1938208612 ✔ - Yagiz Nizipli (@anonrig) (TSC): https://github.com/nodejs/node/pull/52093#pullrequestreview-1944916206 ✔ - Joyee Cheung (@joyeecheung) (TSC): https://github.com/nodejs/node/pull/52093#pullrequestreview-1946897096 ✔ - Jacob Smith (@JakobJingleheimer): https://github.com/nodejs/node/pull/52093#pullrequestreview-1946928149 ✔ Last GitHub CI successful ℹ Last Full PR CI on 2024-03-20T12:55:16Z: https://ci.nodejs.org/job/node-test-pull-request/57855/ ℹ Last Benchmark CI on 2024-03-19T22:34:34Z: https://ci.nodejs.org/view/Node.js%20benchmark/job/benchmark-node-micro-benchmarks/1495/console - Querying data for job/node-test-pull-request/57855/ ✔ Last Jenkins CI successful -------------------------------------------------------------------------------- ✔ Aborted `git node land` session in /home/runner/work/node/node/.ncuhttps://github.com/nodejs/node/actions/runs/8361463879 |
Landed in 8bc7459 |
PR-URL: nodejs#52093 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz.nizipli@sentry.io> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Jacob Smith <jacob@frende.me> Reviewed-By: Richard Lau <rlau@redhat.com>
Can you create a manual backport for v20? It doesnt land cleanly |
This PR eliminates the performance penalty incurred by
--experimental-detect-module
for the common use case of CommonJS entry points:It does this by changing the startup flow for “ambiguous” entry points (where there’s no explicit file extension or
package.json
"type"
field). On currentmain
, there’s an initial file load and CommonJS parse to determine whether or not the such ambiguous entry points should be handled by the CommonJS or the ESM loader; and then the chosen loader reloads and reparses the entry point. In this PR, ambiguous files are always sent into the CommonJS loader, and only if it throws an error do we check if we should retry the entry point as ESM via the ESM loader. Because nothing “extra” happens for the common case of CommonJS entry points that parse successfully as CommonJS, there is no performance penalty for incurred for CommonJS entry points by having--experimental-detect-module
enabled by default.I haven’t gone further to investigate if additional optimizations are possible within the ESM loader; there probably are. But this was significant enough that I thought it could probably land on its own.
Additional benchmarks
@nodejs/loaders @nodejs/performance