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

Devirtualize TokenMap #47274

Merged
7 commits merged into from
Sep 5, 2020
Merged

Devirtualize TokenMap #47274

7 commits merged into from
Sep 5, 2020

Conversation

benaadams
Copy link
Member

@benaadams benaadams commented Aug 31, 2020

Was showing up as a hot path in when building Rosyln

image

image

@benaadams benaadams requested a review from a team as a code owner August 31, 2020 04:40
@333fred 333fred added Area-Compilers Community The pull request was submitted by a contributor who is not a Microsoft employee. labels Aug 31, 2020
benaadams and others added 2 commits August 31, 2020 14:20
Co-authored-by: Youssef Victor <31348972+Youssef1313@users.noreply.github.com>
/// <summary>
/// Used to devirtualize Dictionary/HashSet for EqualityComparer{T}.Default
/// </summary>
internal struct IReferenceOrISignatureEquivalent : IEquatable<IReferenceOrISignatureEquivalent>
Copy link
Member

@333fred 333fred Aug 31, 2020

Choose a reason for hiding this comment

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

I'm not sure I understand why we would need both of these: it seems like just IReferenceOrISignatureEquivalent should be fine.

Copy link
Member Author

@benaadams benaadams Aug 31, 2020

Choose a reason for hiding this comment

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

IReferenceOrISignature uses ReferenceEquals for equals and IReferenceOrISignatureEquivalent has a more complex equals, previously provided by MetadataEntityReferenceComparer.cs

The dictionary, hashtable and concurrentdictionary will devirtualize (and potentially inline) the equals for a struct when the Comparer is unspecified (if it also implements IEquatable) in .NET Core; whereas specifying a Comparer will be a slower interface call.

Copy link
Member

Choose a reason for hiding this comment

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

This would be great info to have in a comment in the file itself :)


In reply to: 480276103 [](ancestors = 480276103)

items[(int)token] = item.AsObject();

// Update the updated array reference before updating _count
Volatile.Write(ref _items, items);
Copy link
Member

Choose a reason for hiding this comment

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

Why is this safe? It seems very likely to me that _items could have been changed by this point.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is the only location _items is updated and it is done in a lock. The Volatile.Write is to ensure its not reordered with the write to _count; so it's safe to Volatile.Read them in reverse order in GetAllItems() since _count only ever increases

object[] items = Volatile.Read(ref _items);

// Return a right sized copy of the array
return (new ReadOnlySpan<object>(items, 0, count)).ToArray();
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't seem particularly safe to me either.

Copy link
Member

Choose a reason for hiding this comment

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

Consider that AddItem does volatile writes in the following order: _items then _count. This method now reads them volatiley in the other order _count then _items. That means by the time we read _count here we know there are at least _count elements in the _items collection hence the array returned here will be well formed (no null values).

There is a subtle invariant change from the previous version of the code:

  • Before: If this method was entered when AddItem was executing then it would return the effect of completing the AddItem method
  • After: If this method was entered when AddItem was executing then it may or may not return the effect of completing the AddItem method.

Don't believe we care about maintaining that invariant.

@333fred
Copy link
Member

333fred commented Aug 31, 2020

Done review pass (commit 3). I'm very concerned by the threading in this PR: I think a full writeup of the strategy being taken here and the threading implications is necessary for review.

src/Compilers/Core/Portable/IReferenceOrISignature.cs Outdated Show resolved Hide resolved
src/Compilers/Core/Portable/IReferenceOrISignature.cs Outdated Show resolved Hide resolved
src/Compilers/Core/Portable/IReferenceOrISignature.cs Outdated Show resolved Hide resolved
src/Compilers/Core/Portable/IReferenceOrISignature.cs Outdated Show resolved Hide resolved
src/Compilers/Core/Portable/IReferenceOrISignature.cs Outdated Show resolved Hide resolved
object[] items = Volatile.Read(ref _items);

// Return a right sized copy of the array
return (new ReadOnlySpan<object>(items, 0, count)).ToArray();
Copy link
Member

Choose a reason for hiding this comment

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

Consider that AddItem does volatile writes in the following order: _items then _count. This method now reads them volatiley in the other order _count then _items. That means by the time we read _count here we know there are at least _count elements in the _items collection hence the array returned here will be well formed (no null values).

There is a subtle invariant change from the previous version of the code:

  • Before: If this method was entered when AddItem was executing then it would return the effect of completing the AddItem method
  • After: If this method was entered when AddItem was executing then it may or may not return the effect of completing the AddItem method.

Don't believe we care about maintaining that invariant.

benaadams and others added 2 commits August 31, 2020 19:22
Co-authored-by: Jared Parsons <jaredpparsons@gmail.com>
@@ -46,7 +47,7 @@ public override void Visit(IEventDefinition eventDefinition)

public override void Visit(IFieldReference fieldReference)
{
if (!_alreadySeen.Add(fieldReference))
if (!_alreadySeen.Add(new IReferenceOrISignatureEquivalent(fieldReference)))
Copy link
Member Author

Choose a reason for hiding this comment

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

Shame can't do implicit conversion from an interface 😉

@benaadams
Copy link
Member Author

REstarting CI

@benaadams benaadams closed this Sep 5, 2020
@benaadams benaadams reopened this Sep 5, 2020
@ghost
Copy link

ghost commented Sep 5, 2020

Hello @333fred!

Because this pull request has the auto-merge label, I will be glad to assist with helping to merge this pull request once all check-in policies pass.

p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (@msftbot) and give me an instruction to get started! Learn more here.

Copy link

@ghost ghost left a comment

Choose a reason for hiding this comment

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

Auto-approval

@333fred 333fred self-assigned this Sep 5, 2020
@ghost ghost merged commit b0827cf into dotnet:master Sep 5, 2020
@ghost ghost added this to the Next milestone Sep 5, 2020
@333fred
Copy link
Member

333fred commented Sep 5, 2020

Thanks @benaadams!

@benaadams benaadams deleted the Devirtualize-TokenMap branch September 5, 2020 09:13
@dibarbet dibarbet modified the milestones: Next, 16.8.P4 Sep 22, 2020
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers auto-merge Community The pull request was submitted by a contributor who is not a Microsoft employee.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants