summaryrefslogtreecommitdiff
path: root/src/mongo/util/aligned.h
diff options
context:
space:
mode:
authorAndrew Morrow <acm@mongodb.com>2022-04-19 14:37:22 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-05-06 21:39:57 +0000
commit5435f9585f857f6145beaf6d31daf336453ba86f (patch)
tree63419058d51c0525e3ac08b79ead25ad88fb9a65 /src/mongo/util/aligned.h
parenta59b750cbd1f77f7471c928b15d8017dc1eba677 (diff)
downloadmongo-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.h96
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