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

⚑ Performance: Project service spends excess time cleaning client files when called synchronously #59335

Closed
1 task done
JoshuaKGoldberg opened this issue Jul 17, 2024 · 7 comments Β· Fixed by #59689
Closed
1 task done
Assignees
Labels
API Relates to the public API for TypeScript Domain: Performance Reports of unusually slow behavior Fix Available A PR has been opened for this issue Infrastructure Issue relates to TypeScript team infrastructure

Comments

@JoshuaKGoldberg
Copy link
Contributor

Acknowledgement

  • I acknowledge that issues using this template may be closed without further explanation at the maintainer's discretion.

Comment

πŸ”Ž Search Terms

project service cleanupProjectsAndScriptInfos openClientFileWithNormalizedPath openClientFile

πŸ™ Actual behavior

TypeScript project services open a file with service.openClientFile(filePathAbsolute, ...). That internally calls to a cleanupProjectsAndScriptInfos to clean up projects, such as removing orphans. There's no way to skip that cleanup.

When using typescript-eslint's parserOptions.projectService, that cleanup adds (seemingly?) unnecessary overhead per file being linted.

πŸ™‚ Expected behavior

When I tried commenting out its contents of cleanupProjectsAndScriptInfos locally, the time for a lint run of ~1024 files with the project service speed up by ~15-20%, from ~3.2 seconds to ~2.5 seconds. ⚑

Request: could we add an option to the ProjectService class to skip cleanups, please?

Alternate request: failing that, maybe the cleanups can be debounced and/or delayed in some way? E.g. added to a queue that only executes if the project service is still active?

Additional information about the issue

On the TypeScript side:

On the typescript-eslint side:

cc @sheetalkamat as FYI, after a pairing with @jakebailey.

@jakebailey suggested I also include a screenshot like this speedscope.app view of an ESLint CLI run with parserOptions.projectService:

speedscope.app time order view of an ESLint CLI run, find-highlighting multiple wide instances of 'cleanupProjectsAndScriptInfos'

@sheetalkamat
Copy link
Member

I will look into performance of cleanupProjectsAndScriptInfos but are you opening one file at a time. You batch open files using protocol.CommandTypes.UpdateOpen which uses projectService.applyChangesInOpenFiles (which i just realise is internal but worth a try) so cleanup happens at the end of opening all those files.

The cleanup is two folds:

  • Deleted files and projects are deferred and kept around.
  • Closed files and projects that are no longer needed are removed.
    So i am not sure its good idea to skip that part..

This is already delayed portion where we dont do this immediately (like file delete or close) but on file open because in most cases projects and script infos in editor scenario can be reused by next file open.

@jakebailey
Copy link
Member

Unfortunately the access pattern of eslint itself is undefined; all it does is call the plugin to ask to parse a specific file. It doesn't provide the list up front or say when it's no longer using a file anymore... 😞

@sheetalkamat sheetalkamat self-assigned this Jul 18, 2024
@DanielRosenwasser DanielRosenwasser added this to the TypeScript 5.7.0 milestone Aug 19, 2024
@DanielRosenwasser DanielRosenwasser added Infrastructure Issue relates to TypeScript team infrastructure Domain: Performance Reports of unusually slow behavior API Relates to the public API for TypeScript labels Aug 19, 2024
@typescript-bot typescript-bot added the Fix Available A PR has been opened for this issue label Aug 19, 2024
@sheetalkamat
Copy link
Member

sheetalkamat commented Aug 19, 2024

Can you please try out #59689 (comment) to see if it helps

@sheetalkamat
Copy link
Member

Unfortunately the access pattern of eslint itself is undefined; all it does is call the plugin to ask to parse a specific file. It doesn't provide the list up front or say when it's no longer using a file anymore... 😞

I am also open to skipping this as an option but when do you suppose eslint will do "cleanup" to remove the files. We would need to expose that part as well. But I am trying to see if fast exits help or not before we do this.

@JoshuaKGoldberg
Copy link
Contributor Author

Can you please try out #59689 (comment) to see if it helps

It does, fantastic! πŸ‘ Looks like most, but not all, of the performance discrepancy is gone:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ version  β”‚ files β”‚ project (even layout) β”‚ service (even layout) β”‚
┼──────────┼───────┼───────────────────────┼────────────────────────
β”‚ baseline β”‚ 1024  β”‚ '2.524 s Β±  0.016 s'  β”‚ '3.338 s Β±  0.013 s'  β”‚
┼──────────┼───────┼───────────────────────┼────────────────────────
β”‚ modified β”‚ 1024  β”‚ '2.531 s Β±  0.010 s'  β”‚ '2.846 s Β±  0.010 s'  β”‚
β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

when do you suppose eslint will do "cleanup" to remove the files.

I don't know. I've asked around internally. In the meantime, the best answer I can guess at is "eventually but not extremely soon".

@sheetalkamat
Copy link
Member

I don't know. I've asked around internally. In the meantime, the best answer I can guess at is "eventually but not extremely soon".

Till we have some idea on what to do here i dont think its advisable to let the collection not happen. In mean time we can get in that optimization PR

@JoshuaKGoldberg
Copy link
Contributor Author

Update: there's no plan to give this sort of information (whether ESLint is in single-run mode, when files are done being linted, etc.) to parsers. We should assume parsers in ESLint-land won't have access to that for a while.

Anyway, thanks for working on this! Really encouraging to see so much of the performance discrepancy go away. ⚑

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API Relates to the public API for TypeScript Domain: Performance Reports of unusually slow behavior Fix Available A PR has been opened for this issue Infrastructure Issue relates to TypeScript team infrastructure
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants