Skip to content

Reduce ImmutableDictionary.Comparers allocations#5744

Merged
rainersigwald merged 5 commits intodotnet:masterfrom
rainersigwald:immutable-shared-comparer
Sep 23, 2020
Merged

Reduce ImmutableDictionary.Comparers allocations#5744
rainersigwald merged 5 commits intodotnet:masterfrom
rainersigwald:immutable-shared-comparer

Conversation

@rainersigwald
Copy link
Member

Fixes #5736 by keeping a prototype ImmutableDictionary-with-comparer
around using the MSBuildNameIgnoreCaseComparer, which is the primary
comparer used for all these dictionaries in MSBuild.

Second commit adds OrdinalIgnoreCase too--that is all that I found in our codebase.

@lifengl, is there an easy way to replicate your results so I can confirm that this hits everything?

Fixes dotnet#5736 by keeping a prototype `ImmutableDictionary`-with-comparer
around using the `MSBuildNameIgnoreCaseComparer`, which is the primary
comparer used for all these dictionaries in MSBuild.
@lifengl
Copy link
Contributor

lifengl commented Sep 18, 2020 via email

#if NET35
return ImmutableDictionary.Create<K, V>(keyComparer);
#else
return typeof(K) == typeof(string) && keyComparer is MSBuildNameIgnoreCaseComparer
Copy link
Member

Choose a reason for hiding this comment

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

nits:

  • Make MSBuildNameIgnoreCaseComparer sealed to future-proof the is-a check?
  • typeof(K) == typeof(string) looks redundant, there's no way K could be something else if keyComparer is MSBuildNameIgnoreCaseComparer

Copy link
Member Author

Choose a reason for hiding this comment

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

I tried and failed to convince the type system that that typeof was unnecessary. Ideally we'd run this logic only when K is string but I'm not aware of a way to do that.

Copy link
Contributor

Choose a reason for hiding this comment

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

I want to be able to say string.Empty is K, but that would match with object, IComparable, etc...

Copy link
Member Author

Choose a reason for hiding this comment

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

Sealing MSBuildNameIgnoreCaseComparer breaks the build--I guess because the compiler currently doesn't know that passing a MSBuildNameIgnoreCaseComparer implies that typeof(K) == typeof(string)? When sealed, I get

error CS0030: Cannot convert type 'Microsoft.Build.Collections.MSBuildNameIgnoreCaseComparer' to 'System.Collections.Generic.IEqualityComparer<K>'

What I'd really like is a specialized implementation of this for K==string, but there's no way to do so that I'm aware of.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, though there may be another option. Looks like the only places we use CopyOnWriteDictionary with K != string are in tests . . . maybe just drop the generic?

@rainersigwald
Copy link
Member Author

Checked with privates:

-7bee5ad8   115953      1855248 System.Collections.Immutable.ImmutableDictionary`2+Comparers[[System.String, mscorlib],[Microsoft.Build.Execution.ProjectMetadataInstance, Microsoft.Build]]
+138f0578        3           48 System.Collections.Immutable.ImmutableDictionary`2+Comparers[[System.String, mscorlib],[Microsoft.Build.Execution.ProjectMetadataInstance, Microsoft.Build]]

Thanks for the catch @lifengl!

@rainersigwald rainersigwald merged commit 2501616 into dotnet:master Sep 23, 2020
@rainersigwald rainersigwald deleted the immutable-shared-comparer branch September 23, 2020 15:44
Forgind pushed a commit to Forgind/msbuild that referenced this pull request Sep 25, 2020
Fixes dotnet#5736 by keeping a prototype `ImmutableDictionary`-with-comparer
around using the `MSBuildNameIgnoreCaseComparer`, which is the primary
comparer used for all these dictionaries in MSBuild. `OrdinalIgnoreCase` is
also used and gets a special case too.

To make the type system happier, it was easiest to drop the `K` generic
parameter to the type and hard-code it to `string`--all internal non-test
uses were already doing that.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allocating exactly same ImmutableDictionary.Comparer in ProjectMetadataInstance dictionary

4 participants