From c51f8563a66af6fa65f5b486ee3b86f5249415b9 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 18 Sep 2018 22:37:06 -0400 Subject: ring_buffer: Use std::hardware_destructive_interference_size to determine alignment size for avoiding false sharing MSVC 19.11 (A.K.A. VS 15.3)'s C++ standard library implements P0154R1 (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html) which defines two new constants within the header, std::hardware_destructive_interference_size and std::hardware_constructive_interference_size. std::hardware_destructive_interference_size defines the minimum recommended offset between two concurrently-accessed objects to avoid performance degradation due to contention introduced by the implementation (with the lower-bound being at least alignof(max_align_t)). In other words, the minimum offset between objects necessary to avoid false-sharing. std::hardware_constructive_interference_size on the other hand defines the maximum recommended size of contiguous memory occupied by two objects accessed wth temporal locality by concurrent threads (also defined to be at least alignof(max_align_t)). In other words the maximum size to promote true-sharing. So we can simply use this facility to determine the ideal alignment size. Unfortunately, only MSVC supports this right now, so we need to enclose it within an ifdef for the time being. --- src/common/ring_buffer.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h index 45926c9ec..0fd5b86f0 100644 --- a/src/common/ring_buffer.h +++ b/src/common/ring_buffer.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include "common/common_types.h" @@ -102,8 +103,15 @@ public: private: // It is important to align the below variables for performance reasons: // Having them on the same cache-line would result in false-sharing between them. - alignas(128) std::atomic m_read_index{0}; - alignas(128) std::atomic m_write_index{0}; + // TODO: Remove this ifdef whenever clang and GCC support + // std::hardware_destructive_interference_size. +#if defined(_MSC_VER) && _MSC_VER >= 1911 + alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_read_index{0}; + alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0}; +#else + alignas(128) std::atomic_size_t m_read_index{0}; + alignas(128) std::atomic_size_t m_write_index{0}; +#endif std::array m_data; }; -- cgit v1.2.3 From ab6dfa4fa55266082171f9752abae00bea8db555 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 18 Sep 2018 23:06:24 -0400 Subject: ring_buffer: Use std::atomic_size_t in a static assert Avoids the need to repeat "std::" twice --- src/common/ring_buffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h index 0fd5b86f0..abe3b4dc2 100644 --- a/src/common/ring_buffer.h +++ b/src/common/ring_buffer.h @@ -30,7 +30,7 @@ class RingBuffer { static_assert(capacity < std::numeric_limits::max() / 2 / granularity); static_assert((capacity & (capacity - 1)) == 0, "capacity must be a power of two"); // Ensure lock-free. - static_assert(std::atomic::is_always_lock_free); + static_assert(std::atomic_size_t::is_always_lock_free); public: /// Pushes slots into the ring buffer -- cgit v1.2.3