Skip to content

Store and check deferred nodes by containing file #27378

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

Merged
merged 2 commits into from
Oct 5, 2018

Conversation

weswigham
Copy link
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).

const id = "" + getNodeId(node);
deferredNodes.set(id, node);
links.deferredNodes.set(id, node);
}
Copy link
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
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
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
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
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.

3 participants