Skip to content

Commit 6175e91

Browse files
Add option to use futex APIs for spinlock
During the discussion of [1] it has been suggested that the congestion could be reduced by relying on C++20 futex APIs. Additionally we incorporated the `try_lock()` design from [2]. [1]: #146 [2]: https://rigtorp.se/spinlock/ Co-authored-by: Milian Wolff <[email protected]>
1 parent 735a74b commit 6175e91

File tree

3 files changed

+44
-5
lines changed

3 files changed

+44
-5
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ project(libcuckoo
55

66
# put these in the cache so they show up in ccmake
77
option (BUILD_EXAMPLES "build example libcuckoo programs")
8+
option (LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT "Optimize the spinlock implementation with C++20 futex support" OFF)
89

910
# Add the libcuckoo interface target
1011
add_subdirectory(libcuckoo)

libcuckoo/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ add_library(libcuckoo::libcuckoo ALIAS libcuckoo)
1717
# will have c++11 turned on in their compile when they use this target.
1818
# XXX: newer cmakes have a "cxx_std_11" feature that could be used
1919
target_compile_features (libcuckoo INTERFACE cxx_constexpr)
20+
if (LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT)
21+
target_compile_features(libcuckoo INTERFACE cxx_std_20)
22+
target_compile_definitions(libcuckoo INTERFACE LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT=1)
23+
endif ()
2024

2125
# Include relative to the base directory
2226
target_include_directories(libcuckoo INTERFACE

libcuckoo/cuckoohash_map.hh

+39-5
Original file line numberDiff line numberDiff line change
@@ -819,12 +819,29 @@ private:
819819
LIBCUCKOO_SQUELCH_PADDING_WARNING
820820
class LIBCUCKOO_ALIGNAS(64) spinlock {
821821
public:
822-
spinlock() : elem_counter_(0), is_migrated_(true) { lock_.clear(); }
822+
spinlock() noexcept
823+
:
824+
#if LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT
825+
lock_(),
826+
#endif
827+
elem_counter_(0), is_migrated_(true) {
828+
829+
#if !LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT
830+
lock_.clear();
831+
#endif
832+
}
823833

824834
spinlock(const spinlock &other) noexcept
825-
: elem_counter_(other.elem_counter()),
835+
:
836+
#if LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT
837+
lock_(),
838+
#endif
839+
elem_counter_(other.elem_counter()),
826840
is_migrated_(other.is_migrated()) {
841+
842+
#if !LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT
827843
lock_.clear();
844+
#endif
828845
}
829846

830847
spinlock &operator=(const spinlock &other) noexcept {
@@ -834,14 +851,31 @@ private:
834851
}
835852

836853
void lock() noexcept {
837-
while (lock_.test_and_set(std::memory_order_acq_rel))
838-
;
854+
// Optimistically assume the lock is free
855+
while (lock_.test_and_set(std::memory_order_acq_rel)) {
856+
#if LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT
857+
// Wait for lock to be released utilizing C++20 futex support
858+
lock_.wait(true, std::memory_order::relaxed);
859+
#endif
860+
}
839861
}
840862

841-
void unlock() noexcept { lock_.clear(std::memory_order_release); }
863+
void unlock() noexcept {
864+
lock_.clear(std::memory_order_release);
865+
#if LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT
866+
lock_.notify_one();
867+
#endif
868+
}
842869

843870
bool try_lock() noexcept {
871+
#if LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT
872+
// First do a relaxed load to check if lock is free in order to prevent
873+
// unnecessary cache misses if someone does while(!try_lock())
874+
return !lock_.test(std::memory_order_relaxed) &&
875+
!lock_.test_and_set(std::memory_order_acq_rel);
876+
#else
844877
return !lock_.test_and_set(std::memory_order_acq_rel);
878+
#endif
845879
}
846880

847881
counter_type &elem_counter() noexcept { return elem_counter_; }

0 commit comments

Comments
 (0)