Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 34.2k
loader: speed up line length calculation used by moduleProvider#50969
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
loader: speed up line length calculation used by moduleProvider #50969
Uh oh!
There was an error while loading. Please reload this page.
Conversation
zeusdeux commented Nov 29, 2023 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
172138f to 686ce4eComparezeusdeux commented Nov 29, 2023
Pinging the modules team since the loaders WG readme doesn't mention who is in it (apologies if you aren't working on loaders but got pinged here 🙏🏽 ). |
GeoffreyBooth commented Nov 29, 2023 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
Next time please just ping @nodejs/loaders. cc @nodejs/performance |
zeusdeux commented Nov 29, 2023 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
Ah so those teams do exist! I assumed they didn't as the autocomplete here didn't show them. 🤦🏽♂️ ![]() |
Uh oh!
There was an error while loading. Please reload this page.
| // account in coverage calculations. | ||
| // codepoints for \n, \u2028 and \u2029 | ||
| if(codePoint===10||codePoint===0x2028||codePoint===0x2029){ | ||
| output.push(lineLength); |
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.
| output.push(lineLength); | |
| ArrayPrototypePush(output,lineLength); |
| lineLength=-1;// To not count the matched codePoint such as \n character | ||
| } | ||
| } | ||
| output.push(lineLength); |
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.
| output.push(lineLength); | |
| ArrayPrototypePush(output,lineLength); |
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.
ArrayPrototypePush has performance implications according to primordials.md. Since adding primordials is optional, I'm strongly against adding it for array.prototype.push in this PR.
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.
I think for source map support, we don't want user code to be able to mess with internal implementation – i.e. we should take the potential performance penalty and generate reliably correct stack traces. Also, we should measure the perf difference, as it might be negligable.
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.
alternatively, output[output.length] = lineLength?
nodejs-github-bot commented Nov 29, 2023
Uh oh!
There was an error while loading. Please reload this page.
| lineLength=-1;// To not count the matched codePoint such as \n character | ||
| } | ||
| } | ||
| output.push(lineLength); |
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.
| output.push(lineLength); | |
| ArrayPrototypePush(output,lineLength); |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
4e8a83f to 813a604Compare| // account in coverage calculations. | ||
| // codepoints for \n (new line), \u2028 (line separator) and \u2029 (paragraph separator) | ||
| if(codePoint===10||codePoint===0x2028||codePoint===0x2029){ | ||
| output.push(lineLength) |
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.
| output.push(lineLength) | |
| ArrayPrototypePush(output,lineLength); |
| // account in coverage calculations. | ||
| // codepoints for \n (new line), \u2028 (line separator) and \u2029 (paragraph separator) | ||
| if(codePoint===10||codePoint===0x2028||codePoint===0x2029){ | ||
| output.push(lineLength) |
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.
Using the non-primordial version due to ArrayPrototypePush being listed as having perf issues — https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md#primordials-with-known-performance-issues
| lineLength=-1;// To not count the matched codePoint such as \n character | ||
| } | ||
| } | ||
| output.push(lineLength) |
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.
| output.push(lineLength) | |
| ArrayPrototypePush(output,lineLength); |
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.
ArrayPrototypePush has known perf issues — #50969 (comment)
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.
Using .push has reliability issues, as user code could affect internals.
813a604 to 133f887Comparezeusdeux commented Nov 29, 2023 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
@aduh95@GeoffreyBooth@anonrig What's the call on the usage of |
anonrig commented Nov 29, 2023
@zeusdeux Primordials are optional and not mandatory for new pull requests. It's your decision. Documentation says that:
Ref: https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md |
GeoffreyBooth commented Nov 30, 2023
I don’t have a preference. I thought we were still debating whether they should be required, but I don’t really see the need. |
When using a loader, for say TypeScript, the esm loader invokes the `lineLengths` function via `maybeCacheSourceMap` when sourcemaps are enabled. Therefore, `lineLengths` ends up getting called quite often when running large servers written in TypeScript for example. Making `lineLengths` faster should therefore speed up server startup times for anyone using a loader with node with sourcemaps enabled. The change itself is fairly simple and is all about removing creation of unnecessary memory and iterating the whole source content only once with the hope of making the function cache friendly.
zeusdeux commented Nov 30, 2023 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
364939f to 51f44abCompareGeoffreyBooth commented Nov 30, 2023
If you're going to use that one you might as well use the others too? |
ljharb left a comment
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.
looks great, robustness > performance, altho even better if we can get both.
you may want to benchmark arr[arr.length] = x over ArrayPrototypePush(arr, x) just in case tho
aduh95 left a comment
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.
Great to hear we can reconcile robustness and perf. Ideally we would run benchmarks after each V8 upgrades to see if the claims in primordials.md are still correct, but it's very time consuming, and easy to get false positive.
If you want to check the performance of arr[arr.length] = value and setOwnProperty(arr, arr.length, value) for completeness, that'd be useful data.
zeusdeux commented Nov 30, 2023
@GeoffreyBooth which other ones? This PR uses |
aduh95 commented Nov 30, 2023 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
Sure, whatever works for you. But FYI this PR needs to wait at least 48 hours after it was opened before we can merge it to give time for folks to review, so it may be quicker to get the data before the PR is merged depending on what are your availabilities. |
nodejs-github-bot commented Dec 1, 2023
Commit Queue failed- Loading data for nodejs/node/pull/50969 ✔ Done loading data for nodejs/node/pull/50969 ----------------------------------- PR info ------------------------------------ Title loader: speed up line length calculation used by moduleProvider (#50969) Author Mudit (@zeusdeux) Branch zeusdeux:speed-up-source-map-processing -> nodejs:main Labels module, performance, needs-ci, source maps Commits 1 - loader: speed up line length calc used by moduleProvider Committers 1 - Mudit Ameta PR-URL: https://github.com/nodejs/node/pull/50969 Reviewed-By: Yagiz Nizipli Reviewed-By: Geoffrey Booth Reviewed-By: Antoine du Hamel ------------------------------ Generated metadata ------------------------------ PR-URL: https://github.com/nodejs/node/pull/50969 Reviewed-By: Yagiz Nizipli Reviewed-By: Geoffrey Booth Reviewed-By: Antoine du Hamel -------------------------------------------------------------------------------- ℹ This PR was created on Wed, 29 Nov 2023 22:02:05 GMT ✔ Approvals: 3 ✔ - Yagiz Nizipli (@anonrig) (TSC): https://github.com/nodejs/node/pull/50969#pullrequestreview-1758724448 ✔ - Geoffrey Booth (@GeoffreyBooth) (TSC): https://github.com/nodejs/node/pull/50969#pullrequestreview-1756426036 ✔ - Antoine du Hamel (@aduh95) (TSC): https://github.com/nodejs/node/pull/50969#pullrequestreview-1757049826 ✔ Last GitHub CI successful ℹ Last Full PR CI on 2023-11-29T22:54:10Z: https://ci.nodejs.org/job/node-test-pull-request/56011/ ⚠ Commits were pushed after the last Full PR CI run: ⚠ - loader: speed up line length calc used by moduleProvider - Querying data for job/node-test-pull-request/56011/ ✔ Last Jenkins CI successful -------------------------------------------------------------------------------- ✔ Aborted `git node land` session in /home/runner/work/node/node/.ncuhttps://github.com/nodejs/node/actions/runs/7065693910 |
nodejs-github-bot commented Dec 2, 2023
JakobJingleheimer left a comment • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
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.
Huzzah 🙌 Thanks for this!
Out of curiosity, where did the huge perf gain come from? arr.map → for, or RegExp → equality assertion, or both significantly contributed? (I would guess both)
nodejs-github-bot commented Dec 2, 2023
Landed in d5a7acf |
zeusdeux commented Dec 3, 2023
@JakobJingleheimer Thanks! The speed up was from the combination of the following —
Basically doing only what's needed and doing it using an known optimizable construct. Finally, no extra memory overhead than absolutely necessary. Thinking in C rather than JS in a way. Functional programming is good but it's usually horrible for perf so going old school for parts that are sensitive to perf is always a win! |
When using a loader, for say TypeScript, the esm loader invokes the `lineLengths` function via `maybeCacheSourceMap` when sourcemaps are enabled. Therefore, `lineLengths` ends up getting called quite often when running large servers written in TypeScript for example. Making `lineLengths` faster should therefore speed up server startup times for anyone using a loader with node with sourcemaps enabled. The change itself is fairly simple and is all about removing creation of unnecessary memory and iterating the whole source content only once with the hope of making the function cache friendly. PR-URL: #50969 Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Geoffrey Booth <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]> Reviewed-By: Vinícius Lourenço Claro Cardoso <[email protected]> Reviewed-By: Jacob Smith <[email protected]>
When using a loader, for say TypeScript, the esm loader invokes the `lineLengths` function via `maybeCacheSourceMap` when sourcemaps are enabled. Therefore, `lineLengths` ends up getting called quite often when running large servers written in TypeScript for example. Making `lineLengths` faster should therefore speed up server startup times for anyone using a loader with node with sourcemaps enabled. The change itself is fairly simple and is all about removing creation of unnecessary memory and iterating the whole source content only once with the hope of making the function cache friendly. PR-URL: #50969 Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Geoffrey Booth <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]> Reviewed-By: Vinícius Lourenço Claro Cardoso <[email protected]> Reviewed-By: Jacob Smith <[email protected]>


When using a loader, for e.g., say TypeScript, the esm loader invokes the
lineLengthsfunction viamaybeCacheSourceMapwhen sourcemaps are enabled. Therefore,lineLengthsends up getting called quite often when running large servers written in TypeScript for example. MakinglineLengthsfaster should therefore speed up server startup times for anyone using a loader with node with sourcemaps enabled.The change itself is fairly simple and is all about removing creation of unnecessary memory and iterating the whole source content only once with the hope of making the function cache friendly.
make -j4 testpassesAlongside that, below are the
cpuprofiles of node 20.9.0 vs nodemainwith this patch taken during the startup of large server (from work) written in TypeScript loaded using esno (a proxy to esbuild-register) showing a ~98% reduction in time taken bymoduleProvider.node 20.9.0
node
main+ patch from this PR