-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Massive performance penalty when shouldProvideParserServices
is true
#243
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
Comments
I wonder if it would make sense to move rules that depend on types to a typescript plugin (example: https://github.com/Microsoft/typescript-styled-plugin). Though that wouldn't be an eslint plugin anymore, and we might come full circle with tslint (https://github.com/Microsoft/typescript-tslint-plugin). |
Related: #134 |
I also noticed in a one of my projects that running |
Full scan of my project takes more than 5 minutes with parserOptions.project specified. This is so long |
cc @typescript-eslint/core-team Is it possible for us to run the parser in "isolatedModules" mode? |
There's been some discussion elsewhere about how plugins could gather some information in advance, before any linting begins, which would allow them to get some information that could then be shared among plugin rules. For example, eslint-plugin-import has a use case where they went to parse all files being linted to find imported files and construct a dependency graph. From what I've seen here, constructing a Program from all files being linted could be a similar use case. Along those lines, I would recommend creating an RFC in the ESLint RFC repository. It would be a great way to get some opinions from the ESLint core team, as well as allow other stakeholders such as eslint-plugin-import a chance to contribute their ideas and hopefully land on a sensible design that works for all. I'll keep an eye out on the ESLint side for when that RFC is created. 😄 |
That makes sense, and the import plugin case sounds quite similar! I'll try to take a look at opening the RFC sometime over the next week or two. |
What I've also noticed is that after the first TS file is scanned the following ones (even if they are JS) slow down when linting. My solution was to lint them separately
|
I believe eslint-plugin-import cheats and caches information between rules and files in module scope, in the import-map file. We could do the same and cache a compiled program. https://github.com/benmosher/eslint-plugin-import/blob/master/src/ExportMap.js#L18 |
Ran some tests against master.
There is a definite slowdown present (1834 ms), however I went digging using the node profiler ( The raw chrome profile data from the runs (you should be able to load the files into the chrome developer tools): typescript-eslint-profiles.zip We use the typescript compiler API Unfortunately, the time cost associated with constructing the program (even once) is going to be proportional to the size of your workspace. At this point in time, I don't know what we can do about this cost. I don't think there's a way for us to circumvent this one off cost. We more or less need every single file in the project parsed by typescript so that we can guarantee for the rules that all imported variables are properly typed. If anyone has any suggestions for this, I'd love to hear it. For users that use the CLI, there's nothing at all we can do about the cost - once the lint finishes, node exits, and with it the cache is gone. This means you have to pay it once per cli run. For watch mode (/ IDE plugins) - I haven't profiled it, but I'd assume that these modes are run more like a persistent server, meaning they should be able to take full advantage of the caching mechanism. |
I think TypeScript 3.4 |
The 3.4 RC is out. |
For anybody following along for the perf piece, please try the latest release - |
Still very slow |
Hey @Djaler What's your codebase look like? Your comment "still very slow" contains absolutely nothing useful for us to action or investigate, and only serves as notification spam for all the participants and watchers of this issue. In future, please give us some information, or at least something more constructive than 3 words. |
I am also seeing slowness in our repo compared to tslint, even without specifying a project.
How many files are there? Are you linting a multi tsconfig repo? What rules are you using? What versions of the plugin/parser/TypeScript/node are you using? Are you running eslint in watch mode? Via your ide? Via CLI on your entire repo? |
What code were you trying to parse?
I first noticed this against my company's repo (which is private), but I've replicated a similar performance penalty on this repo itself.
What happened?
When run normally, I get a pretty fast lint on my machine (2018 Macbook Pro 15-inch, i7/16GB), about 4.5s:
However, if I add
"project": "./tsconfig.json"
to the parser options, I notice a 10x performance decrease (which is roughly consistent with what we noticed on our own repo).Why does this happen?
I've dug into this a little bit, and it seems to come entirely from the time taken to generate the AST and
ts.Program
for each file; inparser.ts
whenshouldProvideParserServices
istrue
(which is only whenproject
is set),getProgramAndAST
takes ~300ms/file, compared to ~1.5ms/file when it's false.Within
getProgramAndAST
, the source of the slowness is almost entirely from calls tots.createProgram
. I wrappedcreateProgramAndAST
as well as the call tocreateProgram
withconsole.time
/console.timeEnd
and got the following (this is a small but representative sample):How could this be fixed?
In comparison, tslint (which lints our internal codebase about 8x faster than typescript-eslint with
project
set) offers an idea for what performance here could look like, only callscreateProgram
once per lint run (https://github.com/palantir/tslint/blob/master/src/runner.ts#L204), and gives it the names of all the files that will be linted (https://github.com/palantir/tslint/blob/master/src/linter.ts#L97). This isn't possible for typescript-estree as it stands, as eslint only provides a single filename to a call to the parser.Some possible approaches for a solution:
ts.createProgram
fasterts.Program
and reuse itI'm happy to undertake one of these approaches (my thinking is that the second is more promising) and file a PR myself, but I wanted to check in with you, the package maintainers, and get feedback on this before investing a lot more time into it.
Versions
@typescript-eslint/typescript-estree
master
TypeScript
3.3.1
node
8.12.0
yarn
1.13.0
The text was updated successfully, but these errors were encountered: