Last active
January 28, 2024 15:10
-
-
Save azat/51a5fcc3a40af9f678906a3a6e14e079 to your computer and use it in GitHub Desktop.
Test various locks (under contention and not)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <boost/smart_ptr/detail/spinlock.hpp> | |
#include <cstdint> | |
#include <cstring> | |
#include <cassert> | |
#include <iostream> | |
#include <list> | |
#include <mutex> | |
#include <optional> | |
#include <shared_mutex> | |
#include <thread> | |
uint64_t now_ns() | |
{ | |
struct timespec ts; | |
clock_gettime(CLOCK_MONOTONIC_RAW, &ts); | |
return ts.tv_sec * (uint64_t)1e9 + ts.tv_nsec; | |
} | |
struct Job | |
{ | |
std::optional<std::thread> thread; | |
uint64_t elapsed_ns = 0; | |
}; | |
template <class L> | |
class SharedScopedLock | |
{ | |
public: | |
SharedScopedLock(L & l) : l(l) { l.lock_shared(); } | |
~SharedScopedLock() { l.unlock_shared(); } | |
private: | |
L & l; | |
}; | |
template <class L> void init_lock(L & l) {} | |
template <> void init_lock(boost::detail::spinlock & l) | |
{ | |
boost::detail::spinlock init = BOOST_DETAIL_SPINLOCK_INIT; | |
std::memcpy(&l, &init, sizeof(init)); | |
} | |
template <class L, class SL> | |
void test_lock(const char * name, size_t threads, size_t iterations = 10'000) | |
{ | |
L lock; | |
init_lock(lock); | |
std::list<Job> jobs(threads); | |
for (auto & job : jobs) | |
{ | |
job.thread.emplace([&lock, &job, iterations]() | |
{ | |
uint64_t start = now_ns(); | |
for (size_t i = 0; i < iterations; ++i) | |
SL sp(lock); | |
uint64_t end = now_ns(); | |
assert(end >= start); | |
job.elapsed_ns += end - start; | |
}); | |
} | |
for (auto & job : jobs) | |
{ | |
job.thread->join(); | |
} | |
uint64_t elapsed_ns = 0; | |
for (auto & job : jobs) | |
elapsed_ns += job.elapsed_ns; | |
std::cout | |
<< name << "(threads=" << threads << ")" | |
<< "/elapsed: " << elapsed_ns/1e6 << " ms, " | |
<< iterations/(elapsed_ns/1e9) << "ops, " | |
<< elapsed_ns/iterations << "ns per call\n"; | |
} | |
void test_boost_spinlock() | |
{ | |
using lock_t = boost::detail::spinlock; | |
using scoped_lock_t = boost::detail::spinlock::scoped_lock; | |
test_lock<lock_t, scoped_lock_t>("boost::atomic_shared_ptr::spinlock", 1); | |
test_lock<lock_t, scoped_lock_t>("boost::atomic_shared_ptr::spinlock", 100); | |
} | |
void test_std_lock() | |
{ | |
using lock_t = std::mutex; | |
using scoped_lock_t = std::scoped_lock<lock_t>; | |
test_lock<lock_t, scoped_lock_t>("std::mutex", 1); | |
test_lock<lock_t, scoped_lock_t>("std::mutex", 100); | |
} | |
void test_std_rw_lock_exclusive() | |
{ | |
using lock_t = std::shared_mutex; | |
using scoped_lock_t = std::scoped_lock<lock_t>; | |
test_lock<lock_t, scoped_lock_t>("std::shared_mutex/exclusive_lock", 1); | |
test_lock<lock_t, scoped_lock_t>("std::shared_mutex/exclusive_lock", 100); | |
} | |
void test_std_rw_lock_shared() | |
{ | |
using lock_t = std::shared_mutex; | |
using scoped_lock_t = SharedScopedLock<lock_t>; | |
test_lock<lock_t, scoped_lock_t>("std::shared_mutex/shared_lock", 1); | |
test_lock<lock_t, scoped_lock_t>("std::shared_mutex/shared_lock", 100); | |
} | |
int main() | |
{ | |
test_boost_spinlock(); | |
test_std_lock(); | |
test_std_rw_lock_exclusive(); | |
test_std_rw_lock_shared(); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ clang++ /tmp/test-contention.cpp -o /tmp/test-contention -O3 -g3 | |
$ /tmp/test-contention | |
boost::atomic_shared_ptr::spinlock(threads=1)/elapsed: 0.083276 ms, 1.20083e+08ops, 8ns per call | |
boost::atomic_shared_ptr::spinlock(threads=100)/elapsed: 469.24 ms, 21311ops, 46924ns per call | |
std::mutex(threads=1)/elapsed: 0.061976 ms, 1.61353e+08ops, 6ns per call | |
std::mutex(threads=100)/elapsed: 2548.34 ms, 3924.13ops, 254833ns per call | |
std::shared_mutex/exclusive_lock(threads=1)/elapsed: 0.065312 ms, 1.53111e+08ops, 6ns per call | |
std::shared_mutex/exclusive_lock(threads=100)/elapsed: 101700 ms, 98.3285ops, 10169990ns per call | |
std::shared_mutex/shared_lock(threads=1)/elapsed: 0.057047 ms, 1.75294e+08ops, 5ns per call | |
std::shared_mutex/shared_lock(threads=100)/elapsed: 4573.88 ms, 2186.33ops, 457388ns per call |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment