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

Modify MergedNamespaceDeclaration.MakeChildren to to allocate less #74698

Merged
merged 2 commits into from
Aug 13, 2024

Conversation

ToddGrun
Copy link
Contributor

@ToddGrun ToddGrun commented Aug 9, 2024

This method is showing up in a profile via the ArrayBuilder.ToDictionary calls.

  1. Two dictionaries are created each call. The second Dictionary is only used for enumeration, and thus we can avoid it.
  2. The ToDictionary implementation cannot use a PooledDictionary, whereas our invocation for types can.
  3. The ToDictionary creates ArrayBuilders for each unique key. This is bad if there are a large number of unique keys (such as for our invocation for types). By creating such a large number of ArrayBuilders, we lose the benefit of the pooling, as the vast majority of the time we won't be able to return or get builders from the pool.

I separated the processing of the namespaces and types collections into their own methods, as they have slightly different characteristics. Specifically:

  1. The namespaces collection has few unique keys, but potentially large numbers of items in their value collections.
  2. The types collection has many unique keys, and usually small numbers of items in their value collections.

Handling namespaces is pretty straight forward using a Dictionary<string, ArrayBuilder>. However, handling types required a bit less convention to get around the issue of creating a large number of ArrayBuilder instances. Instead, we use an object as the value, with it either being an ArrayBuilder when an identity has been seen multiple times, or just a SingleTypeDeclaration when the identity has only been seen once.

MergedNamespaceDeclaration.MakeChildren is showing up as about 1.5% of allocations in the typing section of the speedometer scrolling perf test profile. These changes should shave that down to about 0.5% or so.

*** Relevant allocations from the typing section of the scrolling speedometer profile ***
image

This method is showing up in a profile via the ArrayBuilder.ToDictionary calls.

1) Two dictionaries are created each call. The second Dictionary is only used for enumeration, and thus we can avoid it.
2) The ToDictionary implementation cannot use a PooledDictionary, whereas our invocation for types can.
3) The ToDictionary creates ArrayBuilders for each unique key. This is bad if there are a large number of unique keys (such as for our invocation for types). By creating such a large number of ArrayBuilders, we lose the benefit of the pooling, as the vast majority of the time we won't be able to return or get builders from the pool.

I separated the processing of the namespaces and types collections into their own methods, as they have slightly different characteristics. Specifically:

1) The namespaces collection has few unique keys, but potentially large numbers of items in their value collections.
2) The types collection has many unique keys, and usually small numbers of items in their value collections.

Handling namespaces is pretty straight forward using a  Dictionary<string, ArrayBuilder<SingleNamespaceDeclaration>>. However, handling types required a bit less convention to get around the issue of creating a large number of ArrayBuilder instances. Instead, we use an object as the value, with it either being an ArrayBuilder<SingleTypeDeclaration> when an identity has been seen multiple times, or just a SingleTypeDeclaration when the identity has only been seen once.

MergedNamespaceDeclaration.MakeChildren is showing up as about 1.5% of allocations in the typing section of the speedometer scrolling perf test profile. These changes should shave that down to about 0.5% or so.
@ToddGrun ToddGrun requested a review from a team as a code owner August 9, 2024 16:51
@dotnet-issue-labeler dotnet-issue-labeler bot added Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead labels Aug 9, 2024
@ToddGrun
Copy link
Contributor Author

ToddGrun commented Aug 9, 2024

Triggered an insertion PR to validate the perf benefits. Will update once it completes:

Insertion PR: https://dev.azure.com/devdiv/DevDiv/_git/VS/pullrequest/570768
PIT Results: https://devdiv.visualstudio.com/DevDiv/_apps/hub/ms-vseng.pit-vsengPerf.pit-hub?targetBuild=35209.131.dn-bot.240810.032021.570768&targetBranch=main&targetPerfBuildId=10025580&runGroup=Speedometer&baselineBuild=35209.131&baselineBranch=main

MakeChildren isn't showing up in the profile against the run with these changes.

@ToddGrun
Copy link
Contributor Author

@dotnet/roslyn-compiler for 2nd review, speedometer results were agreeable

@ToddGrun
Copy link
Contributor Author

@dotnet/roslyn-compiler for 2nd review. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers 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.

6 participants