diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ea13f8..425b228 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,7 @@ project(libcuckoo # put these in the cache so they show up in ccmake option (BUILD_EXAMPLES "build example libcuckoo programs") +option (LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT "Optimize the spinlock implementation with C++20 futex support" OFF) # Add the libcuckoo interface target add_subdirectory(libcuckoo) diff --git a/libcuckoo/CMakeLists.txt b/libcuckoo/CMakeLists.txt index 5ec8902..d767870 100644 --- a/libcuckoo/CMakeLists.txt +++ b/libcuckoo/CMakeLists.txt @@ -17,6 +17,10 @@ add_library(libcuckoo::libcuckoo ALIAS libcuckoo) # will have c++11 turned on in their compile when they use this target. # XXX: newer cmakes have a "cxx_std_11" feature that could be used target_compile_features (libcuckoo INTERFACE cxx_constexpr) +if (LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT) + target_compile_features(libcuckoo INTERFACE cxx_std_20) + target_compile_definitions(libcuckoo INTERFACE LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT=1) +endif () # Include relative to the base directory target_include_directories(libcuckoo INTERFACE diff --git a/libcuckoo/cuckoohash_map.hh b/libcuckoo/cuckoohash_map.hh index 3308068..56f4837 100644 --- a/libcuckoo/cuckoohash_map.hh +++ b/libcuckoo/cuckoohash_map.hh @@ -819,12 +819,29 @@ private: LIBCUCKOO_SQUELCH_PADDING_WARNING class LIBCUCKOO_ALIGNAS(64) spinlock { public: - spinlock() : elem_counter_(0), is_migrated_(true) { lock_.clear(); } + spinlock() noexcept + : +#if LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT + lock_(), +#endif + elem_counter_(0), is_migrated_(true) { + +#if !LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT + lock_.clear(); +#endif + } spinlock(const spinlock &other) noexcept - : elem_counter_(other.elem_counter()), + : +#if LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT + lock_(), +#endif + elem_counter_(other.elem_counter()), is_migrated_(other.is_migrated()) { + +#if !LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT lock_.clear(); +#endif } spinlock &operator=(const spinlock &other) noexcept { @@ -834,14 +851,31 @@ private: } void lock() noexcept { - while (lock_.test_and_set(std::memory_order_acq_rel)) - ; + // Optimistically assume the lock is free + while (lock_.test_and_set(std::memory_order_acq_rel)) { +#if LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT + // Wait for lock to be released utilizing C++20 futex support + lock_.wait(true, std::memory_order::relaxed); +#endif + } } - void unlock() noexcept { lock_.clear(std::memory_order_release); } + void unlock() noexcept { + lock_.clear(std::memory_order_release); +#if LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT + lock_.notify_one(); +#endif + } bool try_lock() noexcept { +#if LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT + // First do a relaxed load to check if lock is free in order to prevent + // unnecessary cache misses if someone does while(!try_lock()) + return !lock_.test(std::memory_order_relaxed) && + !lock_.test_and_set(std::memory_order_acq_rel); +#else return !lock_.test_and_set(std::memory_order_acq_rel); +#endif } counter_type &elem_counter() noexcept { return elem_counter_; }