-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Make export-module and reference maps invertible #44402
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
Conversation
The code's a bit rough, but I thought I should post something before our discussion. I'll add perf numbers shortly. |
For a small fraction of the original repro Before:
(The anonymous functions are the lambdas in After
|
To validate, I traced the path argument to every |
Force push is a rebase. |
@typescript-bot perf test this |
Heya @amcasey, I've started to run the perf test suite on this PR at eef8705. You can monitor the build here. Update: The results are in! |
I'm pretty sure we have no perf suite coverage of the builder, but why not? |
@@ -546,70 +549,59 @@ namespace ts { | |||
} | |||
|
|||
Debug.assert(!!state.currentAffectedFilesExportedModulesMap); | |||
|
|||
const seenFileAndExportsOfFile = new Set<string>(); |
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.
Separate PR: @sheetalkamat can we lift this out a couple levels so we don't revisit the same paths repeatedly for different "affected" 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.
Or maybe it belongs on the state?
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.
To share it across more than one affected file, it would have to be on the state. I'm going to try updating it in parallel with seenAffectedFiles
.
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.
Assuming I've done it correctly, it saves about 10% of total time. Nothing to complain about, but much less than this change.
forEachEntry(state.exportedModulesMap, (exportedModules, exportedFromPath) => | ||
!state.currentAffectedFilesExportedModulesMap!.has(exportedFromPath) && // If we already iterated this through cache, ignore it | ||
exportedModules.has(affectedFile.resolvedPath) && | ||
state.exportedModulesMap.getKeys(affectedFile.resolvedPath)?.forEach(exportedFromPath => |
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 couldn't find a place where exportedModulesMap
was consumed "forward", but it's maintained forward and I couldn't see a way to reverse the meaning without making maintenance (specifically, deleting paths) really expensive.
@amcasey Here they are:Comparison Report - master..44402
System
Hosts
Scenarios
Developer Information: |
I claim the material-ui check time regression is from another change because the builder (which I don't believe runs in that perf test) doesn't count towards check time. |
@amcasey, if you want a project that has egregious issues with builder performance, we have one that repros issues fairly regularly (I've seen >30 minutes spent in the stage of determining what files are affected before). I expect it is largely due to the ~2500 files that are in a circular dependency snarl. |
@typescript-bot pack this |
@dmichon-msft Cool! Do you mind giving it a spin? You'll probably want to set |
Right now, we're enumerating all the entries to find out which keys map to a corresponding value. By maintaining a two-way map, we can convert this linear search into a map lookup and skip allocation of many, many iterator results.
Force push is a rebase. |
I've re-verified that a trace of the traversals matches the pre-change version. |
@@ -311,7 +313,7 @@ namespace ts { | |||
newState.affectedFilesIndex = state.affectedFilesIndex; | |||
newState.currentChangedFilePath = state.currentChangedFilePath; | |||
newState.currentAffectedFilesSignatures = state.currentAffectedFilesSignatures && new Map(state.currentAffectedFilesSignatures); | |||
newState.currentAffectedFilesExportedModulesMap = state.currentAffectedFilesExportedModulesMap && new Map(state.currentAffectedFilesExportedModulesMap); |
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.
@andrewbranch I retained the existing behavior but, now that I look at it, it seems wrong. The value type of this map is a set - doesn't that mean that the new and old maps will refer to (and modify) the same sets? It seems like the intention was probably to do a deep copy.
Right now, we're enumerating all the entries to find out which keys map
to a corresponding value. By maintaining a two-way map, we can convert
this linear search into a map lookup and skip allocation of many, many
iterator results.