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

Improve memory efficiency of many OptimisticTransactionDBs #11439

Closed
wants to merge 4 commits into from

Conversation

pdillinger
Copy link
Contributor

Summary: Currently it's easy to use a ton of memory with many small OptimisticTransactionDB instances, because each one by default allocates a million mutexes (40 bytes each on my compiler) for validating transactions. It even puts a lot of pressure on the allocator by allocating each one individually!

In this change:

  • Create a new object and option that enables sharing these buckets of mutexes between instances. This is generally good for load balancing potential contention as various DBs become hotter or colder with txn writes. About the only cases where this sharing wouldn't make sense (e.g. each DB usually written by one thread) are cases that would be better off with OccValidationPolicy::kValidateSerial which doesn't use the buckets anyway.
  • Allocate the mutexes in a contiguous array, for efficiency
  • Add an option to ensure the mutexes are cache-aligned. In several other places we use cache-aligned mutexes but OptimisticTransactionDB historically does not. It should be a space-time trade-off the user can choose.
  • Provide some visibility into the memory used by the mutex buckets with an ApproximateMemoryUsage() function (also used in unit testing)
  • Share code with other users of "striped" mutexes, appropriate refactoring for customization & efficiency (e.g. using FastRange instead of modulus)

Test Plan: unit tests added. Ran sized-up versions of stress test in unit test, including a before-and-after performance test showing no consistent difference. (NOTE: OptimisticTransactionDB not currently covered by db_stress!)

Summary: Currently it's easy to use a ton of memory with many small
OptimisticTransactionDB instances, because each one by default allocates
(by default) a million mutexes (40 bytes each on my compiler) for
validating transactions. It even puts a lot of pressure on the allocator
by allocating each one individually!

In this change:
* Create a new object and option that enables sharing these buckets of
mutexes between instances. This is generally good for load balancing
potential contention as various DBs become hotter or colder with txn
writes. About the only cases where this sharing wouldn't make sense
(e.g. each DB usually written by one thread) are cases that would be
better off with OccValidationPolicy::kValidateSerial which doesn't use
the buckets anyway.
* Allocate the mutexes in a contiguous array, for efficiency
* Add an option to ensure the mutexes are cache-aligned. In several
other places we use cache-aligned mutexes but OptimisticTransactionDB
historically does not. It should be a space-time trade-off the user can
choose.
* Provide some visibility into the memory used by the mutex buckets with
an ApproximateMemoryUsage() function (also used in unit testing)
* Share code with other users of "striped" mutexes, appropriate
refactoring for customization & efficiency (e.g. using FastRange instead
of modulus)

Test Plan: unit tests added. Ran sized-up versions of stress test in unit
test, including a before-and-after performance test showing no
consistent difference. (NOTE: OptimisticTransactionDB not currently covered
by db_stress!)
@facebook-github-bot
Copy link
Contributor

@pdillinger has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

Comment on lines 49 to 50
// Most details in internal derived class
// Users should not derive from this class
Copy link
Contributor

Choose a reason for hiding this comment

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

We could consider enforcing this (instead of relying on users) using the pimpl idiom:

class OccLockBuckets {
public:
   ~OccLockBuckets(); // non-virtual, implemented in .cc
   size_t ApproximateMemoryUsage() const; // non-virtual, gets forwarded to impl_->ApproximateMemoryUsage(), implemented in .cc
private:
   // private ctor, implemented in .cc
   // friends as needed (e.g. factory method)
   class ImplBase;
   std::unique_ptr<ImplBase> impl_;
};

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 don't like the additional level of indirection, and wanted to avoid some boilerplate, but I'll include the version without indirection.

Comment on lines 127 to 134
for (auto v : lk_ptrs) {
v->Lock();
}
Defer unlocks([&]() {
for (auto v : lk_ptrs) {
v->Unlock();
}
});
Copy link
Contributor

Choose a reason for hiding this comment

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

I realize that (at least per the style guide) exceptions don't exist ;) but this is slightly less exception safe than the original. Building a vector of unique_locks as we iterate means that if an exception is thrown during a call to Lock, keys locked earlier get automatically unlocked; here, we only ensure that if every mutex was successfully locked, each will also get unlocked.

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'll note this in a comment.

template <bool cache_aligned>
class OccLockBucketsImpl : public OccLockBucketsImplBase {
public:
OccLockBucketsImpl(size_t bucket_count) : locks_(bucket_count) {}
Copy link
Contributor

Choose a reason for hiding this comment

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

Might want to mark this ctor explicit or the linter might complain

@facebook-github-bot
Copy link
Contributor

@pdillinger has updated the pull request. You must reimport the pull request before landing.

@facebook-github-bot
Copy link
Contributor

@pdillinger has updated the pull request. You must reimport the pull request before landing.

@facebook-github-bot
Copy link
Contributor

@pdillinger has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@facebook-github-bot
Copy link
Contributor

@pdillinger merged this pull request in 17bc277.

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.

3 participants