-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Fix performance regression when reusing old state #28028
Fix performance regression when reusing old state #28028
Conversation
src/compiler/program.ts
Outdated
@@ -594,7 +594,7 @@ namespace ts { | |||
let diagnosticsProducingTypeChecker: TypeChecker; | |||
let noDiagnosticsTypeChecker: TypeChecker; | |||
let classifiableNames: UnderscoreEscapedMap<true>; | |||
let modifiedFilePaths: Path[] | undefined; | |||
let unmodifiedSourceFilesWithAmbientModules: SourceFile[] | undefined; |
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.
This could also be converted to a Map to further increase lookup performance.
The second commit of this PR uses a map instead of an array. This might not bring much additional performance improvement, but makes the lookup easier. |
How many time needed for merge the fix into master and release? |
@RyanCavanaugh can someone look into this? This is really important performance fix. |
@thekiba once this PR lands in master it needs to be backported to release 3.1 before it can be released in a patch release. |
@@ -1213,14 +1201,20 @@ namespace ts { | |||
return oldProgram.structureIsReused; | |||
} | |||
|
|||
modifiedFilePaths = modifiedSourceFiles.map(f => f.newFile.path); | |||
const modifiedFiles = modifiedSourceFiles.map(f => f.oldFile); |
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 you can get away with creating duplicate array by using every function on modifiedSourceFiles to find if oldFile isn't in modified files
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 don't know if I correctly understand what your comment means:
If you mean I should prefer !modifiedSourceFiles.some((f) => f.oldFile === oldFile)
in the loop below, I don't think it's worth changing. That would need to allocate a closure for every lookup and basically does the mapping inplace just to avoid allocating a new (probably very small) array.
@@ -1213,14 +1201,20 @@ namespace ts { | |||
return oldProgram.structureIsReused; | |||
} | |||
|
|||
modifiedFilePaths = modifiedSourceFiles.map(f => f.newFile.path); | |||
const modifiedFiles = modifiedSourceFiles.map(f => f.oldFile); | |||
for (const oldFile of oldSourceFiles) { |
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 this needs to be a function that either gets the value that's cached or calculate this so that we avoid doing this if there are no modules in new files etc.
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 don't think this loop is critical for performance as it's only executed once and it's very likely that this information is needed later anyway.
In addition modifiedSourceFiles
is only available in the scope of this function. I would need to move it to an outer scope to access it later. I'd like to avoid that as it increases the likelihood to retain references to old sourcefiles that could otherwise be garbage collected
@RyanCavanaugh and/or @sheetalkamat this issue is affecting more and more people as TS 3.1 gets widely used now since Angular 7 was released. I had to stall the upgrade to v7 because the regression is quite noticeable in our dev/CI environments. @ajafff answered @sheetalkamat so is there further feedback or changes needed? Can we give some love to this issue? \: |
@sheetalkamat could these changes be part of TS 3.1.6? This problem is affecting a lot of Angular users right now. Since Angular need to test compatibility with each TS minor release individually, we cannot change our peer dependency from ~3.1 to >3.2 without a lot of testing. Right now all users of Angular 7 are being affected by this performance regression. |
I opened #28282 to port this change to v3.1 |
Fix performance regression when reusing old state (#28028)
Check out TypeScript 3.1.6 for the fix. |
Fixes: #28025
This (hopefully) fixes the performance regression reported in #28025.
moduleNameResolvesToAmbientModuleInNonModifiedFile
may be executed for each import (or type reference directive) in every file of the program.Previously it would iterate through all files of the program where most of them are likely to have no ambient module names.
The new implementation stores a file of unmodified files that contain ambient module names. This list is typically a lot shorter than the list of all files.
Note that I removed some unnecessary parameters because their contents are already available in an enclosing scope.
Also note that the old implementation used
modifiedFilePaths
even if it wasundefined
. In that case it assumed all files to be unchanged. But in fact every file could have been changed because reusing the old program aborted before populating that variable.