Skip to content
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: print amount of load time of a module #52213

Merged
merged 29 commits into from
May 30, 2024

Conversation

H4ad
Copy link
Member

@H4ad H4ad commented Mar 26, 2024

I was investigating why npm is slow and I started to figure out that was caused by it loading a lot of modules/js files, but I didn't know what are those modules or even how much time it took to load them.

So, this implementation came to my mind, and I used with the following commands:

NODE_DEBUG=module_cjs ~/Projects/opensource/node-copy-4/out/Release/node ./bin/npm-cli.js run echo --silent &> trace-node.txt

The log looks like this:

MODULE_CJS 413480 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/lib/commands/run-script.js] [../utils/did-you-mean.js]: 0.499ms
MODULE_CJS 413480 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/lib/commands/run-script.js] [../utils/is-windows.js]: 0.102ms
MODULE_CJS 413480 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/lib/base-command.js] [path]: 0.005ms
MODULE_CJS 413480 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/lib/base-command.js] [@npmcli/config/lib/definitions]: 0.001ms
MODULE_CJS 413480 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/lib/workspaces/get-workspaces.js] [path]: 0.003ms
MODULE_CJS 413480 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/lib/workspaces/get-workspaces.js] [@npmcli/map-workspaces]: 0.051ms
MODULE_CJS 413480 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/lib/workspaces/get-workspaces.js] [minimatch]: 0.055ms
MODULE_CJS 413480 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/lib/workspaces/get-workspaces.js] [@npmcli/package-json]: 0.044ms
MODULE_CJS 413480 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/lib/base-command.js] [./workspaces/get-workspaces.js]: 0.346ms
MODULE_CJS 413480 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/lib/base-command.js] [./utils/cmd-list]: 0.027ms
MODULE_CJS 413480 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/lib/base-command.js] [./utils/log-shim.js]: 0.012ms
MODULE_CJS 413480 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/lib/commands/run-script.js] [../base-command.js]: 0.638ms
MODULE_CJS 413480 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/lib/npm.js] [./commands/run-script.js]: 12.02ms

If we try to clean a little bit:

$ cat trace-node.txt | tr . , | sort -sbng -k5,16n -t ' ' | tr , . > formatted-timing.txt

The output was:

MODULE_TIMER 413856 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/node_modules/@npmcli/agent/lib/proxy.js] [socks-proxy-agent]: 13.046ms
MODULE_TIMER 413856 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/lib/npm.js] [./commands/run-script.js]: 13.548ms
MODULE_TIMER 413856 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/lib/cli-entry.js] [./npm.js]: 16.606ms
MODULE_TIMER 413856 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/node_modules/@npmcli/agent/lib/index.js] [./proxy.js]: 18.73ms
MODULE_TIMER 413856 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/node_modules/make-fetch-happen/lib/cache/entry.js] [cacache]: 19.421ms
MODULE_TIMER 413856 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/node_modules/make-fetch-happen/lib/remote.js] [@npmcli/agent]: 20.019ms
MODULE_TIMER 413856 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/node_modules/make-fetch-happen/lib/cache/entry.js] [../remote.js]: 21.397ms
MODULE_TIMER 413856 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/node_modules/make-fetch-happen/lib/cache/index.js] [./entry.js]: 42.496ms
MODULE_TIMER 413856 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/node_modules/make-fetch-happen/lib/fetch.js] [./cache/index.js]: 42.8ms
MODULE_TIMER 413856 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/node_modules/make-fetch-happen/lib/index.js] [./fetch.js]: 46.18ms
MODULE_TIMER 413856 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/node_modules/npm-registry-fetch/lib/index.js] [make-fetch-happen]: 47.044ms
MODULE_TIMER 413856 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/lib/utils/replace-info.js] [npm-registry-fetch]: 64.717ms
MODULE_TIMER 413856 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/lib/utils/error-message.js] [./replace-info.js]: 64.867ms
MODULE_TIMER 413856 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/lib/utils/exit-handler.js] [./error-message.js]: 73.463ms
MODULE_TIMER 413856 [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/lib/cli-entry.js] [./utils/exit-handler.js]: 85.115ms
MODULE_TIMER 413856 [] [/home/h4ad/Projects/opensource/performance-analysis/npm-cli/bin/npm-cli.js]: 117.08ms

In this way, is very clear what is taking a lot of time to load npm, and I actually already opened a PR to fix one of the issues that appeared in this log (npm/cli#7314)


@joyeecheung suggest to include also the support for trace_events, so I created the debugWithTimer to introduce support for both ways extract information, via log or via trace:

$ ./out/Release/node --trace-event-categories node.module_timer --trace-event-file-pattern 'trace-events.log' ~/Projects/opensource/performance-analysis/npm-cli/bin/npm-cli.js run echo --silent

This will generate a trace-events.log file that can be imported on chrome://tracing or Perfetto UI to generate the following graph:

image

To be easier to read, I introduce two labels on the debugWithTimer, the first one will be for logging via NODE_DEBUG and the second one will be used at the trace.

/cc @nodejs/performance

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/loaders

@nodejs-github-bot nodejs-github-bot added the needs-ci PRs that need a full CI run. label Mar 26, 2024
@GeoffreyBooth
Copy link
Member

Could this perhaps get its own label in NODE_DEBUG? I feel like optimizing module load time is an infrequent use case for debugging modules and these logging lines must be very spammy.

@H4ad H4ad force-pushed the trace-load-module branch 2 times, most recently from 82249f3 to 838237a Compare March 26, 2024 14:05
@H4ad
Copy link
Member Author

H4ad commented Mar 26, 2024

@GeoffreyBooth I changed to TIMING_MODULE_CJS, we can also add support for esm under TEST_MODULE_ESM, and then we can measure both cases.

I didn't found any good alternative for this name, so I'm open to change this to another one if needed.

@richardlau
Copy link
Member

Conceptually this feels more like something for trace_events than NODE_DEBUG?

@H4ad H4ad force-pushed the trace-load-module branch 2 times, most recently from b80bab3 to ac6e394 Compare March 27, 2024 10:33
@H4ad
Copy link
Member Author

H4ad commented Mar 27, 2024

@richardlau Maybe I'm not familiar with trace_events but from what I see in the source code, I think it won't be as useful as having it as NODE_DEBUG since to be able to listen to the events we need to import trace_events and then createTracing.

The benefits of emitting these events through NODE_DEBUG is because it is very easy to listen to these events without any additional code, plus even importing trace_events will affect the events that will be emitted by this new debug, so I don't ' I don't think the implementation with trace_events will be as useful as this.

@joyeecheung
Copy link
Member

createTracing() is for user-land tracing to get hooked into our tracing infra. Pretty sure internally we can just use internalBinding('trace_events').trace, which is already what we load during startup.

@joyeecheung
Copy link
Member

Actually console.time() and console.timeEnd() is hooked into the tracing infra via internalBinding('trace_events').trace too. So for timing-related traces, adding some sort of support in debuglog to also emit traces via internalBinding('trace_events').trace sounds like a good idea. You get to both check out human-readable information via stdout, and get a trace file you can visualize using e.g. DevTools.

@richardlau richardlau added the semver-minor PRs that contain new features and should be released in the next minor version. label Mar 27, 2024
@H4ad
Copy link
Member Author

H4ad commented Mar 28, 2024

@joyeecheung I changed the implementation to match what we discussed, I liked this new way, it will print in the stderr if the NODE_DEBUG=module_cjs or it will trace if the category is node. debuglog_{set}, in this case, node.debuglog_module_cjs.

I still have some tests to fix but in general the implementation is good enough to get some reviews.

@H4ad
Copy link
Member Author

H4ad commented Mar 28, 2024

image

If we use the chrome://tracing, we can see what module is loading which module (I think we can improving this visualization).

@H4ad
Copy link
Member Author

H4ad commented Mar 30, 2024

I fixed the tests and I also changed a little bit the visualization for trace, I updated the PR to include the new graph and the new logging example.

@H4ad H4ad requested review from joyeecheung and lemire March 30, 2024 14:10
@H4ad H4ad added the review wanted PRs that need reviews. label Mar 30, 2024
Copy link
Member

@RafaelGSS RafaelGSS left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you update the PR to include only the changes this PR is about? I can see some linting and type changes.

lib/internal/modules/cjs/loader.js Outdated Show resolved Hide resolved
lib/internal/modules/cjs/loader.js Outdated Show resolved Hide resolved
lib/internal/util/debuglog.js Outdated Show resolved Hide resolved
@H4ad H4ad added the request-ci Add this label to start a Jenkins CI on a PR. label May 29, 2024
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label May 29, 2024
@nodejs-github-bot
Copy link
Collaborator

@nodejs-github-bot
Copy link
Collaborator

@nodejs-github-bot
Copy link
Collaborator

@nodejs-github-bot
Copy link
Collaborator

@nodejs-github-bot
Copy link
Collaborator

@H4ad H4ad added commit-queue Add this label to land a pull request using GitHub Actions. commit-queue-squash Add this label to instruct the Commit Queue to squash all the PR commits into the first one. labels May 30, 2024
@nodejs-github-bot nodejs-github-bot added commit-queue-failed An error occurred while landing this pull request using GitHub Actions. and removed commit-queue Add this label to land a pull request using GitHub Actions. labels May 30, 2024
@nodejs-github-bot
Copy link
Collaborator

Commit Queue failed
- Loading data for nodejs/node/pull/52213
✔  Done loading data for nodejs/node/pull/52213
----------------------------------- PR info ------------------------------------
Title      module: print amount of load time of a module (#52213)
   ⚠  Could not retrieve the email or name of the PR author's from user's GitHub profile!
Branch     H4ad:trace-load-module -> nodejs:main
Labels     semver-minor, author ready, needs-ci, review wanted, commit-queue-squash
Commits    29
 - module: print amount of load time of a cjs module
 - try support both node_debug and trace by introducing a new module
 - add trace label to better visualization
 - fix lint issue with max line
 - lazy load trace_timer
 - fix lint issues introduce when lazy load
 - cleanup and added more tests
 - remove unecessary changes
 - remove unecessary changes
 - resolve rebase conflicts
 - fixing nits
 - resolve conflicts after rebase
 - fix lint issues
 - handle unexpected errors during _load
 - add wrap module load function
 - removed unused spaces
 - fixing tests on last modification
 - escape backslash when is label
 - fix references for _load
 - add test to check against un-escaped backslash
 - fix issues with test & missing fn
 - making eslint happy with trailing space
 - minor changes
 - fix missing changing the name of the module
 - try get more info from ci
 - try get more info from ci & make lint happy
 - fix import of common and spawnSync
 - do not cache process.execPath on import
 - thanks richard, you are amazing
Committers 1
 - Vinícius Lourenço 
PR-URL: https://github.com/nodejs/node/pull/52213
Reviewed-By: Joyee Cheung 
Reviewed-By: James M Snell 
Reviewed-By: Matteo Collina 
------------------------------ Generated metadata ------------------------------
PR-URL: https://github.com/nodejs/node/pull/52213
Reviewed-By: Joyee Cheung 
Reviewed-By: James M Snell 
Reviewed-By: Matteo Collina 
--------------------------------------------------------------------------------
   ⚠  Commits were pushed since the last approving review:
   ⚠  - module: print amount of load time of a cjs module
   ⚠  - try support both node_debug and trace by introducing a new module
   ⚠  - add trace label to better visualization
   ⚠  - fix lint issue with max line
   ⚠  - lazy load trace_timer
   ⚠  - fix lint issues introduce when lazy load
   ⚠  - cleanup and added more tests
   ⚠  - remove unecessary changes
   ⚠  - remove unecessary changes
   ⚠  - resolve rebase conflicts
   ⚠  - fixing nits
   ⚠  - resolve conflicts after rebase
   ⚠  - fix lint issues
   ⚠  - handle unexpected errors during _load
   ⚠  - add wrap module load function
   ⚠  - removed unused spaces
   ⚠  - fixing tests on last modification
   ⚠  - escape backslash when is label
   ⚠  - fix references for _load
   ⚠  - add test to check against un-escaped backslash
   ⚠  - fix issues with test & missing fn
   ⚠  - making eslint happy with trailing space
   ⚠  - minor changes
   ⚠  - fix missing changing the name of the module
   ⚠  - try get more info from ci
   ⚠  - try get more info from ci & make lint happy
   ⚠  - fix import of common and spawnSync
   ⚠  - do not cache process.execPath on import
   ⚠  - thanks richard, you are amazing
   ℹ  This PR was created on Tue, 26 Mar 2024 01:41:52 GMT
   ✔  Approvals: 3
   ✔  - Joyee Cheung (@joyeecheung) (TSC): https://github.com/nodejs/node/pull/52213#pullrequestreview-1997745510
   ✔  - James M Snell (@jasnell) (TSC): https://github.com/nodejs/node/pull/52213#pullrequestreview-2026657587
   ✔  - Matteo Collina (@mcollina) (TSC): https://github.com/nodejs/node/pull/52213#pullrequestreview-2035376215
   ✔  Last GitHub CI successful
   ℹ  Last Full PR CI on 2024-05-29T21:59:32Z: https://ci.nodejs.org/job/node-test-pull-request/59531/
- Querying data for job/node-test-pull-request/59531/
   ✔  Last Jenkins CI successful
--------------------------------------------------------------------------------
   ✔  Aborted `git node land` session in /home/runner/work/node/node/.ncu
https://github.com/nodejs/node/actions/runs/9294909258

@H4ad
Copy link
Member Author

H4ad commented May 30, 2024

@joyeecheung We are closer, just one (re)approval :)

Copy link
Member

@joyeecheung joyeecheung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job! LGTM

@joyeecheung joyeecheung added commit-queue Add this label to land a pull request using GitHub Actions. and removed commit-queue-failed An error occurred while landing this pull request using GitHub Actions. labels May 30, 2024
@nodejs-github-bot nodejs-github-bot removed the commit-queue Add this label to land a pull request using GitHub Actions. label May 30, 2024
@nodejs-github-bot nodejs-github-bot merged commit f883865 into nodejs:main May 30, 2024
65 checks passed
@nodejs-github-bot
Copy link
Collaborator

Landed in f883865

@H4ad H4ad deleted the trace-load-module branch May 30, 2024 17:01
targos pushed a commit that referenced this pull request Jun 1, 2024
PR-URL: #52213
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
lukins-cz pushed a commit to lukins-cz/OS-Aplet-node that referenced this pull request Jun 1, 2024
PR-URL: nodejs#52213
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
RafaelGSS added a commit that referenced this pull request Jun 7, 2024
Notable changes:

buffer:
  * (SEMVER-MINOR) add .bytes() method to Blob (Matthew Aitken) #53221
cli:
  * (SEMVER-MINOR) add `NODE_RUN_PACKAGE_JSON_PATH` env (Yagiz Nizipli) #53058
  * (SEMVER-MINOR) add `NODE_RUN_SCRIPT_NAME` env to `node --run` (Yagiz Nizipli) #53032
doc:
  * (SEMVER-MINOR) add context.assert docs (Colin Ihrig) #53169
  * (SEMVER-MINOR) improve explanation about built-in modules (Joyee Cheung) #52762
  * add StefanStojanovic to collaborators (StefanStojanovic) #53118
fs:
  * mark recursive cp methods as stable (Théo LUDWIG) #53127
lib:
  * (SEMVER-MINOR) add EventSource Client (Aras Abbasi) #51575
  * (SEMVER-MINOR) replace MessageEvent with undici's (Matthew Aitken) #52370
module:
  * (SEMVER-MINOR) print amount of load time of a cjs module (Vinicius Lourenço) #52213
net:
  * (SEMVER-MINOR) add new net.server.listen tracing channel (Paolo Insogna) #53136
process:
  * (SEMVER-MINOR) add process.getBuiltinModule(id) (Joyee Cheung) #52762
src,permission:
  * (SEMVER-MINOR) --allow-wasi & prevent WASI exec (Rafael Gonzaga) #53124
test_runner:
  * (SEMVER-MINOR) add snapshot testing (Colin Ihrig) #53169
  * (SEMVER-MINOR) add context.fullName (Colin Ihrig) #53169
  * (SEMVER-MINOR) support module mocking (Colin Ihrig) #52848

PR-URL: TODO
@RafaelGSS RafaelGSS mentioned this pull request Jun 7, 2024
RafaelGSS added a commit that referenced this pull request Jun 7, 2024
Notable changes:

buffer:
  * (SEMVER-MINOR) add .bytes() method to Blob (Matthew Aitken) #53221
cli:
  * (SEMVER-MINOR) add `NODE_RUN_PACKAGE_JSON_PATH` env (Yagiz Nizipli) #53058
  * (SEMVER-MINOR) add `NODE_RUN_SCRIPT_NAME` env to `node --run` (Yagiz Nizipli) #53032
doc:
  * (SEMVER-MINOR) add context.assert docs (Colin Ihrig) #53169
  * (SEMVER-MINOR) improve explanation about built-in modules (Joyee Cheung) #52762
  * add StefanStojanovic to collaborators (StefanStojanovic) #53118
  * add Marco Ippolito to TSC (Rafael Gonzaga) #53008
fs:
  * mark recursive cp methods as stable (Théo LUDWIG) #53127
lib:
  * (SEMVER-MINOR) add EventSource Client (Aras Abbasi) #51575
  * (SEMVER-MINOR) replace MessageEvent with undici's (Matthew Aitken) #52370
module:
  * (SEMVER-MINOR) print amount of load time of a cjs module (Vinicius Lourenço) #52213
net:
  * (SEMVER-MINOR) add new net.server.listen tracing channel (Paolo Insogna) #53136
process:
  * (SEMVER-MINOR) add process.getBuiltinModule(id) (Joyee Cheung) #52762
src,permission:
  * (SEMVER-MINOR) --allow-wasi & prevent WASI exec (Rafael Gonzaga) #53124
test_runner:
  * (SEMVER-MINOR) add snapshot testing (Colin Ihrig) #53169
  * (SEMVER-MINOR) add context.fullName (Colin Ihrig) #53169
  * (SEMVER-MINOR) support module mocking (Colin Ihrig) #52848

PR-URL: #53379
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
author ready PRs that have at least one approval, no pending requests for changes, and a CI started. commit-queue-squash Add this label to instruct the Commit Queue to squash all the PR commits into the first one. needs-ci PRs that need a full CI run. review wanted PRs that need reviews. semver-minor PRs that contain new features and should be released in the next minor version.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

10 participants