22 #include <condition_variable> 26 #include <shared_mutex> 29 using namespace folly;
32 DEFINE_uint64(iterations, 100,
"The number of iterations with lock held");
136 template <
typename NonMovableType>
137 std::vector<std::unique_ptr<NonMovableType>> makeVector(
int num);
147 template <
typename Mutex>
151 std::vector<std::unique_ptr<Mutex>> mutexes);
152 template <
typename Mutex>
156 std::vector<std::unique_ptr<Mutex>> mutexes);
157 template <
typename Mutex>
161 std::vector<std::unique_ptr<Mutex>> mutexes);
177 template <
typename LockingFunc>
178 void pathological(std::size_t iters, LockingFunc func);
185 template <
typename LockingFunc>
186 void simple(std::size_t iters, LockingFunc func);
191 template <
typename LockingFunc>
192 void uncontended(std::size_t iters, LockingFunc func);
198 class BenchmarkStartBarrier {
200 explicit BenchmarkStartBarrier(
int threads) : threads_{threads + 1} {}
203 auto lck = std::unique_lock<std::mutex>{
mutex_};
207 if (started_ == threads_) {
213 while (started_ != threads_) {
219 std::condition_variable
cv_;
226 uncontended(iters, [](
auto& one,
auto& two) {
folly::lock(one, two); });
230 uncontended(iters, [](
auto& one,
auto& two) {
std::lock(one, two); });
234 uncontended(iters, [](
auto& one,
auto& two) {
241 uncontended(iters, [](
auto& one,
auto& two) {
258 simple(iters, [](
auto& one,
auto& two) {
265 simple(iters, [](
auto& one,
auto& two) {
274 pathological(iters, [](
auto& one,
auto& two,
auto& three) {
280 pathological(iters, [](
auto& one,
auto& two,
auto& three) {
286 pathological(iters, [](
auto& one,
auto& two,
auto& three) {
293 BENCHMARK(ThreeThreadsPathologicalCarefullyOrdered, iters) {
294 pathological(iters, [](
auto& one,
auto& two,
auto& three) {
304 ordered(iters, 2, makeVector<std::mutex>(2));
307 smart(iters, 2, makeVector<std::mutex>(2));
310 persistent(iters, 2, makeVector<std::mutex>(2));
316 ordered(iters, 2, makeVector<std::mutex>(4));
319 smart(iters, 2, makeVector<std::mutex>(4));
322 persistent(iters, 2, makeVector<std::mutex>(4));
328 ordered(iters, 2, makeVector<std::mutex>(8));
331 smart(iters, 2, makeVector<std::mutex>(8));
334 persistent(iters, 2, makeVector<std::mutex>(8));
340 ordered(iters, 2, makeVector<std::mutex>(16));
343 smart(iters, 2, makeVector<std::mutex>(16));
346 persistent(iters, 2, makeVector<std::mutex>(16));
352 ordered(iters, 4, makeVector<std::mutex>(2));
355 smart(iters, 4, makeVector<std::mutex>(2));
358 persistent(iters, 4, makeVector<std::mutex>(2));
364 ordered(iters, 4, makeVector<std::mutex>(4));
367 smart(iters, 4, makeVector<std::mutex>(4));
370 persistent(iters, 4, makeVector<std::mutex>(4));
376 ordered(iters, 4, makeVector<std::mutex>(8));
379 smart(iters, 4, makeVector<std::mutex>(8));
382 persistent(iters, 4, makeVector<std::mutex>(8));
388 ordered(iters, 4, makeVector<std::mutex>(16));
391 smart(iters, 4, makeVector<std::mutex>(16));
394 persistent(iters, 4, makeVector<std::mutex>(16));
400 ordered(iters, 8, makeVector<std::mutex>(2));
403 smart(iters, 8, makeVector<std::mutex>(2));
406 persistent(iters, 8, makeVector<std::mutex>(2));
412 ordered(iters, 8, makeVector<std::mutex>(4));
415 smart(iters, 8, makeVector<std::mutex>(4));
418 persistent(iters, 8, makeVector<std::mutex>(4));
424 ordered(iters, 8, makeVector<std::mutex>(8));
427 smart(iters, 8, makeVector<std::mutex>(8));
430 persistent(iters, 8, makeVector<std::mutex>(8));
436 ordered(iters, 8, makeVector<std::mutex>(16));
439 smart(iters, 8, makeVector<std::mutex>(16));
441 BENCHMARK(EightThreadsSixteenMutexesPersistent, iters) {
442 persistent(iters, 8, makeVector<std::mutex>(16));
448 ordered(iters, 16, makeVector<std::mutex>(2));
451 smart(iters, 16, makeVector<std::mutex>(2));
454 persistent(iters, 16, makeVector<std::mutex>(2));
460 ordered(iters, 16, makeVector<std::mutex>(4));
463 smart(iters, 16, makeVector<std::mutex>(4));
466 persistent(iters, 16, makeVector<std::mutex>(4));
472 ordered(iters, 16, makeVector<std::mutex>(8));
475 smart(iters, 16, makeVector<std::mutex>(8));
477 BENCHMARK(SixteenThreadsEightMutexesPersistent, iters) {
478 persistent(iters, 16, makeVector<std::mutex>(8));
484 ordered(iters, 16, makeVector<std::mutex>(16));
487 smart(iters, 16, makeVector<std::mutex>(16));
489 BENCHMARK(SixteenThreadsSixteenMutexesPersistent, iters) {
490 persistent(iters, 16, makeVector<std::mutex>(16));
494 gflags::ParseCommandLineFlags(&argc, &argv,
true);
499 std::pair<int, int> getMutexIndices(
int threadId,
int mutexListSize) {
505 auto index = threadId % mutexListSize;
507 auto firstMutexIndex = ((index + 1) == mutexListSize) ? 0 : index;
508 auto secondMutexIndex =
509 ((index + 1) == mutexListSize) ? (mutexListSize - 1) : (index + 1);
511 return std::make_pair(firstMutexIndex, secondMutexIndex);
514 template <
typename NonMovableType>
515 std::vector<std::unique_ptr<NonMovableType>> makeVector(
int num) {
516 auto vector = std::vector<std::unique_ptr<NonMovableType>>{};
518 for (
auto i = 0;
i < num; ++
i) {
519 vector.push_back(std::make_unique<NonMovableType>());
524 template <
typename Mutex>
528 std::vector<std::unique_ptr<Mutex>> mutexes) {
533 std::sort(mutexes.begin(), mutexes.end(), [](
auto& one,
auto& two) {
534 return one.get() < two.get();
537 auto threads = std::vector<std::thread>{};
538 auto&& barrier = BenchmarkStartBarrier{numThreads};
540 for (
auto thread = 0; thread < numThreads; ++thread) {
541 threads.emplace_back([&mutexes, iters, thread, &barrier] {
544 auto indices = getMutexIndices(thread, mutexes.size());
545 for (
auto i = std::size_t{0};
i < iters; ++
i) {
547 mutexes[indices.first]->lock();
548 mutexes[indices.second]->lock();
550 spin(FLAGS_iterations);
552 mutexes[indices.first]->unlock();
553 mutexes[indices.second]->unlock();
559 suspender.dismissing([&] {
560 for (
auto& thread : threads) {
566 template <
typename Mutex>
570 std::vector<std::unique_ptr<Mutex>> mutexes) {
573 auto threads = std::vector<std::thread>{};
574 auto&& barrier = BenchmarkStartBarrier{numThreads};
576 for (
auto thread = 0; thread < numThreads; ++thread) {
577 threads.emplace_back([iters, &mutexes, thread, &barrier] {
580 auto indices = std::make_pair(
581 thread % mutexes.size(), (thread + 1) % mutexes.size());
582 for (
auto iter = std::size_t{0}; iter < iters; ++iter) {
584 mutexes[indices.first]->lock();
585 if (mutexes[indices.second]->try_lock()) {
589 mutexes[indices.first]->unlock();
590 std::swap(indices.first, indices.second);
594 spin(FLAGS_iterations);
596 mutexes[indices.first]->unlock();
597 mutexes[indices.second]->unlock();
603 suspender.dismissing([&] {
610 template <
typename Mutex>
614 std::vector<std::unique_ptr<Mutex>> mutexes) {
617 auto threads = std::vector<std::thread>{};
618 auto&& barrier = BenchmarkStartBarrier{numThreads};
620 for (
auto thread = 0; thread < numThreads; ++thread) {
621 threads.emplace_back([iters, &mutexes, thread, &barrier] {
624 auto indices = std::make_pair(
625 thread % mutexes.size(), (thread + 1) % mutexes.size());
626 for (
auto iter = std::size_t{0}; iter < iters; ++iter) {
630 mutexes[indices.first]->lock();
631 if (mutexes[indices.second]->try_lock()) {
635 mutexes[indices.first]->unlock();
638 spin(FLAGS_iterations);
640 mutexes[indices.first]->unlock();
641 mutexes[indices.second]->unlock();
647 suspender.dismissing([&] {
654 template <
typename LockingFunc>
655 void simple(std::size_t iters, LockingFunc func) {
659 auto&& barrier = BenchmarkStartBarrier{3};
661 auto threadOne = std::thread{[&] {
664 for (
auto i = std::size_t{0};
i < iters; ++
i) {
665 auto lckOne = std::unique_lock<std::mutex>{one, std::defer_lock};
666 auto lckTwo = std::unique_lock<std::mutex>{two, std::defer_lock};
667 func(lckOne, lckTwo);
669 spin(FLAGS_iterations);
673 auto threadTwo = std::thread{[&] {
676 for (
auto i = std::size_t{0};
i < iters; ++
i) {
677 auto lck = std::unique_lock<std::mutex>{one};
678 spin(FLAGS_iterations * FLAGS_iterations);
682 auto threadThree = std::thread{[&] {
685 for (
auto i = std::size_t{0};
i < iters; ++
i) {
686 auto lck = std::unique_lock<std::mutex>{two};
687 spin(FLAGS_iterations * FLAGS_iterations);
692 suspender.dismissing([&] {
699 template <
typename LockingFunc>
700 void pathological(std::size_t iters, LockingFunc func) {
705 auto&& barrier = BenchmarkStartBarrier{3};
707 auto threadOne = std::thread{[&] {
710 for (
auto i = std::size_t{0};
i < iters; ++
i) {
711 auto lckOne = std::unique_lock<std::mutex>{one, std::defer_lock};
712 auto lckTwo = std::unique_lock<std::mutex>{two, std::defer_lock};
713 auto lckThree = std::unique_lock<std::mutex>{three, std::defer_lock};
714 func(lckOne, lckTwo, lckThree);
716 spin(FLAGS_iterations);
720 auto threadTwo = std::thread{[&] {
723 for (
auto i = std::size_t{0};
i < iters; ++
i) {
724 auto lck = std::unique_lock<std::mutex>{one};
726 spin(FLAGS_iterations * FLAGS_iterations);
730 auto threadThree = std::thread{[&] {
733 for (
auto i = std::size_t{0};
i < iters; ++
i) {
734 auto lckTwo = std::unique_lock<std::mutex>{two};
735 auto lckThree = std::unique_lock<std::mutex>{three};
737 spin(FLAGS_iterations * FLAGS_iterations);
742 suspender.dismissing([&] {
749 template <
typename LockingFunc>
750 void uncontended(std::size_t iters, LockingFunc func) {
755 suspender.dismissing([&] {
756 for (
auto i = std::size_t{0};
i < iters; ++
i) {
759 spin(FLAGS_iterations);
767 void spin(
int iterations) {
768 for (
auto i = 0;
i < iterations; ++
i) {
DEFINE_uint64(iterations, 100,"The number of iterations with lock held")
—— Concurrent Priority Queue Implementation ——
std::condition_variable cv_
int main(int argc, char **argv)
std::vector< std::thread::id > threads
auto lock(SynchronizedLocker...lockersIn) -> std::tuple< typename SynchronizedLocker::LockedPtr... >
auto lock(Synchronized< D, M > &synchronized, Args &&...args)
bool wait(Waiter *waiter, bool shouldSleep, Waiter *&next)
bool spin(Waiter &waiter)
BENCHMARK(fbFollyGlobalBenchmarkBaseline)
void swap(SwapTrackingAlloc< T > &, SwapTrackingAlloc< T > &)
auto doNotOptimizeAway(const T &datum) -> typename std::enable_if< !detail::DoNotOptimizeAwayNeedsIndirect< T >::value >::type