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

Optimize OOP compilation of HasSourceGenerators. #72978

Merged
merged 5 commits into from
Apr 11, 2024

Conversation

CyrusNajmabadi
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi commented Apr 11, 2024

We had two problems with the prior approach.

  1. the information was cached with ProjectStates. But htat meant if the project-state cahnged (which it does on any doc-change) we would recompute if the project had generators.
  2. when calling to OOP to have it determine if the project had generators, we would do a full-project sync (which included document contents). So, again, when a doc changed, this would increase the cost of computing this info.

The new approach is much more lightweight.

First, On the host side, we now cache the information along with the list-of-analyzer-references a project has. That list almost never changes when projects fork (unless hte user literally adds/removes references). So the information is reused virtually all the time on the host side.

Second, when we do make the call to oop, oop only needs to sync over the analyzer references, which it can do in in a much more lightweight fashion. Oop also caches the information on a per-analyzer-reference, so that different projects which reference the same analyzer references can call to oop and have the question answered the first time without a call back to the host at all.

--

https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_build/results?buildId=9398165&view=results
https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_build/results?buildId=9398506&view=results

@CyrusNajmabadi CyrusNajmabadi requested a review from a team as a code owner April 11, 2024 04:20
@dotnet-issue-labeler dotnet-issue-labeler bot added Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead labels Apr 11, 2024
@CyrusNajmabadi
Copy link
Member Author

Going to to a test insertion to see if this has any perf issues.

var result = await client.TryInvokeAsync<IRemoteSourceGenerationService, bool>(
solution,
projectId,
(service, solution, cancellationToken) => service.HasGeneratorsAsync(solution, projectId, cancellationToken),
(service, solution, cancellationToken) => service.HasGeneratorsAsync(solution, projectId, analyzerReferences, projectState.Language, cancellationToken),
Copy link
Member Author

Choose a reason for hiding this comment

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

new approach passes along the analyzer-reference checksums and language, as we can answer on the oop side with just that info.

private static readonly Dictionary<string, AnalyzerReferenceMap> s_languageToAnalyzerReferenceMap = new()
{
{ LanguageNames.CSharp, new() },
{ LanguageNames.VisualBasic, new() },
Copy link
Member Author

Choose a reason for hiding this comment

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

it's fine to have these be keyed just by VB/C#. OOP is only supported for those two languages alone. This makes it easy to initialize and look into this without locking, or complex types like ConcurrentDict.

{
return RunServiceAsync(solutionChecksum, async solution =>
{ LanguageNames.CSharp, (new(), static analyzerReference => HasSourceGenerators(analyzerReference, LanguageNames.CSharp)) },
{ LanguageNames.VisualBasic, (new(), static analyzerReference => HasSourceGenerators(analyzerReference, LanguageNames.VisualBasic)) },
Copy link
Member Author

Choose a reason for hiding this comment

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

same here. we can hardcode in C#/vb here to simplify thigns.

Copy link
Member Author

Choose a reason for hiding this comment

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

note: we cache thsi per analyzer reference so that multiple projects with the same analyzer references (common), and lang, will cache the same result.

string language,
CancellationToken cancellationToken)
{
if (analyzerReferenceChecksums.Length == 0)
Copy link
Member Author

Choose a reason for hiding this comment

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

technically not necessary. the host side already checked this. d

@CyrusNajmabadi
Copy link
Member Author

https://dev.azure.com/devdiv/DevDiv/_git/VS/pullrequest/543307 shows no regressions. (There is one regression, but it was caused by #72965 not this PR).

return false;

var analyzerReferenceMap = s_languageToAnalyzerReferenceMap[projectState.Language];
if (!analyzerReferenceMap.TryGetValue(projectState.AnalyzerReferences, out var lazy))
{
// Extracted into local function to prevent allocations in the case where we find a value in the cache.
Copy link
Contributor

Choose a reason for hiding this comment

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

// Extracted into local function to prevent allocations in the case where we find a value in the cache.

Could we just make static and get rid of this comment?

Copy link
Member Author

Choose a reason for hiding this comment

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

my guess is no. there are needed captures in these paths. but i will try.

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah, it's all sorts of captury. :) No go on static here. We need to capture enough data to be able to call to oop effectively to answer the question. note that once this happens once, and is cached, that captured data will be dropped. So we only need this alloc when looking at a fresh (non-identity) list of analyzer-references. Which should be very rare as the solution forks forwards.

@CyrusNajmabadi CyrusNajmabadi requested a review from ToddGrun April 11, 2024 18:10
Copy link
Contributor

@ToddGrun ToddGrun left a comment

Choose a reason for hiding this comment

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

:shipit:

@CyrusNajmabadi CyrusNajmabadi merged commit d601b62 into dotnet:main Apr 11, 2024
27 checks passed
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Apr 11, 2024
@CyrusNajmabadi CyrusNajmabadi deleted the lessTesxtSync branch April 11, 2024 19:39
@CyrusNajmabadi
Copy link
Member Author

@jasonmalinowski For review when you get back.

@dibarbet dibarbet modified the milestones: Next, 17.11 P1 Apr 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants