Skip to content

[Runtime] Switch MetadataCache to ConcurrentReadableHashMap. #34307

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

Merged

Conversation

mikeash
Copy link
Contributor

@mikeash mikeash commented Oct 14, 2020

Use StableAddressConcurrentReadableHashMap for now. MetadataCacheEntry's methods for awaiting a particular state assume a stable address, where it will repeatedly examine this in a loop while waiting on a condition variable, so we give it a stable address to accommodate that. This code can be changed to perform the necessary table lookup each time through the loop instead, and thus allow us to use a plain ConcurrentReadableHashMap and avoid the extra indirection, and I'll take care of that in a followup.

rdar://problem/70220660

@mikeash
Copy link
Contributor Author

mikeash commented Oct 14, 2020

@swift-ci please benchmark

Copy link
Contributor

@rjmccall rjmccall left a comment

Choose a reason for hiding this comment

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

So the actual entry in the hashtable now is an EntryType*, right? Or is it indirected an extra step for some reason?

}

Key key{value.Data.NumElements, elements.data(), value.Data.Labels};
return hash_value(key);
Copy link
Contributor

Choose a reason for hiding this comment

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

Surely there's a way to do this without having to copy the elements into a local array. You could use a TransformRange to project out the Metadata* for hash_value().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I couldn't find a nice way to do it, but I didn't know we had TransformRange. That looks nice and simple, I'll do that.

Note that entry hashes are only used when resizing the table, so it's not a performance impact for lookups. But it's still definitely worth doing better.

@@ -106,17 +106,14 @@ struct ConcurrencyControl {
ConcurrencyControl() = default;
};

template <class EntryType, uint16_t Tag, bool ProvideDestructor = true>
template <class EntryType, uint16_t Tag>
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there just never a situation where we care about destructors for these?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I couldn't find any. ConcurrentMap doesn't support removal. It does support destroying the entire tree, but all uses of it just create it and let it live forever. I assume this was left over from a much earlier era when it was thought that these caches might not be permanent, or metadata might not be immortal.

Copy link
Contributor

Choose a reason for hiding this comment

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

Mostly I was reluctant to write a class that might be useless in other contexts.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, that'll do it too.

@mikeash
Copy link
Contributor Author

mikeash commented Oct 15, 2020

So the actual entry in the hashtable now is an EntryType*, right? Or is it indirected an extra step for some reason?

That's right. The wrapper forwards key matching operations, but it's ultimately just a pointer, so there's just one level of indirection. As I mention in the PR description, I'd like to even get rid of that, but it's a bigger job.

@rjmccall
Copy link
Contributor

So the actual entry in the hashtable now is an EntryType*, right? Or is it indirected an extra step for some reason?

That's right. The wrapper forwards key matching operations, but it's ultimately just a pointer, so there's just one level of indirection. As I mention in the PR description, I'd like to even get rid of that, but it's a bigger job.

I think the entries usually have the metadata inline, and the metadata address isn't allowed to change, so I'm not sure how you could do that.

@mikeash
Copy link
Contributor Author

mikeash commented Oct 15, 2020

The entries store a pointer:

  using ValueType = Metadata *;
...
  /// Valid if TrackingInfo.getState() >= PrivateMetadataState::Abstract.
  ValueType Value;

I think the entry can be created before the size is known, so it has to be separate.

@rjmccall
Copy link
Contributor

The entries store a pointer:

It might be an interior pointer. It is for TupleCacheEntry, at least, and I think it is for some of the others as well. And I'm not sure you can safely copy a MetadataCacheEntryBase — you'll need to do serious surgery, at least.

@mikeash
Copy link
Contributor Author

mikeash commented Oct 16, 2020

Ah, I didn't catch that it could be an interior pointer! I'm OK with major surgery to improve the cases that can benefit from it, but I will keep in mind that it won't be easy. I'll keep my eye out for better approaches, too.

In the meantime, I need to figure out these benchmark regressions before I land this one....

@mikeash
Copy link
Contributor Author

mikeash commented Oct 23, 2020

@swift-ci please benchmark

@mikeash
Copy link
Contributor Author

mikeash commented Oct 23, 2020

Playing with some perf improvements to the hashmap code. I'll make them a separate PR if they pan out.

@mikeash mikeash force-pushed the metadatacache-concurrentreadablehashmap branch from 343a15d to 9174785 Compare October 28, 2020 20:02
@swiftlang swiftlang deleted a comment from swift-ci Oct 28, 2020
@swiftlang swiftlang deleted a comment from swift-ci Oct 28, 2020
@mikeash mikeash requested a review from rjmccall October 28, 2020 21:09
@mikeash mikeash force-pushed the metadatacache-concurrentreadablehashmap branch from 9174785 to 1b59c52 Compare October 28, 2020 21:11
@mikeash
Copy link
Contributor Author

mikeash commented Oct 28, 2020

I changed that copying hash_value function to use TransformRange instead. I also got the performance up to par by adding a cache for the most recently looked up type and doing a quick check for a repeated lookup before doing a full lookup, like ConcurrentMap does.

@mikeash
Copy link
Contributor Author

mikeash commented Oct 28, 2020

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 1b59c5284f7f3ca947ddbdf3beb0e781747bcb3b

@mikeash
Copy link
Contributor Author

mikeash commented Oct 29, 2020

The new table spills out of the available space when targeting 32-bit. Working out how to shrink it further...

Use StableAddressConcurrentReadableHashMap for now. MetadataCacheEntry's methods for awaiting a particular state assume a stable address, where it will repeatedly examine `this` in a loop while waiting on a condition variable, so we give it a stable address to accommodate that. This code can be changed to perform the necessary table lookup each time through the loop instead, and thus allow us to use a plain ConcurrentReadableHashMap and avoid the extra indirection, and I'll take care of that in a followup.

rdar://problem/70220660
…ntry and check that before doing a more expensive lookup.
…e it to get LockingConcurrentMapStorage to fit into the available space on 32-bit.
@mikeash mikeash force-pushed the metadatacache-concurrentreadablehashmap branch from 0a08cae to cf53ad2 Compare October 30, 2020 16:36
@mikeash
Copy link
Contributor Author

mikeash commented Oct 30, 2020

Shrank it using some indirection. We really should adopt os_unfair_lock on Darwin as much as possible, though. It's nice and small and avoids this whole issue.

@mikeash
Copy link
Contributor Author

mikeash commented Oct 30, 2020

@swift-ci please test

1 similar comment
@mikeash
Copy link
Contributor Author

mikeash commented Oct 30, 2020

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - cf53ad2

@mikeash
Copy link
Contributor Author

mikeash commented Nov 2, 2020

@swift-ci please test os x platform

@mikeash
Copy link
Contributor Author

mikeash commented Nov 2, 2020

@rjmccall Any other changes or comments before I merge?

@mikeash
Copy link
Contributor Author

mikeash commented Nov 4, 2020

@swift-ci please test

@mikeash
Copy link
Contributor Author

mikeash commented Nov 4, 2020

Re-testing to make sure nothing broke since last week.

@swift-ci
Copy link
Contributor

swift-ci commented Nov 4, 2020

Build failed
Swift Test OS X Platform
Git Sha - cf53ad2

@mikeash
Copy link
Contributor Author

mikeash commented Nov 4, 2020

The OS X test failure is unrelated. Pushing the Big Red Button.

@mikeash mikeash merged commit 12d1147 into swiftlang:main Nov 4, 2020
ainu-bot added a commit to google/swift that referenced this pull request Nov 4, 2020
* 'main' of github.com:apple/swift:
  [Runtime] Switch MetadataCache to ConcurrentReadableHashMap. (swiftlang#34307)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants