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

Fix performance regression when reusing old state #28028

Merged

Conversation

ajafff
Copy link
Contributor

@ajafff ajafff commented Oct 21, 2018

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 was undefined. 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.

@@ -594,7 +594,7 @@ namespace ts {
let diagnosticsProducingTypeChecker: TypeChecker;
let noDiagnosticsTypeChecker: TypeChecker;
let classifiableNames: UnderscoreEscapedMap<true>;
let modifiedFilePaths: Path[] | undefined;
let unmodifiedSourceFilesWithAmbientModules: SourceFile[] | undefined;
Copy link
Contributor Author

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.

@ajafff
Copy link
Contributor Author

ajafff commented Oct 21, 2018

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.
This changes the file name in the trace from the first file to the last one containing that ambient module.

@thekiba
Copy link

thekiba commented Oct 22, 2018

How many time needed for merge the fix into master and release?

@obenjiro
Copy link

@RyanCavanaugh can someone look into this? This is really important performance fix.

@ajafff
Copy link
Contributor Author

ajafff commented Oct 22, 2018

@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.
Given the impact this regression has, it's highly likely that this PR is reviewed today by @sheetalkamat or @RyanCavanaugh

@@ -1213,14 +1201,20 @@ namespace ts {
return oldProgram.structureIsReused;
}

modifiedFilePaths = modifiedSourceFiles.map(f => f.newFile.path);
const modifiedFiles = modifiedSourceFiles.map(f => f.oldFile);
Copy link
Member

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

Copy link
Contributor Author

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) {
Copy link
Member

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.

Copy link
Contributor Author

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

@alfaproject
Copy link

alfaproject commented Oct 25, 2018

@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 sheetalkamat merged commit 539b9a6 into microsoft:master Oct 25, 2018
@filipesilva
Copy link
Contributor

@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.

@ajafff ajafff deleted the optimize-resolve-reusing-old-state branch November 1, 2018 12:39
@ajafff
Copy link
Contributor Author

ajafff commented Nov 1, 2018

I opened #28282 to port this change to v3.1

DanielRosenwasser added a commit that referenced this pull request Nov 1, 2018
 Fix performance regression when reusing old state (#28028)
@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Nov 1, 2018

Check out TypeScript 3.1.6 for the fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Building performance is slowdown
7 participants