Skip to content

Store and check deferred nodes by containing file#27378

Merged
weswigham merged 2 commits into
microsoft:masterfrom
weswigham:store-deferred-nodes-by-file
Oct 5, 2018
Merged

Store and check deferred nodes by containing file#27378
weswigham merged 2 commits into
microsoft:masterfrom
weswigham:store-deferred-nodes-by-file

Conversation

@weswigham
Copy link
Copy Markdown
Member

Fixes #26680

During checking, we collect deferred nodes which we do not want to perform analysis on until later. These nodes may not actually be in the file being checked (the only time we care about deferred nodes), since a type in one file can pull on an expression in another. In an extreme case, a single deferred node in one file can pull on literally every other type in the program in a deferred fashion - causing us to check all of those nodes when checking the original file. Add in a bit of mutual recursion, and you have a situation where we end up rechecking the same deferred nodes at the end of every file (or even just a subset, which is still quite bad), because every file "depends" on them. To avoid these redundant checks, I now store all deferrals onto a list unique to the source file the node is contained within, provided that file hasn't been checked yet. (If it has, the node should have already been checked in a deferred way and there's nothing to do.)

As this is a perf issue, I don't really have a regression test for this (testing perf issues that aren't an OOM are not something we're equipped for), but here are some charts:

Before:
image

After:
image

This is to say, this reduced the error update for the active file in the editor for this repro down from almost two seconds (sluggish, effectively a whole program check just to get updated errors for one file) to around a quarter second (where message serialization time takes a bit of overhead).

Comment thread src/compiler/checker.ts
const id = "" + getNodeId(node);
deferredNodes.set(id, node);
links.deferredNodes.set(id, node);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we assert in the else case that deferred node already exists?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. We are perfectly fine adding the node multiple times (and in fact we will in many cases), we're still only visiting it once for errors in the end. That's why we swapped to a map in the first place, instead of an array.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant when file was typechecked? The assert would be to verify the deferredNode exists in the map.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seeings as previously if deferredNodes didn't exist, we'd quietly do nothing, I don't think an assert is quite correct. And experimentally, it looks like such an assert is only triggered during a full typecheck walk after a normal check (like in tests) under scenarios with erroneous syntax, such as a top-level return statement.

@weswigham weswigham merged commit 4ad6541 into microsoft:master Oct 5, 2018
@weswigham weswigham deleted the store-deferred-nodes-by-file branch October 5, 2018 21:40
@microsoft microsoft locked as resolved and limited conversation to collaborators Oct 21, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants