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

Avoid unnecessary project enumeration in SolutionState.Branch #53810

Closed

Conversation

mavasani
Copy link
Contributor

@mavasani mavasani commented Jun 1, 2021

Fixes #53769

@mavasani mavasani added Area-IDE Tenet-Performance Regression in measured performance of the product from goals. labels Jun 1, 2021
@mavasani mavasani added this to the 16.11 milestone Jun 1, 2021
@mavasani mavasani requested review from jasonmalinowski and a team June 1, 2021 15:07
@mavasani
Copy link
Contributor Author

mavasani commented Jun 1, 2021

@vatsalyaagrawal @jinujoseph This is for 16.11

analyzerReferences ??= AnalyzerReferences;
projectIdToTrackerMap ??= _projectIdToTrackerMap;
filePathToDocumentIdsMap ??= _filePathToDocumentIdsMap;
dependencyGraph ??= _dependencyGraph;
var newFrozenSourceGeneratedDocumentState = frozenSourceGeneratedDocument.HasValue ? frozenSourceGeneratedDocument.Value : _frozenSourceGeneratedDocumentState;

// PERF: Only invoke WithLanguages if we have different set of project IDs (AddProject/RemoveProject operation)
Copy link
Member

Choose a reason for hiding this comment

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

seems sane. but i worry if it's possible to somehow have an Add+REmove processed at teh same time that woudl lead us to have the same count. @jasonmalinowski for validation that we do things in tiny steps, so this sort of optimization is ok.

Copy link
Contributor Author

@mavasani mavasani Jun 1, 2021

Choose a reason for hiding this comment

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

I audited the existing callsites to validate that they satisfy the first assertion. Definitely need @jasonmalinowski's confirmation on this one though.

Copy link
Member

Choose a reason for hiding this comment

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

So for some reason we also have a ProjectIds collection we pass around, and that's immutable. I'm guessing you're safe to do the WithLanguages any time the ProjectId collection object reference changes. The check is won't have false negatives where we fail to rerun WithLanguages; it could theoretically have a false positive where might run it if we manipulate the list and end up with the same original semantic value but I don't think we do that anywhere. (Or if we do, it's fine enough to not care since we'll still get quite the win.)

Copy link
Contributor Author

@mavasani mavasani Jun 2, 2021

Choose a reason for hiding this comment

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

Ah, that seems to be a much better approach. Let me adjust.

Copy link
Member

@CyrusNajmabadi CyrusNajmabadi left a comment

Choose a reason for hiding this comment

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

This LGTM as an optimization in theory. But i just want signoff from @jasonmalinowski that it's totally safe with how the WS forks.

Comment on lines 230 to 233
Debug.Assert(idToProjectStateMap.Count != _projectIdToProjectStateMap.Count ||
idToProjectStateMap.Keys.SetEquals(_projectIdToProjectStateMap.Keys));
Debug.Assert(idToProjectStateMap.Count != _projectIdToProjectStateMap.Count ||
Options == Options.WithLanguages(GetRemoteSupportedProjectLanguages(idToProjectStateMap)));
Copy link
Member

Choose a reason for hiding this comment

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

Can we put these invariants in the CheckInvariants method? That way it'll get ran through places other than Branch, but actually at the constructor level.

…assert. We were not accounting for a new language.

Additionally, also guard the added assert to only execute when input optionset for Branch method is null.
@mavasani
Copy link
Contributor Author

mavasani commented Jun 3, 2021

Alright, we have lot of test failures with this change. It seems it is not that trivial to add the optimization to skip Options.WithLanguages call because of the way options can be set on a specific solution snapshot without actually adding a project of that language.

For example, if we start with a workspace with an empty solution, and then do workspace.CurrentSolution.Options.WithChangedOption(..., C#);, it gives a solution snapshot with a SerializableOptionSet whose _languages has count 1 with C# as the entry, but project IDs for the solution snapshot is empty. Now, if we invoke a Branch on this solution snapshot, the project IDs are same, but Options == Options.WithLanguages(GetRemoteSupportedProjectLanguages(idToProjectStateMap))) does not hold as it creates a new option set due to differing language count. Same is true even for my prior implementation.

Going to ponder a bit more on this, marking the PR as draft meanwhile.

@mavasani mavasani marked this pull request as draft June 3, 2021 11:21
@davkean
Copy link
Member

davkean commented Jun 17, 2021

@mavasani In #54162, I found half of GetRemoteSupportedProjectLanguages occurred in the "Add Project" case, from reading the code it doesn't appear to address this. Any algorithm that results in your walking every single project in the solution is going to result in badness opening huge solutions.

@sharwell
Copy link
Member

Superseded by #54196

@sharwell sharwell closed this Jun 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-IDE Needs Shiproom Approval Tenet-Performance Regression in measured performance of the product from goals.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants