diff options
author | Andrew Morrow <acm@mongodb.com> | 2022-04-19 14:37:22 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-05-06 21:39:57 +0000 |
commit | 5435f9585f857f6145beaf6d31daf336453ba86f (patch) | |
tree | 63419058d51c0525e3ac08b79ead25ad88fb9a65 /src/mongo/util/aligned.h | |
parent | a59b750cbd1f77f7471c928b15d8017dc1eba677 (diff) | |
download | mongo-5435f9585f857f6145beaf6d31daf336453ba86f.tar.gz |
SERVER-65664 Provide more specific types for cache alignment needs
Diffstat (limited to 'src/mongo/util/aligned.h')
-rw-r--r-- | src/mongo/util/aligned.h | 96 |
1 files changed, 83 insertions, 13 deletions
diff --git a/src/mongo/util/aligned.h b/src/mongo/util/aligned.h index bdab7f8cac1..3521202834e 100644 --- a/src/mongo/util/aligned.h +++ b/src/mongo/util/aligned.h @@ -37,16 +37,32 @@ namespace mongo { +namespace aligned_detail { +template <size_t step> +constexpr size_t roundUpByStep(size_t value) { + return (((value + (step - 1)) / step) * step); +} +} // namespace aligned_detail + /** - * A wrapper holding a `T` value aligned to `alignof(T)` or `MinAlign`, - * whichever is greater (i.e. more strict). The value is accessed with a - * pointer-like syntax. + * A wrapper holding a `T` value aligned to `alignof(T)` or + * `minAlign`, whichever is greater (i.e. more strict). The value is + * accessed with a pointer-like syntax. Additionally the object will + * be placed in a buffer no smaller than minStorageSize, of which the + * contained T object may use no more than maxObjectSize bytes. */ -template <typename T, size_t MinAlign> +template <typename T, + size_t minAlign = alignof(T), + size_t minStorageSize = sizeof(T), + size_t maxObjectSize = minStorageSize> class Aligned { public: using value_type = T; - static constexpr size_t alignment = std::max(alignof(T), MinAlign); + + static_assert(sizeof(value_type) <= maxObjectSize); + + static constexpr size_t kAlignment = std::max(alignof(value_type), minAlign); + static constexpr size_t kStorageSize = std::max(sizeof(value_type), minStorageSize); template <typename... As> explicit Aligned(As&&... args) noexcept(std::is_nothrow_constructible_v<value_type, As&&...>) { @@ -99,7 +115,7 @@ public: } private: - static_assert((MinAlign & (MinAlign - 1)) == 0, "alignments must be a power of two"); + static_assert((minAlign & (minAlign - 1)) == 0, "alignments must be a power of two"); const value_type* _raw() const noexcept { return reinterpret_cast<const value_type*>(&_storage); @@ -117,21 +133,75 @@ private: return *stdx::launder(_raw()); } - std::aligned_storage_t<sizeof(value_type), alignment> _storage; + std::aligned_storage_t<kStorageSize, kAlignment> _storage; }; /** - * Swap the values. Should not necessarily require they agreen on alignment. - * defined out-of-class to work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89612 + * Swap the values. Defined out-of-class to work around + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89612 + * + * TODO: It should be possible to swap Aligned<T> vs Aligned<U> if T + * and U can be swapped, as well to swap instances with varying + * alignment, padding, etc. However, defining that generic swap + * results in ambiguities. */ -template <typename T, size_t MinAlign> -void swap(Aligned<T, MinAlign>& a, - Aligned<T, MinAlign>& b) noexcept(std::is_nothrow_swappable_v<T>) { +template <typename T, auto... Pack> +void swap(Aligned<T, Pack...>& a, Aligned<T, Pack...>& b) noexcept(std::is_nothrow_swappable_v<T>) { using std::swap; swap(*a, *b); } +/** + * A `CacheExclusive` object is Aligned to the destructive + * interference size, ensuring that it will start at an address that + * will not exhibit false sharing with any objects that precede it in + * memory. Additionally, the storage for the object is padded to a + * sufficient multiple of the destructive interference size to ensure + * that it will not exhibit false sharing with any other objects that + * follow it in memory. However, it is not assured that the embedded T + * object will internally exhibit true sharing with all of itself, as + * the contained object is permitted to be larger than the + * constructive interference size. + */ +template <typename T> +using CacheExclusive = + Aligned<T, + stdx::hardware_destructive_interference_size, + aligned_detail::roundUpByStep<stdx::hardware_destructive_interference_size>(sizeof(T))>; + + +/** + * A `CacheCombined` object is Aligned to the constructive + * interference size, ensuring that it will start at an address that + * can exhibit true sharing for some forward extent. Additionally, the + * size of the object is constrained to ensure that all of its state + * is eligible for true sharing within the same region of constructive + * interference. However, there is no guarantee that the object will + * not exhibit false sharing with objects that either precede or + * follow it in memory, unless those objects are themselves protected + * from false sharing. + */ +template <typename T> +using CacheCombined = Aligned<T, + stdx::hardware_constructive_interference_size, + sizeof(T), + stdx::hardware_constructive_interference_size>; + + +/** + * A `CacheCombinedExclusive` object is Aligned to the destructive + * interference size, ensuring that it will start at an address that + * will not exhibit false sharing with objects that precede it in + * memory. Additionally, the storage for the object is padded to the + * destructive interference size, ensuring that it will not exhibit + * false sharing with objects that follow it. Finally, the size of the + * object is constrained to the constructive interference size, + * ensuring that the object will internally exhibit true sharing. + */ template <typename T> -using CacheAligned = Aligned<T, stdx::hardware_destructive_interference_size>; +using CacheCombinedExclusive = Aligned<T, + stdx::hardware_destructive_interference_size, + stdx::hardware_destructive_interference_size, + stdx::hardware_constructive_interference_size>; } // namespace mongo |