2 * Copyright 2015-present Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 #include <folly/SharedMutex.h>
22 #include <boost/optional.hpp>
23 #include <boost/thread/shared_mutex.hpp>
25 #include <folly/Benchmark.h>
26 #include <folly/MPMCQueue.h>
27 #include <folly/portability/GFlags.h>
28 #include <folly/portability/GTest.h>
29 #include <folly/synchronization/RWSpinLock.h>
30 #include <folly/test/DeterministicSchedule.h>
32 using namespace folly;
33 using namespace folly::test;
35 using namespace std::chrono;
37 typedef DeterministicSchedule DSched;
38 typedef SharedMutexImpl<true, void, DeterministicAtomic, true>
39 DSharedMutexReadPriority;
40 typedef SharedMutexImpl<false, void, DeterministicAtomic, true>
41 DSharedMutexWritePriority;
43 template <typename Lock>
46 SharedMutexToken token1;
47 SharedMutexToken token2;
48 SharedMutexToken token3;
50 EXPECT_TRUE(lock.try_lock());
51 EXPECT_FALSE(lock.try_lock());
52 EXPECT_FALSE(lock.try_lock_shared(token1));
55 EXPECT_TRUE(lock.try_lock_shared(token1));
56 EXPECT_FALSE(lock.try_lock());
57 EXPECT_TRUE(lock.try_lock_shared(token2));
58 lock.lock_shared(token3);
59 lock.unlock_shared(token3);
60 lock.unlock_shared(token2);
61 lock.unlock_shared(token1);
66 lock.lock_shared(token1);
67 lock.lock_shared(token2);
68 lock.unlock_shared(token1);
69 lock.unlock_shared(token2);
72 lock.unlock_and_lock_shared(token1);
73 lock.lock_shared(token2);
74 lock.unlock_shared(token2);
75 lock.unlock_shared(token1);
78 TEST(SharedMutex, basic) {
79 runBasicTest<SharedMutexReadPriority>();
80 runBasicTest<SharedMutexWritePriority>();
83 template <typename Lock>
84 void runBasicHoldersTest() {
86 SharedMutexToken token;
89 // create an exclusive write lock via holder
90 typename Lock::WriteHolder holder(lock);
91 EXPECT_FALSE(lock.try_lock());
92 EXPECT_FALSE(lock.try_lock_shared(token));
94 // move ownership to another write holder via move constructor
95 typename Lock::WriteHolder holder2(std::move(holder));
96 EXPECT_FALSE(lock.try_lock());
97 EXPECT_FALSE(lock.try_lock_shared(token));
99 // move ownership to another write holder via assign operator
100 typename Lock::WriteHolder holder3(nullptr);
101 holder3 = std::move(holder2);
102 EXPECT_FALSE(lock.try_lock());
103 EXPECT_FALSE(lock.try_lock_shared(token));
105 // downgrade from exclusive to upgrade lock via move constructor
106 typename Lock::UpgradeHolder holder4(std::move(holder3));
108 // ensure we can lock from a shared source
109 EXPECT_FALSE(lock.try_lock());
110 EXPECT_TRUE(lock.try_lock_shared(token));
111 lock.unlock_shared(token);
113 // promote from upgrade to exclusive lock via move constructor
114 typename Lock::WriteHolder holder5(std::move(holder4));
115 EXPECT_FALSE(lock.try_lock());
116 EXPECT_FALSE(lock.try_lock_shared(token));
118 // downgrade exclusive to shared lock via move constructor
119 typename Lock::ReadHolder holder6(std::move(holder5));
121 // ensure we can lock from another shared source
122 EXPECT_FALSE(lock.try_lock());
123 EXPECT_TRUE(lock.try_lock_shared(token));
124 lock.unlock_shared(token);
128 typename Lock::WriteHolder holder(lock);
129 EXPECT_FALSE(lock.try_lock());
133 typename Lock::ReadHolder holder(lock);
134 typename Lock::ReadHolder holder2(lock);
135 typename Lock::UpgradeHolder holder3(lock);
139 typename Lock::UpgradeHolder holder(lock);
140 typename Lock::ReadHolder holder2(lock);
141 typename Lock::ReadHolder holder3(std::move(holder));
145 TEST(SharedMutex, basic_holders) {
146 runBasicHoldersTest<SharedMutexReadPriority>();
147 runBasicHoldersTest<SharedMutexWritePriority>();
150 template <typename Lock>
151 void runManyReadLocksTestWithTokens() {
154 vector<SharedMutexToken> tokens;
155 for (int i = 0; i < 1000; ++i) {
156 tokens.emplace_back();
157 EXPECT_TRUE(lock.try_lock_shared(tokens.back()));
159 for (auto& token : tokens) {
160 lock.unlock_shared(token);
162 EXPECT_TRUE(lock.try_lock());
166 TEST(SharedMutex, many_read_locks_with_tokens) {
167 runManyReadLocksTestWithTokens<SharedMutexReadPriority>();
168 runManyReadLocksTestWithTokens<SharedMutexWritePriority>();
171 template <typename Lock>
172 void runManyReadLocksTestWithoutTokens() {
175 for (int i = 0; i < 1000; ++i) {
176 EXPECT_TRUE(lock.try_lock_shared());
178 for (int i = 0; i < 1000; ++i) {
179 lock.unlock_shared();
181 EXPECT_TRUE(lock.try_lock());
185 TEST(SharedMutex, many_read_locks_without_tokens) {
186 runManyReadLocksTestWithoutTokens<SharedMutexReadPriority>();
187 runManyReadLocksTestWithoutTokens<SharedMutexWritePriority>();
190 template <typename Lock>
191 void runTimeoutInPastTest() {
194 EXPECT_TRUE(lock.try_lock_for(milliseconds(0)));
196 EXPECT_TRUE(lock.try_lock_for(milliseconds(-1)));
198 EXPECT_TRUE(lock.try_lock_shared_for(milliseconds(0)));
199 lock.unlock_shared();
200 EXPECT_TRUE(lock.try_lock_shared_for(milliseconds(-1)));
201 lock.unlock_shared();
202 EXPECT_TRUE(lock.try_lock_until(system_clock::now() - milliseconds(1)));
205 lock.try_lock_shared_until(system_clock::now() - milliseconds(1)));
206 lock.unlock_shared();
207 EXPECT_TRUE(lock.try_lock_until(steady_clock::now() - milliseconds(1)));
210 lock.try_lock_shared_until(steady_clock::now() - milliseconds(1)));
211 lock.unlock_shared();
214 TEST(SharedMutex, timeout_in_past) {
215 runTimeoutInPastTest<SharedMutexReadPriority>();
216 runTimeoutInPastTest<SharedMutexWritePriority>();
219 template <class Func>
220 bool funcHasDuration(milliseconds expectedDuration, Func func) {
221 // elapsed time should eventually fall within expectedDuration +- 25%
222 for (int tries = 0; tries < 100; ++tries) {
223 auto start = steady_clock::now();
225 auto elapsed = steady_clock::now() - start;
226 if (elapsed > expectedDuration - expectedDuration / 4 &&
227 elapsed < expectedDuration + expectedDuration / 4) {
234 template <typename Lock>
235 void runFailingTryTimeoutTest() {
238 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
239 EXPECT_FALSE(lock.try_lock_for(milliseconds(10)));
241 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
242 typename Lock::Token token;
243 EXPECT_FALSE(lock.try_lock_shared_for(milliseconds(10), token));
245 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
246 EXPECT_FALSE(lock.try_lock_upgrade_for(milliseconds(10)));
248 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
249 EXPECT_FALSE(lock.try_lock_until(steady_clock::now() + milliseconds(10)));
251 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
252 typename Lock::Token token;
253 EXPECT_FALSE(lock.try_lock_shared_until(
254 steady_clock::now() + milliseconds(10), token));
256 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
258 lock.try_lock_upgrade_until(steady_clock::now() + milliseconds(10)));
260 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
261 EXPECT_FALSE(lock.try_lock_until(system_clock::now() + milliseconds(10)));
263 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
264 typename Lock::Token token;
265 EXPECT_FALSE(lock.try_lock_shared_until(
266 system_clock::now() + milliseconds(10), token));
268 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
270 lock.try_lock_upgrade_until(system_clock::now() + milliseconds(10)));
275 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
276 EXPECT_FALSE(lock.try_lock_for(milliseconds(10)));
278 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
279 EXPECT_FALSE(lock.try_lock_until(steady_clock::now() + milliseconds(10)));
281 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
282 EXPECT_FALSE(lock.try_lock_until(system_clock::now() + milliseconds(10)));
284 lock.unlock_shared();
287 for (int p = 0; p < 8; ++p) {
288 EXPECT_FALSE(lock.try_lock_for(nanoseconds(1 << p)));
292 for (int p = 0; p < 8; ++p) {
293 typename Lock::ReadHolder holder1(lock);
294 typename Lock::ReadHolder holder2(lock);
295 typename Lock::ReadHolder holder3(lock);
296 EXPECT_FALSE(lock.try_lock_for(nanoseconds(1 << p)));
300 TEST(SharedMutex, failing_try_timeout) {
301 runFailingTryTimeoutTest<SharedMutexReadPriority>();
302 runFailingTryTimeoutTest<SharedMutexWritePriority>();
305 template <typename Lock>
306 void runBasicUpgradeTest() {
308 typename Lock::Token token1;
309 typename Lock::Token token2;
312 EXPECT_FALSE(lock.try_lock());
313 EXPECT_TRUE(lock.try_lock_shared(token1));
314 lock.unlock_shared(token1);
315 lock.unlock_upgrade();
318 lock.unlock_upgrade_and_lock();
319 EXPECT_FALSE(lock.try_lock_shared(token1));
323 lock.unlock_upgrade_and_lock_shared(token1);
325 lock.unlock_upgrade_and_lock_shared(token2);
326 lock.unlock_shared(token1);
327 lock.unlock_shared(token2);
330 lock.unlock_and_lock_upgrade();
331 EXPECT_TRUE(lock.try_lock_shared(token1));
332 lock.unlock_upgrade();
333 lock.unlock_shared(token1);
336 TEST(SharedMutex, basic_upgrade_tests) {
337 runBasicUpgradeTest<SharedMutexReadPriority>();
338 runBasicUpgradeTest<SharedMutexWritePriority>();
341 TEST(SharedMutex, read_has_prio) {
342 SharedMutexReadPriority lock;
343 SharedMutexToken token1;
344 SharedMutexToken token2;
345 lock.lock_shared(token1);
346 bool exclusiveAcquired = false;
347 auto writer = thread([&] {
349 exclusiveAcquired = true;
353 // lock() can't complete until we unlock token1, but it should stake
354 // its claim with regards to other exclusive or upgrade locks. We can
355 // use try_lock_upgrade to poll for that eventuality.
356 while (lock.try_lock_upgrade()) {
357 lock.unlock_upgrade();
358 this_thread::yield();
360 EXPECT_FALSE(exclusiveAcquired);
362 // Even though lock() is stuck we should be able to get token2
363 EXPECT_TRUE(lock.try_lock_shared(token2));
364 lock.unlock_shared(token1);
365 lock.unlock_shared(token2);
367 EXPECT_TRUE(exclusiveAcquired);
370 TEST(SharedMutex, write_has_prio) {
371 SharedMutexWritePriority lock;
372 SharedMutexToken token1;
373 SharedMutexToken token2;
374 lock.lock_shared(token1);
375 auto writer = thread([&] {
380 // eventually lock() should block readers
381 while (lock.try_lock_shared(token2)) {
382 lock.unlock_shared(token2);
383 this_thread::yield();
386 lock.unlock_shared(token1);
391 SharedMutexToken token;
393 template <typename T>
398 template <typename T>
399 void unlock(T* lock) {
403 template <typename T>
404 void lock_shared(T* lock) {
405 lock->lock_shared(token);
408 template <typename T>
409 void unlock_shared(T* lock) {
410 lock->unlock_shared(token);
415 template <typename T>
420 template <typename T>
421 void unlock(T* lock) {
425 template <typename T>
426 void lock_shared(T* lock) {
430 template <typename T>
431 void unlock_shared(T* lock) {
432 lock->unlock_shared();
437 template <typename T>
442 template <typename T>
443 void unlock(T* lock) {
447 template <typename T>
448 void lock_shared(T* lock) {
452 template <typename T>
453 void unlock_shared(T* lock) {
459 pthread_rwlock_t lock_;
461 PosixRWLock() { pthread_rwlock_init(&lock_, nullptr); }
463 ~PosixRWLock() { pthread_rwlock_destroy(&lock_); }
465 void lock() { pthread_rwlock_wrlock(&lock_); }
467 void unlock() { pthread_rwlock_unlock(&lock_); }
469 void lock_shared() { pthread_rwlock_rdlock(&lock_); }
471 void unlock_shared() { pthread_rwlock_unlock(&lock_); }
475 pthread_mutex_t lock_;
477 PosixMutex() { pthread_mutex_init(&lock_, nullptr); }
479 ~PosixMutex() { pthread_mutex_destroy(&lock_); }
481 void lock() { pthread_mutex_lock(&lock_); }
483 void unlock() { pthread_mutex_unlock(&lock_); }
485 void lock_shared() { pthread_mutex_lock(&lock_); }
487 void unlock_shared() { pthread_mutex_unlock(&lock_); }
490 template <template <typename> class Atom, typename Lock, typename Locker>
491 static void runContendedReaders(size_t numOps,
493 bool useSeparateLocks) {
497 int valueProtectedByLock = 10;
500 Atom<bool> go(false);
501 Atom<bool>* goPtr = &go; // workaround for clang bug
502 vector<thread> threads(numThreads);
505 for (size_t t = 0; t < numThreads; ++t) {
506 threads[t] = DSched::thread([&, t, numThreads] {
508 Lock* lock = useSeparateLocks ? &privateLock : &globalLock;
510 while (!goPtr->load()) {
511 this_thread::yield();
513 for (size_t op = t; op < numOps; op += numThreads) {
514 locker.lock_shared(lock);
515 // note: folly::doNotOptimizeAway reads and writes to its arg,
516 // so the following two lines are very different than a call
517 // to folly::doNotOptimizeAway(valueProtectedByLock);
518 auto copy = valueProtectedByLock;
519 folly::doNotOptimizeAway(copy);
520 locker.unlock_shared(lock);
527 for (auto& thr : threads) {
532 static void folly_rwspin_reads(uint32_t numOps,
534 bool useSeparateLocks) {
535 runContendedReaders<atomic, RWSpinLock, Locker>(
536 numOps, numThreads, useSeparateLocks);
539 static void shmtx_wr_pri_reads(uint32_t numOps,
541 bool useSeparateLocks) {
542 runContendedReaders<atomic, SharedMutexWritePriority, TokenLocker>(
543 numOps, numThreads, useSeparateLocks);
546 static void shmtx_w_bare_reads(uint32_t numOps,
548 bool useSeparateLocks) {
549 runContendedReaders<atomic, SharedMutexWritePriority, Locker>(
550 numOps, numThreads, useSeparateLocks);
553 static void shmtx_rd_pri_reads(uint32_t numOps,
555 bool useSeparateLocks) {
556 runContendedReaders<atomic, SharedMutexReadPriority, TokenLocker>(
557 numOps, numThreads, useSeparateLocks);
560 static void shmtx_r_bare_reads(uint32_t numOps,
562 bool useSeparateLocks) {
563 runContendedReaders<atomic, SharedMutexReadPriority, Locker>(
564 numOps, numThreads, useSeparateLocks);
567 static void folly_ticket_reads(uint32_t numOps,
569 bool useSeparateLocks) {
570 runContendedReaders<atomic, RWTicketSpinLock64, Locker>(
571 numOps, numThreads, useSeparateLocks);
574 static void boost_shared_reads(uint32_t numOps,
576 bool useSeparateLocks) {
577 runContendedReaders<atomic, boost::shared_mutex, Locker>(
578 numOps, numThreads, useSeparateLocks);
581 static void pthrd_rwlock_reads(uint32_t numOps,
583 bool useSeparateLocks) {
584 runContendedReaders<atomic, PosixRWLock, Locker>(
585 numOps, numThreads, useSeparateLocks);
588 template <template <typename> class Atom, typename Lock, typename Locker>
589 static void runMixed(size_t numOps,
591 double writeFraction,
592 bool useSeparateLocks) {
596 int valueProtectedByLock = 0;
599 Atom<bool> go(false);
600 Atom<bool>* goPtr = &go; // workaround for clang bug
601 vector<thread> threads(numThreads);
604 for (size_t t = 0; t < numThreads; ++t) {
605 threads[t] = DSched::thread([&, t, numThreads] {
606 struct drand48_data buffer;
607 srand48_r(t, &buffer);
608 long writeThreshold = writeFraction * 0x7fffffff;
610 Lock* lock = useSeparateLocks ? &privateLock : &globalLock;
612 while (!goPtr->load()) {
613 this_thread::yield();
615 for (size_t op = t; op < numOps; op += numThreads) {
617 lrand48_r(&buffer, &randVal);
618 bool writeOp = randVal < writeThreshold;
621 if (!useSeparateLocks) {
622 ++valueProtectedByLock;
626 locker.lock_shared(lock);
627 auto v = valueProtectedByLock;
628 folly::doNotOptimizeAway(v);
629 locker.unlock_shared(lock);
637 for (auto& thr : threads) {
642 static void folly_rwspin(size_t numOps,
644 double writeFraction,
645 bool useSeparateLocks) {
646 runMixed<atomic, RWSpinLock, Locker>(
647 numOps, numThreads, writeFraction, useSeparateLocks);
650 static void shmtx_wr_pri(uint32_t numOps,
652 double writeFraction,
653 bool useSeparateLocks) {
654 runMixed<atomic, SharedMutexWritePriority, TokenLocker>(
655 numOps, numThreads, writeFraction, useSeparateLocks);
658 static void shmtx_w_bare(uint32_t numOps,
660 double writeFraction,
661 bool useSeparateLocks) {
662 runMixed<atomic, SharedMutexWritePriority, Locker>(
663 numOps, numThreads, writeFraction, useSeparateLocks);
666 static void shmtx_rd_pri(uint32_t numOps,
668 double writeFraction,
669 bool useSeparateLocks) {
670 runMixed<atomic, SharedMutexReadPriority, TokenLocker>(
671 numOps, numThreads, writeFraction, useSeparateLocks);
674 static void shmtx_r_bare(uint32_t numOps,
676 double writeFraction,
677 bool useSeparateLocks) {
678 runMixed<atomic, SharedMutexReadPriority, Locker>(
679 numOps, numThreads, writeFraction, useSeparateLocks);
682 static void folly_ticket(size_t numOps,
684 double writeFraction,
685 bool useSeparateLocks) {
686 runMixed<atomic, RWTicketSpinLock64, Locker>(
687 numOps, numThreads, writeFraction, useSeparateLocks);
690 static void boost_shared(size_t numOps,
692 double writeFraction,
693 bool useSeparateLocks) {
694 runMixed<atomic, boost::shared_mutex, Locker>(
695 numOps, numThreads, writeFraction, useSeparateLocks);
698 static void pthrd_rwlock(size_t numOps,
700 double writeFraction,
701 bool useSeparateLocks) {
702 runMixed<atomic, PosixRWLock, Locker>(
703 numOps, numThreads, writeFraction, useSeparateLocks);
706 static void pthrd_mutex_(size_t numOps,
708 double writeFraction,
709 bool useSeparateLocks) {
710 runMixed<atomic, PosixMutex, Locker>(
711 numOps, numThreads, writeFraction, useSeparateLocks);
714 template <typename Lock, template <typename> class Atom>
715 static void runAllAndValidate(size_t numOps, size_t numThreads) {
717 Atom<int> globalExclusiveCount(0);
718 Atom<int> globalUpgradeCount(0);
719 Atom<int> globalSharedCount(0);
721 Atom<bool> go(false);
723 // clang crashes on access to Atom<> captured by ref in closure
724 Atom<int>* globalExclusiveCountPtr = &globalExclusiveCount;
725 Atom<int>* globalUpgradeCountPtr = &globalUpgradeCount;
726 Atom<int>* globalSharedCountPtr = &globalSharedCount;
727 Atom<bool>* goPtr = &go;
729 vector<thread> threads(numThreads);
732 for (size_t t = 0; t < numThreads; ++t) {
733 threads[t] = DSched::thread([&, t, numThreads] {
734 struct drand48_data buffer;
735 srand48_r(t, &buffer);
737 bool exclusive = false;
738 bool upgrade = false;
740 bool ourGlobalTokenUsed = false;
741 SharedMutexToken ourGlobalToken;
744 vector<SharedMutexToken> privateTokens;
746 while (!goPtr->load()) {
747 this_thread::yield();
749 for (size_t op = t; op < numOps; op += numThreads) {
750 // randVal in [0,1000)
752 lrand48_r(&buffer, &randVal);
753 randVal = (long)((randVal * (uint64_t)1000) / 0x7fffffff);
755 // make as many assertions as possible about the global state
757 EXPECT_EQ(1, globalExclusiveCountPtr->load(memory_order_acquire));
758 EXPECT_EQ(0, globalUpgradeCountPtr->load(memory_order_acquire));
759 EXPECT_EQ(0, globalSharedCountPtr->load(memory_order_acquire));
762 EXPECT_EQ(0, globalExclusiveCountPtr->load(memory_order_acquire));
763 EXPECT_EQ(1, globalUpgradeCountPtr->load(memory_order_acquire));
766 EXPECT_EQ(0, globalExclusiveCountPtr->load(memory_order_acquire));
767 EXPECT_TRUE(globalSharedCountPtr->load(memory_order_acquire) > 0);
769 EXPECT_FALSE(ourGlobalTokenUsed);
772 // independent 20% chance we do something to the private lock
774 // it's okay to take multiple private shared locks because
775 // we never take an exclusive lock, so reader versus writer
776 // priority doesn't cause deadlocks
777 if (randVal < 100 && privateTokens.size() > 0) {
778 auto i = randVal % privateTokens.size();
779 privateLock.unlock_shared(privateTokens[i]);
780 privateTokens.erase(privateTokens.begin() + i);
782 SharedMutexToken token;
783 privateLock.lock_shared(token);
784 privateTokens.push_back(token);
789 // if we've got a lock, the only thing we can do is release it
790 // or transform it into a different kind of lock
793 --*globalExclusiveCountPtr;
796 } else if (randVal < 700) {
797 globalLock.unlock_and_lock_shared();
798 ++*globalSharedCountPtr;
800 } else if (randVal < 900) {
801 globalLock.unlock_and_lock_shared(ourGlobalToken);
802 ++*globalSharedCountPtr;
804 ourGlobalTokenUsed = true;
806 globalLock.unlock_and_lock_upgrade();
807 ++*globalUpgradeCountPtr;
810 } else if (upgrade) {
812 --*globalUpgradeCountPtr;
814 globalLock.unlock_upgrade();
815 } else if (randVal < 700) {
816 globalLock.unlock_upgrade_and_lock_shared();
817 ++*globalSharedCountPtr;
819 } else if (randVal < 900) {
820 globalLock.unlock_upgrade_and_lock_shared(ourGlobalToken);
821 ++*globalSharedCountPtr;
823 ourGlobalTokenUsed = true;
825 globalLock.unlock_upgrade_and_lock();
826 ++*globalExclusiveCountPtr;
831 --*globalSharedCountPtr;
832 if (ourGlobalTokenUsed) {
833 globalLock.unlock_shared(ourGlobalToken);
834 ourGlobalTokenUsed = false;
836 globalLock.unlock_shared();
838 } else if (randVal < 400) {
839 // 40% chance of shared lock with token, 5 ways to get it
841 // delta t goes from -1 millis to 7 millis
842 auto dt = microseconds(10 * (randVal - 100));
845 globalLock.lock_shared(ourGlobalToken);
847 } else if (randVal < 500) {
848 shared = globalLock.try_lock_shared(ourGlobalToken);
849 } else if (randVal < 600) {
850 shared = globalLock.try_lock_shared_for(dt, ourGlobalToken);
851 } else if (randVal < 800) {
852 shared = globalLock.try_lock_shared_until(
853 system_clock::now() + dt, ourGlobalToken);
856 ourGlobalTokenUsed = true;
857 ++*globalSharedCountPtr;
859 } else if (randVal < 800) {
860 // 40% chance of shared lock without token
861 auto dt = microseconds(10 * (randVal - 100));
863 globalLock.lock_shared();
865 } else if (randVal < 500) {
866 shared = globalLock.try_lock_shared();
867 } else if (randVal < 600) {
868 shared = globalLock.try_lock_shared_for(dt);
869 } else if (randVal < 800) {
870 shared = globalLock.try_lock_shared_until(
871 system_clock::now() + dt);
874 ++*globalSharedCountPtr;
876 } else if (randVal < 900) {
877 // 10% change of upgrade lock
878 globalLock.lock_upgrade();
880 ++*globalUpgradeCountPtr;
882 // 10% chance of exclusive lock, 5 ways to get it
884 // delta t goes from -1 millis to 9 millis
885 auto dt = microseconds(100 * (randVal - 910));
890 } else if (randVal < 500) {
891 exclusive = globalLock.try_lock();
892 } else if (randVal < 600) {
893 exclusive = globalLock.try_lock_for(dt);
894 } else if (randVal < 700) {
895 exclusive = globalLock.try_lock_until(steady_clock::now() + dt);
897 exclusive = globalLock.try_lock_until(system_clock::now() + dt);
900 ++*globalExclusiveCountPtr;
906 --*globalExclusiveCountPtr;
910 --*globalUpgradeCountPtr;
911 globalLock.unlock_upgrade();
914 --*globalSharedCountPtr;
915 if (ourGlobalTokenUsed) {
916 globalLock.unlock_shared(ourGlobalToken);
917 ourGlobalTokenUsed = false;
919 globalLock.unlock_shared();
922 for (auto& token : privateTokens) {
923 privateLock.unlock_shared(token);
930 for (auto& thr : threads) {
935 TEST(SharedMutex, deterministic_concurrent_readers_of_one_lock_read_prio) {
936 for (int pass = 0; pass < 3; ++pass) {
937 DSched sched(DSched::uniform(pass));
938 runContendedReaders<DeterministicAtomic,
939 DSharedMutexReadPriority,
940 Locker>(1000, 3, false);
944 TEST(SharedMutex, deterministic_concurrent_readers_of_one_lock_write_prio) {
945 for (int pass = 0; pass < 3; ++pass) {
946 DSched sched(DSched::uniform(pass));
947 runContendedReaders<DeterministicAtomic,
948 DSharedMutexWritePriority,
949 Locker>(1000, 3, false);
953 TEST(SharedMutex, concurrent_readers_of_one_lock_read_prio) {
954 for (int pass = 0; pass < 10; ++pass) {
955 runContendedReaders<atomic, SharedMutexReadPriority, Locker>(
960 TEST(SharedMutex, concurrent_readers_of_one_lock_write_prio) {
961 for (int pass = 0; pass < 10; ++pass) {
962 runContendedReaders<atomic, SharedMutexWritePriority, Locker>(
967 TEST(SharedMutex, deterministic_readers_of_concurrent_locks_read_prio) {
968 for (int pass = 0; pass < 3; ++pass) {
969 DSched sched(DSched::uniform(pass));
970 runContendedReaders<DeterministicAtomic,
971 DSharedMutexReadPriority,
972 Locker>(1000, 3, true);
976 TEST(SharedMutex, deterministic_readers_of_concurrent_locks_write_prio) {
977 for (int pass = 0; pass < 3; ++pass) {
978 DSched sched(DSched::uniform(pass));
979 runContendedReaders<DeterministicAtomic,
980 DSharedMutexWritePriority,
981 Locker>(1000, 3, true);
985 TEST(SharedMutex, readers_of_concurrent_locks_read_prio) {
986 for (int pass = 0; pass < 10; ++pass) {
987 runContendedReaders<atomic, SharedMutexReadPriority, TokenLocker>(
992 TEST(SharedMutex, readers_of_concurrent_locks_write_prio) {
993 for (int pass = 0; pass < 10; ++pass) {
994 runContendedReaders<atomic, SharedMutexWritePriority, TokenLocker>(
999 TEST(SharedMutex, deterministic_mixed_mostly_read_read_prio) {
1000 for (int pass = 0; pass < 3; ++pass) {
1001 DSched sched(DSched::uniform(pass));
1002 runMixed<DeterministicAtomic, DSharedMutexReadPriority, Locker>(
1003 1000, 3, 0.1, false);
1007 TEST(SharedMutex, deterministic_mixed_mostly_read_write_prio) {
1008 for (int pass = 0; pass < 3; ++pass) {
1009 DSched sched(DSched::uniform(pass));
1010 runMixed<DeterministicAtomic, DSharedMutexWritePriority, Locker>(
1011 1000, 3, 0.1, false);
1015 TEST(SharedMutex, mixed_mostly_read_read_prio) {
1016 for (int pass = 0; pass < 5; ++pass) {
1017 runMixed<atomic, SharedMutexReadPriority, TokenLocker>(
1018 10000, 32, 0.1, false);
1022 TEST(SharedMutex, mixed_mostly_read_write_prio) {
1023 for (int pass = 0; pass < 5; ++pass) {
1024 runMixed<atomic, SharedMutexWritePriority, TokenLocker>(
1025 10000, 32, 0.1, false);
1029 TEST(SharedMutex, deterministic_mixed_mostly_write_read_prio) {
1030 for (int pass = 0; pass < 1; ++pass) {
1031 DSched sched(DSched::uniform(pass));
1032 runMixed<DeterministicAtomic, DSharedMutexReadPriority, TokenLocker>(
1033 1000, 10, 0.9, false);
1037 TEST(SharedMutex, deterministic_mixed_mostly_write_write_prio) {
1038 for (int pass = 0; pass < 1; ++pass) {
1039 DSched sched(DSched::uniform(pass));
1040 runMixed<DeterministicAtomic, DSharedMutexWritePriority, TokenLocker>(
1041 1000, 10, 0.9, false);
1045 TEST(SharedMutex, deterministic_lost_wakeup_write_prio) {
1046 for (int pass = 0; pass < 10; ++pass) {
1047 DSched sched(DSched::uniformSubset(pass, 2, 200));
1048 runMixed<DeterministicAtomic, DSharedMutexWritePriority, TokenLocker>(
1049 1000, 3, 1.0, false);
1053 TEST(SharedMutex, mixed_mostly_write_read_prio) {
1054 for (int pass = 0; pass < (folly::kIsSanitizeAddress ? 1 : 5); ++pass) {
1055 runMixed<atomic, SharedMutexReadPriority, TokenLocker>(
1056 50000, 300, 0.9, false);
1060 TEST(SharedMutex, mixed_mostly_write_write_prio) {
1061 for (int pass = 0; pass < (folly::kIsSanitizeAddress ? 1 : 5); ++pass) {
1062 runMixed<atomic, SharedMutexWritePriority, TokenLocker>(
1063 50000, 300, 0.9, false);
1067 TEST(SharedMutex, deterministic_all_ops_read_prio) {
1068 for (int pass = 0; pass < 5; ++pass) {
1069 DSched sched(DSched::uniform(pass));
1070 runAllAndValidate<DSharedMutexReadPriority, DeterministicAtomic>(1000, 8);
1074 TEST(SharedMutex, deterministic_all_ops_write_prio) {
1075 for (int pass = 0; pass < 5; ++pass) {
1076 DSched sched(DSched::uniform(pass));
1077 runAllAndValidate<DSharedMutexWritePriority, DeterministicAtomic>(1000, 8);
1081 TEST(SharedMutex, all_ops_read_prio) {
1082 for (int pass = 0; pass < 5; ++pass) {
1083 runAllAndValidate<SharedMutexReadPriority, atomic>(100000, 32);
1087 TEST(SharedMutex, all_ops_write_prio) {
1088 for (int pass = 0; pass < 5; ++pass) {
1089 runAllAndValidate<SharedMutexWritePriority, atomic>(100000, 32);
1093 FOLLY_ASSUME_FBVECTOR_COMPATIBLE(
1094 boost::optional<boost::optional<SharedMutexToken>>)
1096 // Setup is a set of threads that either grab a shared lock, or exclusive
1097 // and then downgrade it, or upgrade then upgrade and downgrade, then
1098 // enqueue the shared lock to a second set of threads that just performs
1099 // unlocks. Half of the shared locks use tokens, the others don't.
1100 template <typename Lock, template <typename> class Atom>
1101 static void runRemoteUnlock(size_t numOps,
1102 double preWriteFraction,
1103 double preUpgradeFraction,
1104 size_t numSendingThreads,
1105 size_t numReceivingThreads) {
1107 MPMCQueue<boost::optional<boost::optional<SharedMutexToken>>, Atom>
1109 auto queuePtr = &queue; // workaround for clang crash
1111 Atom<bool> go(false);
1112 auto goPtr = &go; // workaround for clang crash
1113 Atom<int> pendingSenders(numSendingThreads);
1114 auto pendingSendersPtr = &pendingSenders; // workaround for clang crash
1115 vector<thread> threads(numSendingThreads + numReceivingThreads);
1118 for (size_t t = 0; t < threads.size(); ++t) {
1119 threads[t] = DSched::thread([&, t, numSendingThreads] {
1120 if (t >= numSendingThreads) {
1122 typename decltype(queue)::value_type elem;
1124 queuePtr->blockingRead(elem);
1126 // EOF, pass the EOF token
1127 queuePtr->blockingWrite(std::move(elem));
1131 globalLock.unlock_shared(**elem);
1133 globalLock.unlock_shared();
1138 // else we're a sender
1140 struct drand48_data buffer;
1141 srand48_r(t, &buffer);
1143 while (!goPtr->load()) {
1144 this_thread::yield();
1146 for (size_t op = t; op < numOps; op += numSendingThreads) {
1147 long unscaledRandVal;
1148 lrand48_r(&buffer, &unscaledRandVal);
1151 double randVal = ((double)unscaledRandVal) / 0x7fffffff;
1153 // extract a bit and rescale
1154 bool useToken = randVal >= 0.5;
1155 randVal = (randVal - (useToken ? 0.5 : 0.0)) * 2;
1157 boost::optional<SharedMutexToken> maybeToken;
1160 SharedMutexToken token;
1161 if (randVal < preWriteFraction) {
1163 globalLock.unlock_and_lock_shared(token);
1164 } else if (randVal < preWriteFraction + preUpgradeFraction / 2) {
1165 globalLock.lock_upgrade();
1166 globalLock.unlock_upgrade_and_lock_shared(token);
1167 } else if (randVal < preWriteFraction + preUpgradeFraction) {
1168 globalLock.lock_upgrade();
1169 globalLock.unlock_upgrade_and_lock();
1170 globalLock.unlock_and_lock_shared(token);
1172 globalLock.lock_shared(token);
1176 if (randVal < preWriteFraction) {
1178 globalLock.unlock_and_lock_shared();
1179 } else if (randVal < preWriteFraction + preUpgradeFraction / 2) {
1180 globalLock.lock_upgrade();
1181 globalLock.unlock_upgrade_and_lock_shared();
1182 } else if (randVal < preWriteFraction + preUpgradeFraction) {
1183 globalLock.lock_upgrade();
1184 globalLock.unlock_upgrade_and_lock();
1185 globalLock.unlock_and_lock_shared();
1187 globalLock.lock_shared();
1191 // blockingWrite is emplace-like, so this automatically adds
1192 // another level of wrapping
1193 queuePtr->blockingWrite(maybeToken);
1195 if (--*pendingSendersPtr == 0) {
1196 queuePtr->blockingWrite(boost::none);
1203 for (auto& thr : threads) {
1208 TEST(SharedMutex, deterministic_remote_write_prio) {
1209 for (int pass = 0; pass < 1; ++pass) {
1210 DSched sched(DSched::uniform(pass));
1211 runRemoteUnlock<DSharedMutexWritePriority, DeterministicAtomic>(
1212 500, 0.1, 0.1, 5, 5);
1216 TEST(SharedMutex, deterministic_remote_read_prio) {
1217 for (int pass = 0; pass < 1; ++pass) {
1218 DSched sched(DSched::uniform(pass));
1219 runRemoteUnlock<DSharedMutexReadPriority, DeterministicAtomic>(
1220 500, 0.1, 0.1, 5, 5);
1224 TEST(SharedMutex, remote_write_prio) {
1225 for (int pass = 0; pass < 10; ++pass) {
1226 runRemoteUnlock<SharedMutexWritePriority, atomic>(100000, 0.1, 0.1, 5, 5);
1230 TEST(SharedMutex, remote_read_prio) {
1231 for (int pass = 0; pass < (folly::kIsSanitizeAddress ? 1 : 100); ++pass) {
1232 runRemoteUnlock<SharedMutexReadPriority, atomic>(100000, 0.1, 0.1, 5, 5);
1236 static void burn(size_t n) {
1237 for (size_t i = 0; i < n; ++i) {
1238 folly::doNotOptimizeAway(i);
1242 // Two threads and three locks, arranged so that they have to proceed
1243 // in turn with reader/writer conflict
1244 template <typename Lock, template <typename> class Atom = atomic>
1245 static void runPingPong(size_t numRounds, size_t burnCount) {
1248 pair<Lock, char[56]> locks[3];
1253 auto availPtr = &avail; // workaround for clang crash
1254 Atom<bool> go(false);
1255 auto goPtr = &go; // workaround for clang crash
1256 vector<thread> threads(2);
1258 locks[0].first.lock();
1259 locks[1].first.lock();
1260 locks[2].first.lock_shared();
1263 threads[0] = DSched::thread([&] {
1265 while (!goPtr->load()) {
1266 this_thread::yield();
1268 for (size_t i = 0; i < numRounds; ++i) {
1269 locks[i % 3].first.unlock();
1270 locks[(i + 2) % 3].first.lock();
1274 threads[1] = DSched::thread([&] {
1276 while (!goPtr->load()) {
1277 this_thread::yield();
1279 for (size_t i = 0; i < numRounds; ++i) {
1280 locks[i % 3].first.lock_shared();
1282 locks[(i + 2) % 3].first.unlock_shared();
1286 while (avail.load() < 2) {
1287 this_thread::yield();
1292 for (auto& thr : threads) {
1295 locks[numRounds % 3].first.unlock();
1296 locks[(numRounds + 1) % 3].first.unlock();
1297 locks[(numRounds + 2) % 3].first.unlock_shared();
1300 static void folly_rwspin_ping_pong(size_t n, size_t scale, size_t burnCount) {
1301 runPingPong<RWSpinLock>(n / scale, burnCount);
1304 static void shmtx_w_bare_ping_pong(size_t n, size_t scale, size_t burnCount) {
1305 runPingPong<SharedMutexWritePriority>(n / scale, burnCount);
1308 static void shmtx_r_bare_ping_pong(size_t n, size_t scale, size_t burnCount) {
1309 runPingPong<SharedMutexReadPriority>(n / scale, burnCount);
1312 static void folly_ticket_ping_pong(size_t n, size_t scale, size_t burnCount) {
1313 runPingPong<RWTicketSpinLock64>(n / scale, burnCount);
1316 static void boost_shared_ping_pong(size_t n, size_t scale, size_t burnCount) {
1317 runPingPong<boost::shared_mutex>(n / scale, burnCount);
1320 static void pthrd_rwlock_ping_pong(size_t n, size_t scale, size_t burnCount) {
1321 runPingPong<PosixRWLock>(n / scale, burnCount);
1324 TEST(SharedMutex, deterministic_ping_pong_write_prio) {
1325 for (int pass = 0; pass < 1; ++pass) {
1326 DSched sched(DSched::uniform(pass));
1327 runPingPong<DSharedMutexWritePriority, DeterministicAtomic>(500, 0);
1331 TEST(SharedMutex, deterministic_ping_pong_read_prio) {
1332 for (int pass = 0; pass < 1; ++pass) {
1333 DSched sched(DSched::uniform(pass));
1334 runPingPong<DSharedMutexReadPriority, DeterministicAtomic>(500, 0);
1338 TEST(SharedMutex, ping_pong_write_prio) {
1339 for (int pass = 0; pass < 1; ++pass) {
1340 runPingPong<SharedMutexWritePriority, atomic>(50000, 0);
1344 TEST(SharedMutex, ping_pong_read_prio) {
1345 for (int pass = 0; pass < 1; ++pass) {
1346 runPingPong<SharedMutexReadPriority, atomic>(50000, 0);
1350 // This is here so you can tell how much of the runtime reported by the
1351 // more complex harnesses is due to the harness, although due to the
1352 // magic of compiler optimization it may also be slower
1353 BENCHMARK(single_thread_lock_shared_unlock_shared, iters) {
1355 for (size_t n = 0; n < iters; ++n) {
1356 SharedMutex::Token token;
1357 lock.lock_shared(token);
1358 folly::doNotOptimizeAway(0);
1359 lock.unlock_shared(token);
1363 BENCHMARK(single_thread_lock_unlock, iters) {
1365 for (size_t n = 0; n < iters; ++n) {
1367 folly::doNotOptimizeAway(0);
1372 #define BENCH_BASE(...) FB_VA_GLUE(BENCHMARK_NAMED_PARAM, (__VA_ARGS__))
1373 #define BENCH_REL(...) FB_VA_GLUE(BENCHMARK_RELATIVE_NAMED_PARAM, (__VA_ARGS__))
1375 // 100% reads. Best-case scenario for deferred locks. Lock is colocated
1376 // with read data, so inline lock takes cache miss every time but deferred
1377 // lock has only cache hits and local access.
1378 BENCHMARK_DRAW_LINE()
1379 BENCHMARK_DRAW_LINE()
1380 BENCH_BASE(folly_rwspin_reads, 1thread, 1, false)
1381 BENCH_REL (shmtx_wr_pri_reads, 1thread, 1, false)
1382 BENCH_REL (shmtx_w_bare_reads, 1thread, 1, false)
1383 BENCH_REL (shmtx_rd_pri_reads, 1thread, 1, false)
1384 BENCH_REL (shmtx_r_bare_reads, 1thread, 1, false)
1385 BENCH_REL (folly_ticket_reads, 1thread, 1, false)
1386 BENCH_REL (boost_shared_reads, 1thread, 1, false)
1387 BENCH_REL (pthrd_rwlock_reads, 1thread, 1, false)
1388 BENCHMARK_DRAW_LINE()
1389 BENCH_BASE(folly_rwspin_reads, 2thread, 2, false)
1390 BENCH_REL (shmtx_wr_pri_reads, 2thread, 2, false)
1391 BENCH_REL (shmtx_w_bare_reads, 2thread, 2, false)
1392 BENCH_REL (shmtx_rd_pri_reads, 2thread, 2, false)
1393 BENCH_REL (shmtx_r_bare_reads, 2thread, 2, false)
1394 BENCH_REL (folly_ticket_reads, 2thread, 2, false)
1395 BENCH_REL (boost_shared_reads, 2thread, 2, false)
1396 BENCH_REL (pthrd_rwlock_reads, 2thread, 2, false)
1397 BENCHMARK_DRAW_LINE()
1398 BENCH_BASE(folly_rwspin_reads, 4thread, 4, false)
1399 BENCH_REL (shmtx_wr_pri_reads, 4thread, 4, false)
1400 BENCH_REL (shmtx_w_bare_reads, 4thread, 4, false)
1401 BENCH_REL (shmtx_rd_pri_reads, 4thread, 4, false)
1402 BENCH_REL (shmtx_r_bare_reads, 4thread, 4, false)
1403 BENCH_REL (folly_ticket_reads, 4thread, 4, false)
1404 BENCH_REL (boost_shared_reads, 4thread, 4, false)
1405 BENCH_REL (pthrd_rwlock_reads, 4thread, 4, false)
1406 BENCHMARK_DRAW_LINE()
1407 BENCH_BASE(folly_rwspin_reads, 8thread, 8, false)
1408 BENCH_REL (shmtx_wr_pri_reads, 8thread, 8, false)
1409 BENCH_REL (shmtx_w_bare_reads, 8thread, 8, false)
1410 BENCH_REL (shmtx_rd_pri_reads, 8thread, 8, false)
1411 BENCH_REL (shmtx_r_bare_reads, 8thread, 8, false)
1412 BENCH_REL (folly_ticket_reads, 8thread, 8, false)
1413 BENCH_REL (boost_shared_reads, 8thread, 8, false)
1414 BENCH_REL (pthrd_rwlock_reads, 8thread, 8, false)
1415 BENCHMARK_DRAW_LINE()
1416 BENCH_BASE(folly_rwspin_reads, 16thread, 16, false)
1417 BENCH_REL (shmtx_wr_pri_reads, 16thread, 16, false)
1418 BENCH_REL (shmtx_w_bare_reads, 16thread, 16, false)
1419 BENCH_REL (shmtx_rd_pri_reads, 16thread, 16, false)
1420 BENCH_REL (shmtx_r_bare_reads, 16thread, 16, false)
1421 BENCH_REL (folly_ticket_reads, 16thread, 16, false)
1422 BENCH_REL (boost_shared_reads, 16thread, 16, false)
1423 BENCH_REL (pthrd_rwlock_reads, 16thread, 16, false)
1424 BENCHMARK_DRAW_LINE()
1425 BENCH_BASE(folly_rwspin_reads, 32thread, 32, false)
1426 BENCH_REL (shmtx_wr_pri_reads, 32thread, 32, false)
1427 BENCH_REL (shmtx_w_bare_reads, 32thread, 32, false)
1428 BENCH_REL (shmtx_rd_pri_reads, 32thread, 32, false)
1429 BENCH_REL (shmtx_r_bare_reads, 32thread, 32, false)
1430 BENCH_REL (folly_ticket_reads, 32thread, 32, false)
1431 BENCH_REL (boost_shared_reads, 32thread, 32, false)
1432 BENCH_REL (pthrd_rwlock_reads, 32thread, 32, false)
1433 BENCHMARK_DRAW_LINE()
1434 BENCH_BASE(folly_rwspin_reads, 64thread, 64, false)
1435 BENCH_REL (shmtx_wr_pri_reads, 64thread, 64, false)
1436 BENCH_REL (shmtx_w_bare_reads, 64thread, 64, false)
1437 BENCH_REL (shmtx_rd_pri_reads, 64thread, 64, false)
1438 BENCH_REL (shmtx_r_bare_reads, 64thread, 64, false)
1439 BENCH_REL (folly_ticket_reads, 64thread, 64, false)
1440 BENCH_REL (boost_shared_reads, 64thread, 64, false)
1441 BENCH_REL (pthrd_rwlock_reads, 64thread, 64, false)
1443 // 1 lock used by everybody, 100% writes. Threads only hurt, but it is
1444 // good to not fail catastrophically. Compare to single_thread_lock_unlock
1445 // to see the overhead of the generic driver (and its pseudo-random number
1446 // generator). pthrd_mutex_ is a pthread_mutex_t (default, not adaptive),
1447 // which is better than any of the reader-writer locks for this scenario.
1448 BENCHMARK_DRAW_LINE()
1449 BENCHMARK_DRAW_LINE()
1450 BENCH_BASE(folly_rwspin, 1thread_all_write, 1, 1.0, false)
1451 BENCH_REL (shmtx_wr_pri, 1thread_all_write, 1, 1.0, false)
1452 BENCH_REL (shmtx_rd_pri, 1thread_all_write, 1, 1.0, false)
1453 BENCH_REL (folly_ticket, 1thread_all_write, 1, 1.0, false)
1454 BENCH_REL (boost_shared, 1thread_all_write, 1, 1.0, false)
1455 BENCH_REL (pthrd_rwlock, 1thread_all_write, 1, 1.0, false)
1456 BENCH_REL (pthrd_mutex_, 1thread_all_write, 1, 1.0, false)
1457 BENCHMARK_DRAW_LINE()
1458 BENCH_BASE(folly_rwspin, 2thread_all_write, 2, 1.0, false)
1459 BENCH_REL (shmtx_wr_pri, 2thread_all_write, 2, 1.0, false)
1460 BENCH_REL (shmtx_rd_pri, 2thread_all_write, 2, 1.0, false)
1461 BENCH_REL (folly_ticket, 2thread_all_write, 2, 1.0, false)
1462 BENCH_REL (boost_shared, 2thread_all_write, 2, 1.0, false)
1463 BENCH_REL (pthrd_rwlock, 2thread_all_write, 2, 1.0, false)
1464 BENCH_REL (pthrd_mutex_, 2thread_all_write, 2, 1.0, false)
1465 BENCHMARK_DRAW_LINE()
1466 BENCH_BASE(folly_rwspin, 4thread_all_write, 4, 1.0, false)
1467 BENCH_REL (shmtx_wr_pri, 4thread_all_write, 4, 1.0, false)
1468 BENCH_REL (shmtx_rd_pri, 4thread_all_write, 4, 1.0, false)
1469 BENCH_REL (folly_ticket, 4thread_all_write, 4, 1.0, false)
1470 BENCH_REL (boost_shared, 4thread_all_write, 4, 1.0, false)
1471 BENCH_REL (pthrd_rwlock, 4thread_all_write, 4, 1.0, false)
1472 BENCH_REL (pthrd_mutex_, 4thread_all_write, 4, 1.0, false)
1473 BENCHMARK_DRAW_LINE()
1474 BENCH_BASE(folly_rwspin, 8thread_all_write, 8, 1.0, false)
1475 BENCH_REL (shmtx_wr_pri, 8thread_all_write, 8, 1.0, false)
1476 BENCH_REL (shmtx_rd_pri, 8thread_all_write, 8, 1.0, false)
1477 BENCH_REL (folly_ticket, 8thread_all_write, 8, 1.0, false)
1478 BENCH_REL (boost_shared, 8thread_all_write, 8, 1.0, false)
1479 BENCH_REL (pthrd_rwlock, 8thread_all_write, 8, 1.0, false)
1480 BENCH_REL (pthrd_mutex_, 8thread_all_write, 8, 1.0, false)
1481 BENCHMARK_DRAW_LINE()
1482 BENCH_BASE(folly_rwspin, 16thread_all_write, 16, 1.0, false)
1483 BENCH_REL (shmtx_wr_pri, 16thread_all_write, 16, 1.0, false)
1484 BENCH_REL (shmtx_rd_pri, 16thread_all_write, 16, 1.0, false)
1485 BENCH_REL (folly_ticket, 16thread_all_write, 16, 1.0, false)
1486 BENCH_REL (boost_shared, 16thread_all_write, 16, 1.0, false)
1487 BENCH_REL (pthrd_rwlock, 16thread_all_write, 16, 1.0, false)
1488 BENCH_REL (pthrd_mutex_, 16thread_all_write, 16, 1.0, false)
1489 BENCHMARK_DRAW_LINE()
1490 BENCH_BASE(folly_rwspin, 32thread_all_write, 32, 1.0, false)
1491 BENCH_REL (shmtx_wr_pri, 32thread_all_write, 32, 1.0, false)
1492 BENCH_REL (shmtx_rd_pri, 32thread_all_write, 32, 1.0, false)
1493 BENCH_REL (folly_ticket, 32thread_all_write, 32, 1.0, false)
1494 BENCH_REL (boost_shared, 32thread_all_write, 32, 1.0, false)
1495 BENCH_REL (pthrd_rwlock, 32thread_all_write, 32, 1.0, false)
1496 BENCH_REL (pthrd_mutex_, 32thread_all_write, 32, 1.0, false)
1497 BENCHMARK_DRAW_LINE()
1498 BENCH_BASE(folly_rwspin, 64thread_all_write, 64, 1.0, false)
1499 BENCH_REL (shmtx_wr_pri, 64thread_all_write, 64, 1.0, false)
1500 BENCH_REL (shmtx_rd_pri, 64thread_all_write, 64, 1.0, false)
1501 BENCH_REL (folly_ticket, 64thread_all_write, 64, 1.0, false)
1502 BENCH_REL (boost_shared, 64thread_all_write, 64, 1.0, false)
1503 BENCH_REL (pthrd_rwlock, 64thread_all_write, 64, 1.0, false)
1504 BENCH_REL (pthrd_mutex_, 64thread_all_write, 64, 1.0, false)
1506 // 1 lock used by everybody, 10% writes. Not much scaling to be had. Perf
1507 // is best at 1 thread, once you've got multiple threads > 8 threads hurts.
1508 BENCHMARK_DRAW_LINE()
1509 BENCHMARK_DRAW_LINE()
1510 BENCH_BASE(folly_rwspin, 1thread_10pct_write, 1, 0.10, false)
1511 BENCH_REL (shmtx_wr_pri, 1thread_10pct_write, 1, 0.10, false)
1512 BENCH_REL (shmtx_rd_pri, 1thread_10pct_write, 1, 0.10, false)
1513 BENCH_REL (folly_ticket, 1thread_10pct_write, 1, 0.10, false)
1514 BENCH_REL (boost_shared, 1thread_10pct_write, 1, 0.10, false)
1515 BENCH_REL (pthrd_rwlock, 1thread_10pct_write, 1, 0.10, false)
1516 BENCHMARK_DRAW_LINE()
1517 BENCH_BASE(folly_rwspin, 2thread_10pct_write, 2, 0.10, false)
1518 BENCH_REL (shmtx_wr_pri, 2thread_10pct_write, 2, 0.10, false)
1519 BENCH_REL (shmtx_rd_pri, 2thread_10pct_write, 2, 0.10, false)
1520 BENCH_REL (folly_ticket, 2thread_10pct_write, 2, 0.10, false)
1521 BENCH_REL (boost_shared, 2thread_10pct_write, 2, 0.10, false)
1522 BENCH_REL (pthrd_rwlock, 2thread_10pct_write, 2, 0.10, false)
1523 BENCHMARK_DRAW_LINE()
1524 BENCH_BASE(folly_rwspin, 4thread_10pct_write, 4, 0.10, false)
1525 BENCH_REL (shmtx_wr_pri, 4thread_10pct_write, 4, 0.10, false)
1526 BENCH_REL (shmtx_rd_pri, 4thread_10pct_write, 4, 0.10, false)
1527 BENCH_REL (folly_ticket, 4thread_10pct_write, 4, 0.10, false)
1528 BENCH_REL (boost_shared, 4thread_10pct_write, 4, 0.10, false)
1529 BENCH_REL (pthrd_rwlock, 4thread_10pct_write, 4, 0.10, false)
1530 BENCHMARK_DRAW_LINE()
1531 BENCH_BASE(folly_rwspin, 8thread_10pct_write, 8, 0.10, false)
1532 BENCH_REL (shmtx_wr_pri, 8thread_10pct_write, 8, 0.10, false)
1533 BENCH_REL (shmtx_rd_pri, 8thread_10pct_write, 8, 0.10, false)
1534 BENCH_REL (folly_ticket, 8thread_10pct_write, 8, 0.10, false)
1535 BENCH_REL (boost_shared, 8thread_10pct_write, 8, 0.10, false)
1536 BENCH_REL (pthrd_rwlock, 8thread_10pct_write, 8, 0.10, false)
1537 BENCHMARK_DRAW_LINE()
1538 BENCH_BASE(folly_rwspin, 16thread_10pct_write, 16, 0.10, false)
1539 BENCH_REL (shmtx_wr_pri, 16thread_10pct_write, 16, 0.10, false)
1540 BENCH_REL (shmtx_rd_pri, 16thread_10pct_write, 16, 0.10, false)
1541 BENCH_REL (folly_ticket, 16thread_10pct_write, 16, 0.10, false)
1542 BENCH_REL (boost_shared, 16thread_10pct_write, 16, 0.10, false)
1543 BENCH_REL (pthrd_rwlock, 16thread_10pct_write, 16, 0.10, false)
1544 BENCHMARK_DRAW_LINE()
1545 BENCH_BASE(folly_rwspin, 32thread_10pct_write, 32, 0.10, false)
1546 BENCH_REL (shmtx_wr_pri, 32thread_10pct_write, 32, 0.10, false)
1547 BENCH_REL (shmtx_rd_pri, 32thread_10pct_write, 32, 0.10, false)
1548 BENCH_REL (folly_ticket, 32thread_10pct_write, 32, 0.10, false)
1549 BENCH_REL (boost_shared, 32thread_10pct_write, 32, 0.10, false)
1550 BENCH_REL (pthrd_rwlock, 32thread_10pct_write, 32, 0.10, false)
1551 BENCHMARK_DRAW_LINE()
1552 BENCH_BASE(folly_rwspin, 64thread_10pct_write, 64, 0.10, false)
1553 BENCH_REL (shmtx_wr_pri, 64thread_10pct_write, 64, 0.10, false)
1554 BENCH_REL (shmtx_rd_pri, 64thread_10pct_write, 64, 0.10, false)
1555 BENCH_REL (folly_ticket, 64thread_10pct_write, 64, 0.10, false)
1556 BENCH_REL (boost_shared, 64thread_10pct_write, 64, 0.10, false)
1557 BENCH_REL (pthrd_rwlock, 64thread_10pct_write, 64, 0.10, false)
1559 // 1 lock used by everybody, 1% writes. This is a more realistic example
1560 // than the concurrent_*_reads benchmark, but still shows SharedMutex locks
1561 // winning over all of the others
1562 BENCHMARK_DRAW_LINE()
1563 BENCHMARK_DRAW_LINE()
1564 BENCH_BASE(folly_rwspin, 1thread_1pct_write, 1, 0.01, false)
1565 BENCH_REL (shmtx_wr_pri, 1thread_1pct_write, 1, 0.01, false)
1566 BENCH_REL (shmtx_w_bare, 1thread_1pct_write, 1, 0.01, false)
1567 BENCH_REL (shmtx_rd_pri, 1thread_1pct_write, 1, 0.01, false)
1568 BENCH_REL (shmtx_r_bare, 1thread_1pct_write, 1, 0.01, false)
1569 BENCH_REL (folly_ticket, 1thread_1pct_write, 1, 0.01, false)
1570 BENCH_REL (boost_shared, 1thread_1pct_write, 1, 0.01, false)
1571 BENCH_REL (pthrd_rwlock, 1thread_1pct_write, 1, 0.01, false)
1572 BENCHMARK_DRAW_LINE()
1573 BENCH_BASE(folly_rwspin, 2thread_1pct_write, 2, 0.01, false)
1574 BENCH_REL (shmtx_wr_pri, 2thread_1pct_write, 2, 0.01, false)
1575 BENCH_REL (shmtx_w_bare, 2thread_1pct_write, 2, 0.01, false)
1576 BENCH_REL (shmtx_rd_pri, 2thread_1pct_write, 2, 0.01, false)
1577 BENCH_REL (shmtx_r_bare, 2thread_1pct_write, 2, 0.01, false)
1578 BENCH_REL (folly_ticket, 2thread_1pct_write, 2, 0.01, false)
1579 BENCH_REL (boost_shared, 2thread_1pct_write, 2, 0.01, false)
1580 BENCH_REL (pthrd_rwlock, 2thread_1pct_write, 2, 0.01, false)
1581 BENCHMARK_DRAW_LINE()
1582 BENCH_BASE(folly_rwspin, 4thread_1pct_write, 4, 0.01, false)
1583 BENCH_REL (shmtx_wr_pri, 4thread_1pct_write, 4, 0.01, false)
1584 BENCH_REL (shmtx_w_bare, 4thread_1pct_write, 4, 0.01, false)
1585 BENCH_REL (shmtx_rd_pri, 4thread_1pct_write, 4, 0.01, false)
1586 BENCH_REL (shmtx_r_bare, 4thread_1pct_write, 4, 0.01, false)
1587 BENCH_REL (folly_ticket, 4thread_1pct_write, 4, 0.01, false)
1588 BENCH_REL (boost_shared, 4thread_1pct_write, 4, 0.01, false)
1589 BENCH_REL (pthrd_rwlock, 4thread_1pct_write, 4, 0.01, false)
1590 BENCHMARK_DRAW_LINE()
1591 BENCH_BASE(folly_rwspin, 8thread_1pct_write, 8, 0.01, false)
1592 BENCH_REL (shmtx_wr_pri, 8thread_1pct_write, 8, 0.01, false)
1593 BENCH_REL (shmtx_w_bare, 8thread_1pct_write, 8, 0.01, false)
1594 BENCH_REL (shmtx_rd_pri, 8thread_1pct_write, 8, 0.01, false)
1595 BENCH_REL (shmtx_r_bare, 8thread_1pct_write, 8, 0.01, false)
1596 BENCH_REL (folly_ticket, 8thread_1pct_write, 8, 0.01, false)
1597 BENCH_REL (boost_shared, 8thread_1pct_write, 8, 0.01, false)
1598 BENCH_REL (pthrd_rwlock, 8thread_1pct_write, 8, 0.01, false)
1599 BENCHMARK_DRAW_LINE()
1600 BENCH_BASE(folly_rwspin, 16thread_1pct_write, 16, 0.01, false)
1601 BENCH_REL (shmtx_wr_pri, 16thread_1pct_write, 16, 0.01, false)
1602 BENCH_REL (shmtx_w_bare, 16thread_1pct_write, 16, 0.01, false)
1603 BENCH_REL (shmtx_rd_pri, 16thread_1pct_write, 16, 0.01, false)
1604 BENCH_REL (shmtx_r_bare, 16thread_1pct_write, 16, 0.01, false)
1605 BENCH_REL (folly_ticket, 16thread_1pct_write, 16, 0.01, false)
1606 BENCH_REL (boost_shared, 16thread_1pct_write, 16, 0.01, false)
1607 BENCH_REL (pthrd_rwlock, 16thread_1pct_write, 16, 0.01, false)
1608 BENCHMARK_DRAW_LINE()
1609 BENCH_BASE(folly_rwspin, 32thread_1pct_write, 32, 0.01, false)
1610 BENCH_REL (shmtx_wr_pri, 32thread_1pct_write, 32, 0.01, false)
1611 BENCH_REL (shmtx_w_bare, 32thread_1pct_write, 32, 0.01, false)
1612 BENCH_REL (shmtx_rd_pri, 32thread_1pct_write, 32, 0.01, false)
1613 BENCH_REL (shmtx_r_bare, 32thread_1pct_write, 32, 0.01, false)
1614 BENCH_REL (folly_ticket, 32thread_1pct_write, 32, 0.01, false)
1615 BENCH_REL (boost_shared, 32thread_1pct_write, 32, 0.01, false)
1616 BENCH_REL (pthrd_rwlock, 32thread_1pct_write, 32, 0.01, false)
1617 BENCHMARK_DRAW_LINE()
1618 BENCH_BASE(folly_rwspin, 64thread_1pct_write, 64, 0.01, false)
1619 BENCH_REL (shmtx_wr_pri, 64thread_1pct_write, 64, 0.01, false)
1620 BENCH_REL (shmtx_w_bare, 64thread_1pct_write, 64, 0.01, false)
1621 BENCH_REL (shmtx_rd_pri, 64thread_1pct_write, 64, 0.01, false)
1622 BENCH_REL (shmtx_r_bare, 64thread_1pct_write, 64, 0.01, false)
1623 BENCH_REL (folly_ticket, 64thread_1pct_write, 64, 0.01, false)
1624 BENCH_REL (boost_shared, 64thread_1pct_write, 64, 0.01, false)
1625 BENCH_REL (pthrd_rwlock, 64thread_1pct_write, 64, 0.01, false)
1627 // Worst case scenario for deferred locks. No actual sharing, likely that
1628 // read operations will have to first set the kDeferredReadersPossibleBit,
1629 // and likely that writers will have to scan deferredReaders[].
1630 BENCHMARK_DRAW_LINE()
1631 BENCH_BASE(folly_rwspin, 2thr_2lock_50pct_write, 2, 0.50, true)
1632 BENCH_REL (shmtx_wr_pri, 2thr_2lock_50pct_write, 2, 0.50, true)
1633 BENCH_REL (shmtx_rd_pri, 2thr_2lock_50pct_write, 2, 0.50, true)
1634 BENCH_BASE(folly_rwspin, 4thr_4lock_50pct_write, 4, 0.50, true)
1635 BENCH_REL (shmtx_wr_pri, 4thr_4lock_50pct_write, 4, 0.50, true)
1636 BENCH_REL (shmtx_rd_pri, 4thr_4lock_50pct_write, 4, 0.50, true)
1637 BENCH_BASE(folly_rwspin, 8thr_8lock_50pct_write, 8, 0.50, true)
1638 BENCH_REL (shmtx_wr_pri, 8thr_8lock_50pct_write, 8, 0.50, true)
1639 BENCH_REL (shmtx_rd_pri, 8thr_8lock_50pct_write, 8, 0.50, true)
1640 BENCH_BASE(folly_rwspin, 16thr_16lock_50pct_write, 16, 0.50, true)
1641 BENCH_REL (shmtx_wr_pri, 16thr_16lock_50pct_write, 16, 0.50, true)
1642 BENCH_REL (shmtx_rd_pri, 16thr_16lock_50pct_write, 16, 0.50, true)
1643 BENCH_BASE(folly_rwspin, 32thr_32lock_50pct_write, 32, 0.50, true)
1644 BENCH_REL (shmtx_wr_pri, 32thr_32lock_50pct_write, 32, 0.50, true)
1645 BENCH_REL (shmtx_rd_pri, 32thr_32lock_50pct_write, 32, 0.50, true)
1646 BENCH_BASE(folly_rwspin, 64thr_64lock_50pct_write, 64, 0.50, true)
1647 BENCH_REL (shmtx_wr_pri, 64thr_64lock_50pct_write, 64, 0.50, true)
1648 BENCH_REL (shmtx_rd_pri, 64thr_64lock_50pct_write, 64, 0.50, true)
1649 BENCHMARK_DRAW_LINE()
1650 BENCH_BASE(folly_rwspin, 2thr_2lock_10pct_write, 2, 0.10, true)
1651 BENCH_REL (shmtx_wr_pri, 2thr_2lock_10pct_write, 2, 0.10, true)
1652 BENCH_REL (shmtx_rd_pri, 2thr_2lock_10pct_write, 2, 0.10, true)
1653 BENCH_BASE(folly_rwspin, 4thr_4lock_10pct_write, 4, 0.10, true)
1654 BENCH_REL (shmtx_wr_pri, 4thr_4lock_10pct_write, 4, 0.10, true)
1655 BENCH_REL (shmtx_rd_pri, 4thr_4lock_10pct_write, 4, 0.10, true)
1656 BENCH_BASE(folly_rwspin, 8thr_8lock_10pct_write, 8, 0.10, true)
1657 BENCH_REL (shmtx_wr_pri, 8thr_8lock_10pct_write, 8, 0.10, true)
1658 BENCH_REL (shmtx_rd_pri, 8thr_8lock_10pct_write, 8, 0.10, true)
1659 BENCH_BASE(folly_rwspin, 16thr_16lock_10pct_write, 16, 0.10, true)
1660 BENCH_REL (shmtx_wr_pri, 16thr_16lock_10pct_write, 16, 0.10, true)
1661 BENCH_REL (shmtx_rd_pri, 16thr_16lock_10pct_write, 16, 0.10, true)
1662 BENCH_BASE(folly_rwspin, 32thr_32lock_10pct_write, 32, 0.10, true)
1663 BENCH_REL (shmtx_wr_pri, 32thr_32lock_10pct_write, 32, 0.10, true)
1664 BENCH_REL (shmtx_rd_pri, 32thr_32lock_10pct_write, 32, 0.10, true)
1665 BENCH_BASE(folly_rwspin, 64thr_64lock_10pct_write, 64, 0.10, true)
1666 BENCH_REL (shmtx_wr_pri, 64thr_64lock_10pct_write, 64, 0.10, true)
1667 BENCH_REL (shmtx_rd_pri, 64thr_64lock_10pct_write, 64, 0.10, true)
1668 BENCHMARK_DRAW_LINE()
1669 BENCH_BASE(folly_rwspin, 2thr_2lock_1pct_write, 2, 0.01, true)
1670 BENCH_REL (shmtx_wr_pri, 2thr_2lock_1pct_write, 2, 0.01, true)
1671 BENCH_REL (shmtx_rd_pri, 2thr_2lock_1pct_write, 2, 0.01, true)
1672 BENCH_BASE(folly_rwspin, 4thr_4lock_1pct_write, 4, 0.01, true)
1673 BENCH_REL (shmtx_wr_pri, 4thr_4lock_1pct_write, 4, 0.01, true)
1674 BENCH_REL (shmtx_rd_pri, 4thr_4lock_1pct_write, 4, 0.01, true)
1675 BENCH_BASE(folly_rwspin, 8thr_8lock_1pct_write, 8, 0.01, true)
1676 BENCH_REL (shmtx_wr_pri, 8thr_8lock_1pct_write, 8, 0.01, true)
1677 BENCH_REL (shmtx_rd_pri, 8thr_8lock_1pct_write, 8, 0.01, true)
1678 BENCH_BASE(folly_rwspin, 16thr_16lock_1pct_write, 16, 0.01, true)
1679 BENCH_REL (shmtx_wr_pri, 16thr_16lock_1pct_write, 16, 0.01, true)
1680 BENCH_REL (shmtx_rd_pri, 16thr_16lock_1pct_write, 16, 0.01, true)
1681 BENCH_BASE(folly_rwspin, 32thr_32lock_1pct_write, 32, 0.01, true)
1682 BENCH_REL (shmtx_wr_pri, 32thr_32lock_1pct_write, 32, 0.01, true)
1683 BENCH_REL (shmtx_rd_pri, 32thr_32lock_1pct_write, 32, 0.01, true)
1684 BENCH_BASE(folly_rwspin, 64thr_64lock_1pct_write, 64, 0.01, true)
1685 BENCH_REL (shmtx_wr_pri, 64thr_64lock_1pct_write, 64, 0.01, true)
1686 BENCH_REL (shmtx_rd_pri, 64thr_64lock_1pct_write, 64, 0.01, true)
1688 // Ping-pong tests have a scaled number of iterations, because their burn
1689 // loop would make them too slow otherwise. Ping-pong with burn count of
1690 // 100k or 300k shows the advantage of soft-spin, reducing the cost of
1691 // each wakeup by about 20 usec. (Take benchmark reported difference,
1692 // ~400 nanos, multiply by the scale of 100, then divide by 2 because
1693 // each round has two wakeups.)
1694 BENCHMARK_DRAW_LINE()
1695 BENCHMARK_DRAW_LINE()
1696 BENCH_BASE(folly_rwspin_ping_pong, burn0, 1, 0)
1697 BENCH_REL (shmtx_w_bare_ping_pong, burn0, 1, 0)
1698 BENCH_REL (shmtx_r_bare_ping_pong, burn0, 1, 0)
1699 BENCH_REL (folly_ticket_ping_pong, burn0, 1, 0)
1700 BENCH_REL (boost_shared_ping_pong, burn0, 1, 0)
1701 BENCH_REL (pthrd_rwlock_ping_pong, burn0, 1, 0)
1702 BENCHMARK_DRAW_LINE()
1703 BENCH_BASE(folly_rwspin_ping_pong, burn100k, 100, 100000)
1704 BENCH_REL (shmtx_w_bare_ping_pong, burn100k, 100, 100000)
1705 BENCH_REL (shmtx_r_bare_ping_pong, burn100k, 100, 100000)
1706 BENCH_REL (folly_ticket_ping_pong, burn100k, 100, 100000)
1707 BENCH_REL (boost_shared_ping_pong, burn100k, 100, 100000)
1708 BENCH_REL (pthrd_rwlock_ping_pong, burn100k, 100, 100000)
1709 BENCHMARK_DRAW_LINE()
1710 BENCH_BASE(folly_rwspin_ping_pong, burn300k, 100, 300000)
1711 BENCH_REL (shmtx_w_bare_ping_pong, burn300k, 100, 300000)
1712 BENCH_REL (shmtx_r_bare_ping_pong, burn300k, 100, 300000)
1713 BENCH_REL (folly_ticket_ping_pong, burn300k, 100, 300000)
1714 BENCH_REL (boost_shared_ping_pong, burn300k, 100, 300000)
1715 BENCH_REL (pthrd_rwlock_ping_pong, burn300k, 100, 300000)
1716 BENCHMARK_DRAW_LINE()
1717 BENCH_BASE(folly_rwspin_ping_pong, burn1M, 1000, 1000000)
1718 BENCH_REL (shmtx_w_bare_ping_pong, burn1M, 1000, 1000000)
1719 BENCH_REL (shmtx_r_bare_ping_pong, burn1M, 1000, 1000000)
1720 BENCH_REL (folly_ticket_ping_pong, burn1M, 1000, 1000000)
1721 BENCH_REL (boost_shared_ping_pong, burn1M, 1000, 1000000)
1722 BENCH_REL (pthrd_rwlock_ping_pong, burn1M, 1000, 1000000)
1724 // Reproduce with 10 minutes and
1726 // shared_mutex_test --benchmark --bm_min_iters=1000000
1728 // Comparison use folly::RWSpinLock as the baseline, with the
1729 // following row being the default SharedMutex (using *Holder or
1730 // Token-ful methods).
1732 // Following results on 2-socket Intel(R) Xeon(R) CPU E5-2660 0 @ 2.20GHz
1734 // ============================================================================
1735 // folly/test/SharedMutexTest.cpp relative time/iter iters/s
1736 // ============================================================================
1737 // single_thread_lock_shared_unlock_shared 25.17ns 39.74M
1738 // single_thread_lock_unlock 25.88ns 38.64M
1739 // ----------------------------------------------------------------------------
1740 // ----------------------------------------------------------------------------
1741 // folly_rwspin_reads(1thread) 15.16ns 65.95M
1742 // shmtx_wr_pri_reads(1thread) 69.18% 21.92ns 45.63M
1743 // shmtx_w_bare_reads(1thread) 56.07% 27.04ns 36.98M
1744 // shmtx_rd_pri_reads(1thread) 69.06% 21.95ns 45.55M
1745 // shmtx_r_bare_reads(1thread) 56.36% 26.90ns 37.17M
1746 // folly_ticket_reads(1thread) 57.56% 26.34ns 37.96M
1747 // boost_shared_reads(1thread) 10.55% 143.72ns 6.96M
1748 // pthrd_rwlock_reads(1thread) 39.61% 38.28ns 26.12M
1749 // ----------------------------------------------------------------------------
1750 // folly_rwspin_reads(2thread) 45.05ns 22.20M
1751 // shmtx_wr_pri_reads(2thread) 379.98% 11.86ns 84.34M
1752 // shmtx_w_bare_reads(2thread) 319.27% 14.11ns 70.87M
1753 // shmtx_rd_pri_reads(2thread) 385.59% 11.68ns 85.59M
1754 // shmtx_r_bare_reads(2thread) 306.56% 14.70ns 68.04M
1755 // folly_ticket_reads(2thread) 61.07% 73.78ns 13.55M
1756 // boost_shared_reads(2thread) 13.54% 332.66ns 3.01M
1757 // pthrd_rwlock_reads(2thread) 34.22% 131.65ns 7.60M
1758 // ----------------------------------------------------------------------------
1759 // folly_rwspin_reads(4thread) 62.19ns 16.08M
1760 // shmtx_wr_pri_reads(4thread) 1022.82% 6.08ns 164.48M
1761 // shmtx_w_bare_reads(4thread) 875.37% 7.10ns 140.76M
1762 // shmtx_rd_pri_reads(4thread) 1060.46% 5.86ns 170.53M
1763 // shmtx_r_bare_reads(4thread) 879.88% 7.07ns 141.49M
1764 // folly_ticket_reads(4thread) 64.62% 96.23ns 10.39M
1765 // boost_shared_reads(4thread) 14.86% 418.49ns 2.39M
1766 // pthrd_rwlock_reads(4thread) 25.01% 248.65ns 4.02M
1767 // ----------------------------------------------------------------------------
1768 // folly_rwspin_reads(8thread) 64.09ns 15.60M
1769 // shmtx_wr_pri_reads(8thread) 2191.99% 2.92ns 342.03M
1770 // shmtx_w_bare_reads(8thread) 1804.92% 3.55ns 281.63M
1771 // shmtx_rd_pri_reads(8thread) 2194.60% 2.92ns 342.44M
1772 // shmtx_r_bare_reads(8thread) 1800.53% 3.56ns 280.95M
1773 // folly_ticket_reads(8thread) 54.90% 116.74ns 8.57M
1774 // boost_shared_reads(8thread) 18.25% 351.24ns 2.85M
1775 // pthrd_rwlock_reads(8thread) 28.19% 227.31ns 4.40M
1776 // ----------------------------------------------------------------------------
1777 // folly_rwspin_reads(16thread) 70.06ns 14.27M
1778 // shmtx_wr_pri_reads(16thread) 4970.09% 1.41ns 709.38M
1779 // shmtx_w_bare_reads(16thread) 4143.75% 1.69ns 591.44M
1780 // shmtx_rd_pri_reads(16thread) 5009.31% 1.40ns 714.98M
1781 // shmtx_r_bare_reads(16thread) 4067.36% 1.72ns 580.54M
1782 // folly_ticket_reads(16thread) 46.78% 149.77ns 6.68M
1783 // boost_shared_reads(16thread) 21.67% 323.37ns 3.09M
1784 // pthrd_rwlock_reads(16thread) 35.05% 199.90ns 5.00M
1785 // ----------------------------------------------------------------------------
1786 // folly_rwspin_reads(32thread) 58.83ns 17.00M
1787 // shmtx_wr_pri_reads(32thread) 5158.37% 1.14ns 876.79M
1788 // shmtx_w_bare_reads(32thread) 4246.03% 1.39ns 721.72M
1789 // shmtx_rd_pri_reads(32thread) 4845.97% 1.21ns 823.69M
1790 // shmtx_r_bare_reads(32thread) 4721.44% 1.25ns 802.52M
1791 // folly_ticket_reads(32thread) 28.40% 207.15ns 4.83M
1792 // boost_shared_reads(32thread) 17.08% 344.54ns 2.90M
1793 // pthrd_rwlock_reads(32thread) 30.01% 196.02ns 5.10M
1794 // ----------------------------------------------------------------------------
1795 // folly_rwspin_reads(64thread) 59.19ns 16.89M
1796 // shmtx_wr_pri_reads(64thread) 3804.54% 1.56ns 642.76M
1797 // shmtx_w_bare_reads(64thread) 3625.06% 1.63ns 612.43M
1798 // shmtx_rd_pri_reads(64thread) 3418.19% 1.73ns 577.48M
1799 // shmtx_r_bare_reads(64thread) 3416.98% 1.73ns 577.28M
1800 // folly_ticket_reads(64thread) 30.53% 193.90ns 5.16M
1801 // boost_shared_reads(64thread) 18.59% 318.47ns 3.14M
1802 // pthrd_rwlock_reads(64thread) 31.35% 188.81ns 5.30M
1803 // ----------------------------------------------------------------------------
1804 // ----------------------------------------------------------------------------
1805 // folly_rwspin(1thread_all_write) 23.77ns 42.06M
1806 // shmtx_wr_pri(1thread_all_write) 85.09% 27.94ns 35.79M
1807 // shmtx_rd_pri(1thread_all_write) 85.32% 27.87ns 35.89M
1808 // folly_ticket(1thread_all_write) 88.11% 26.98ns 37.06M
1809 // boost_shared(1thread_all_write) 16.49% 144.14ns 6.94M
1810 // pthrd_rwlock(1thread_all_write) 53.99% 44.04ns 22.71M
1811 // pthrd_mutex_(1thread_all_write) 86.05% 27.63ns 36.20M
1812 // ----------------------------------------------------------------------------
1813 // folly_rwspin(2thread_all_write) 76.05ns 13.15M
1814 // shmtx_wr_pri(2thread_all_write) 60.67% 125.35ns 7.98M
1815 // shmtx_rd_pri(2thread_all_write) 60.36% 125.99ns 7.94M
1816 // folly_ticket(2thread_all_write) 129.10% 58.91ns 16.98M
1817 // boost_shared(2thread_all_write) 18.65% 407.74ns 2.45M
1818 // pthrd_rwlock(2thread_all_write) 40.90% 185.92ns 5.38M
1819 // pthrd_mutex_(2thread_all_write) 127.37% 59.71ns 16.75M
1820 // ----------------------------------------------------------------------------
1821 // folly_rwspin(4thread_all_write) 207.17ns 4.83M
1822 // shmtx_wr_pri(4thread_all_write) 119.42% 173.49ns 5.76M
1823 // shmtx_rd_pri(4thread_all_write) 117.68% 176.05ns 5.68M
1824 // folly_ticket(4thread_all_write) 182.39% 113.59ns 8.80M
1825 // boost_shared(4thread_all_write) 11.98% 1.73us 578.46K
1826 // pthrd_rwlock(4thread_all_write) 27.50% 753.25ns 1.33M
1827 // pthrd_mutex_(4thread_all_write) 117.75% 175.95ns 5.68M
1828 // ----------------------------------------------------------------------------
1829 // folly_rwspin(8thread_all_write) 326.50ns 3.06M
1830 // shmtx_wr_pri(8thread_all_write) 125.47% 260.22ns 3.84M
1831 // shmtx_rd_pri(8thread_all_write) 124.73% 261.76ns 3.82M
1832 // folly_ticket(8thread_all_write) 253.39% 128.85ns 7.76M
1833 // boost_shared(8thread_all_write) 6.36% 5.13us 194.87K
1834 // pthrd_rwlock(8thread_all_write) 38.54% 847.09ns 1.18M
1835 // pthrd_mutex_(8thread_all_write) 166.31% 196.32ns 5.09M
1836 // ----------------------------------------------------------------------------
1837 // folly_rwspin(16thread_all_write) 729.89ns 1.37M
1838 // shmtx_wr_pri(16thread_all_write) 219.91% 331.91ns 3.01M
1839 // shmtx_rd_pri(16thread_all_write) 220.09% 331.62ns 3.02M
1840 // folly_ticket(16thread_all_write) 390.06% 187.12ns 5.34M
1841 // boost_shared(16thread_all_write) 10.27% 7.11us 140.72K
1842 // pthrd_rwlock(16thread_all_write) 113.90% 640.84ns 1.56M
1843 // pthrd_mutex_(16thread_all_write) 401.97% 181.58ns 5.51M
1844 // ----------------------------------------------------------------------------
1845 // folly_rwspin(32thread_all_write) 1.55us 645.01K
1846 // shmtx_wr_pri(32thread_all_write) 415.05% 373.54ns 2.68M
1847 // shmtx_rd_pri(32thread_all_write) 258.45% 599.88ns 1.67M
1848 // folly_ticket(32thread_all_write) 525.40% 295.09ns 3.39M
1849 // boost_shared(32thread_all_write) 20.84% 7.44us 134.45K
1850 // pthrd_rwlock(32thread_all_write) 254.16% 610.00ns 1.64M
1851 // pthrd_mutex_(32thread_all_write) 852.51% 181.86ns 5.50M
1852 // ----------------------------------------------------------------------------
1853 // folly_rwspin(64thread_all_write) 2.03us 492.00K
1854 // shmtx_wr_pri(64thread_all_write) 517.65% 392.64ns 2.55M
1855 // shmtx_rd_pri(64thread_all_write) 288.20% 705.24ns 1.42M
1856 // folly_ticket(64thread_all_write) 638.22% 318.47ns 3.14M
1857 // boost_shared(64thread_all_write) 27.56% 7.37us 135.61K
1858 // pthrd_rwlock(64thread_all_write) 326.75% 622.04ns 1.61M
1859 // pthrd_mutex_(64thread_all_write) 1231.57% 165.04ns 6.06M
1860 // ----------------------------------------------------------------------------
1861 // ----------------------------------------------------------------------------
1862 // folly_rwspin(1thread_10pct_write) 19.39ns 51.58M
1863 // shmtx_wr_pri(1thread_10pct_write) 93.87% 20.65ns 48.42M
1864 // shmtx_rd_pri(1thread_10pct_write) 93.60% 20.71ns 48.28M
1865 // folly_ticket(1thread_10pct_write) 73.75% 26.29ns 38.04M
1866 // boost_shared(1thread_10pct_write) 12.97% 149.53ns 6.69M
1867 // pthrd_rwlock(1thread_10pct_write) 44.15% 43.92ns 22.77M
1868 // ----------------------------------------------------------------------------
1869 // folly_rwspin(2thread_10pct_write) 227.88ns 4.39M
1870 // shmtx_wr_pri(2thread_10pct_write) 321.08% 70.98ns 14.09M
1871 // shmtx_rd_pri(2thread_10pct_write) 280.65% 81.20ns 12.32M
1872 // folly_ticket(2thread_10pct_write) 220.43% 103.38ns 9.67M
1873 // boost_shared(2thread_10pct_write) 58.78% 387.71ns 2.58M
1874 // pthrd_rwlock(2thread_10pct_write) 112.68% 202.23ns 4.94M
1875 // ----------------------------------------------------------------------------
1876 // folly_rwspin(4thread_10pct_write) 444.94ns 2.25M
1877 // shmtx_wr_pri(4thread_10pct_write) 470.35% 94.60ns 10.57M
1878 // shmtx_rd_pri(4thread_10pct_write) 349.08% 127.46ns 7.85M
1879 // folly_ticket(4thread_10pct_write) 305.64% 145.58ns 6.87M
1880 // boost_shared(4thread_10pct_write) 44.43% 1.00us 998.57K
1881 // pthrd_rwlock(4thread_10pct_write) 100.59% 442.31ns 2.26M
1882 // ----------------------------------------------------------------------------
1883 // folly_rwspin(8thread_10pct_write) 424.67ns 2.35M
1884 // shmtx_wr_pri(8thread_10pct_write) 337.53% 125.82ns 7.95M
1885 // shmtx_rd_pri(8thread_10pct_write) 232.32% 182.79ns 5.47M
1886 // folly_ticket(8thread_10pct_write) 206.59% 205.56ns 4.86M
1887 // boost_shared(8thread_10pct_write) 19.45% 2.18us 457.90K
1888 // pthrd_rwlock(8thread_10pct_write) 78.58% 540.42ns 1.85M
1889 // ----------------------------------------------------------------------------
1890 // folly_rwspin(16thread_10pct_write) 727.04ns 1.38M
1891 // shmtx_wr_pri(16thread_10pct_write) 400.60% 181.49ns 5.51M
1892 // shmtx_rd_pri(16thread_10pct_write) 312.94% 232.33ns 4.30M
1893 // folly_ticket(16thread_10pct_write) 283.67% 256.30ns 3.90M
1894 // boost_shared(16thread_10pct_write) 15.87% 4.58us 218.32K
1895 // pthrd_rwlock(16thread_10pct_write) 131.28% 553.82ns 1.81M
1896 // ----------------------------------------------------------------------------
1897 // folly_rwspin(32thread_10pct_write) 810.61ns 1.23M
1898 // shmtx_wr_pri(32thread_10pct_write) 429.61% 188.68ns 5.30M
1899 // shmtx_rd_pri(32thread_10pct_write) 321.13% 252.42ns 3.96M
1900 // folly_ticket(32thread_10pct_write) 247.65% 327.32ns 3.06M
1901 // boost_shared(32thread_10pct_write) 8.34% 9.71us 102.94K
1902 // pthrd_rwlock(32thread_10pct_write) 144.28% 561.85ns 1.78M
1903 // ----------------------------------------------------------------------------
1904 // folly_rwspin(64thread_10pct_write) 1.10us 912.30K
1905 // shmtx_wr_pri(64thread_10pct_write) 486.68% 225.22ns 4.44M
1906 // shmtx_rd_pri(64thread_10pct_write) 412.96% 265.43ns 3.77M
1907 // folly_ticket(64thread_10pct_write) 280.23% 391.15ns 2.56M
1908 // boost_shared(64thread_10pct_write) 6.16% 17.79us 56.22K
1909 // pthrd_rwlock(64thread_10pct_write) 198.81% 551.34ns 1.81M
1910 // ----------------------------------------------------------------------------
1911 // ----------------------------------------------------------------------------
1912 // folly_rwspin(1thread_1pct_write) 19.02ns 52.57M
1913 // shmtx_wr_pri(1thread_1pct_write) 94.46% 20.14ns 49.66M
1914 // shmtx_w_bare(1thread_1pct_write) 76.60% 24.83ns 40.27M
1915 // shmtx_rd_pri(1thread_1pct_write) 93.83% 20.27ns 49.33M
1916 // shmtx_r_bare(1thread_1pct_write) 77.04% 24.69ns 40.50M
1917 // folly_ticket(1thread_1pct_write) 72.83% 26.12ns 38.29M
1918 // boost_shared(1thread_1pct_write) 12.48% 152.44ns 6.56M
1919 // pthrd_rwlock(1thread_1pct_write) 42.85% 44.39ns 22.53M
1920 // ----------------------------------------------------------------------------
1921 // folly_rwspin(2thread_1pct_write) 110.63ns 9.04M
1922 // shmtx_wr_pri(2thread_1pct_write) 442.12% 25.02ns 39.96M
1923 // shmtx_w_bare(2thread_1pct_write) 374.65% 29.53ns 33.86M
1924 // shmtx_rd_pri(2thread_1pct_write) 371.08% 29.81ns 33.54M
1925 // shmtx_r_bare(2thread_1pct_write) 138.02% 80.15ns 12.48M
1926 // folly_ticket(2thread_1pct_write) 131.34% 84.23ns 11.87M
1927 // boost_shared(2thread_1pct_write) 30.35% 364.58ns 2.74M
1928 // pthrd_rwlock(2thread_1pct_write) 95.48% 115.87ns 8.63M
1929 // ----------------------------------------------------------------------------
1930 // folly_rwspin(4thread_1pct_write) 140.62ns 7.11M
1931 // shmtx_wr_pri(4thread_1pct_write) 627.13% 22.42ns 44.60M
1932 // shmtx_w_bare(4thread_1pct_write) 552.94% 25.43ns 39.32M
1933 // shmtx_rd_pri(4thread_1pct_write) 226.06% 62.21ns 16.08M
1934 // shmtx_r_bare(4thread_1pct_write) 77.61% 181.19ns 5.52M
1935 // folly_ticket(4thread_1pct_write) 119.58% 117.60ns 8.50M
1936 // boost_shared(4thread_1pct_write) 25.36% 554.54ns 1.80M
1937 // pthrd_rwlock(4thread_1pct_write) 45.55% 308.72ns 3.24M
1938 // ----------------------------------------------------------------------------
1939 // folly_rwspin(8thread_1pct_write) 166.23ns 6.02M
1940 // shmtx_wr_pri(8thread_1pct_write) 687.09% 24.19ns 41.33M
1941 // shmtx_w_bare(8thread_1pct_write) 611.80% 27.17ns 36.80M
1942 // shmtx_rd_pri(8thread_1pct_write) 140.37% 118.43ns 8.44M
1943 // shmtx_r_bare(8thread_1pct_write) 80.32% 206.97ns 4.83M
1944 // folly_ticket(8thread_1pct_write) 117.06% 142.01ns 7.04M
1945 // boost_shared(8thread_1pct_write) 22.29% 745.67ns 1.34M
1946 // pthrd_rwlock(8thread_1pct_write) 49.84% 333.55ns 3.00M
1947 // ----------------------------------------------------------------------------
1948 // folly_rwspin(16thread_1pct_write) 419.79ns 2.38M
1949 // shmtx_wr_pri(16thread_1pct_write) 1397.92% 30.03ns 33.30M
1950 // shmtx_w_bare(16thread_1pct_write) 1324.60% 31.69ns 31.55M
1951 // shmtx_rd_pri(16thread_1pct_write) 278.12% 150.94ns 6.63M
1952 // shmtx_r_bare(16thread_1pct_write) 194.25% 216.11ns 4.63M
1953 // folly_ticket(16thread_1pct_write) 255.38% 164.38ns 6.08M
1954 // boost_shared(16thread_1pct_write) 33.71% 1.25us 803.01K
1955 // pthrd_rwlock(16thread_1pct_write) 131.96% 318.12ns 3.14M
1956 // ----------------------------------------------------------------------------
1957 // folly_rwspin(32thread_1pct_write) 395.99ns 2.53M
1958 // shmtx_wr_pri(32thread_1pct_write) 1332.76% 29.71ns 33.66M
1959 // shmtx_w_bare(32thread_1pct_write) 1208.86% 32.76ns 30.53M
1960 // shmtx_rd_pri(32thread_1pct_write) 252.97% 156.54ns 6.39M
1961 // shmtx_r_bare(32thread_1pct_write) 193.79% 204.35ns 4.89M
1962 // folly_ticket(32thread_1pct_write) 173.16% 228.69ns 4.37M
1963 // boost_shared(32thread_1pct_write) 17.00% 2.33us 429.40K
1964 // pthrd_rwlock(32thread_1pct_write) 129.88% 304.89ns 3.28M
1965 // ----------------------------------------------------------------------------
1966 // folly_rwspin(64thread_1pct_write) 424.07ns 2.36M
1967 // shmtx_wr_pri(64thread_1pct_write) 1297.89% 32.67ns 30.61M
1968 // shmtx_w_bare(64thread_1pct_write) 1228.88% 34.51ns 28.98M
1969 // shmtx_rd_pri(64thread_1pct_write) 270.40% 156.83ns 6.38M
1970 // shmtx_r_bare(64thread_1pct_write) 218.05% 194.48ns 5.14M
1971 // folly_ticket(64thread_1pct_write) 171.44% 247.36ns 4.04M
1972 // boost_shared(64thread_1pct_write) 10.60% 4.00us 249.95K
1973 // pthrd_rwlock(64thread_1pct_write) 143.80% 294.91ns 3.39M
1974 // ----------------------------------------------------------------------------
1975 // folly_rwspin(2thr_2lock_50pct_write) 10.87ns 91.99M
1976 // shmtx_wr_pri(2thr_2lock_50pct_write) 83.71% 12.99ns 77.01M
1977 // shmtx_rd_pri(2thr_2lock_50pct_write) 84.08% 12.93ns 77.34M
1978 // folly_rwspin(4thr_4lock_50pct_write) 5.32ns 188.12M
1979 // shmtx_wr_pri(4thr_4lock_50pct_write) 82.21% 6.47ns 154.65M
1980 // shmtx_rd_pri(4thr_4lock_50pct_write) 81.20% 6.55ns 152.75M
1981 // folly_rwspin(8thr_8lock_50pct_write) 2.64ns 379.06M
1982 // shmtx_wr_pri(8thr_8lock_50pct_write) 81.26% 3.25ns 308.03M
1983 // shmtx_rd_pri(8thr_8lock_50pct_write) 80.95% 3.26ns 306.86M
1984 // folly_rwspin(16thr_16lock_50pct_write) 1.52ns 656.77M
1985 // shmtx_wr_pri(16thr_16lock_50pct_write) 86.24% 1.77ns 566.41M
1986 // shmtx_rd_pri(16thr_16lock_50pct_write) 83.72% 1.82ns 549.82M
1987 // folly_rwspin(32thr_32lock_50pct_write) 1.19ns 841.03M
1988 // shmtx_wr_pri(32thr_32lock_50pct_write) 85.08% 1.40ns 715.55M
1989 // shmtx_rd_pri(32thr_32lock_50pct_write) 86.44% 1.38ns 727.00M
1990 // folly_rwspin(64thr_64lock_50pct_write) 1.46ns 684.28M
1991 // shmtx_wr_pri(64thr_64lock_50pct_write) 84.53% 1.73ns 578.43M
1992 // shmtx_rd_pri(64thr_64lock_50pct_write) 82.80% 1.76ns 566.58M
1993 // ----------------------------------------------------------------------------
1994 // folly_rwspin(2thr_2lock_10pct_write) 10.01ns 99.85M
1995 // shmtx_wr_pri(2thr_2lock_10pct_write) 92.02% 10.88ns 91.88M
1996 // shmtx_rd_pri(2thr_2lock_10pct_write) 92.35% 10.84ns 92.22M
1997 // folly_rwspin(4thr_4lock_10pct_write) 4.81ns 207.87M
1998 // shmtx_wr_pri(4thr_4lock_10pct_write) 89.32% 5.39ns 185.67M
1999 // shmtx_rd_pri(4thr_4lock_10pct_write) 88.96% 5.41ns 184.93M
2000 // folly_rwspin(8thr_8lock_10pct_write) 2.39ns 417.62M
2001 // shmtx_wr_pri(8thr_8lock_10pct_write) 91.17% 2.63ns 380.76M
2002 // shmtx_rd_pri(8thr_8lock_10pct_write) 89.53% 2.67ns 373.92M
2003 // folly_rwspin(16thr_16lock_10pct_write) 1.16ns 860.47M
2004 // shmtx_wr_pri(16thr_16lock_10pct_write) 74.35% 1.56ns 639.77M
2005 // shmtx_rd_pri(16thr_16lock_10pct_write) 91.34% 1.27ns 785.97M
2006 // folly_rwspin(32thr_32lock_10pct_write) 1.15ns 866.23M
2007 // shmtx_wr_pri(32thr_32lock_10pct_write) 92.32% 1.25ns 799.72M
2008 // shmtx_rd_pri(32thr_32lock_10pct_write) 94.40% 1.22ns 817.71M
2009 // folly_rwspin(64thr_64lock_10pct_write) 1.41ns 710.54M
2010 // shmtx_wr_pri(64thr_64lock_10pct_write) 94.14% 1.50ns 668.88M
2011 // shmtx_rd_pri(64thr_64lock_10pct_write) 94.80% 1.48ns 673.56M
2012 // ----------------------------------------------------------------------------
2013 // folly_rwspin(2thr_2lock_1pct_write) 9.58ns 104.36M
2014 // shmtx_wr_pri(2thr_2lock_1pct_write) 92.00% 10.42ns 96.01M
2015 // shmtx_rd_pri(2thr_2lock_1pct_write) 91.79% 10.44ns 95.79M
2016 // folly_rwspin(4thr_4lock_1pct_write) 4.71ns 212.30M
2017 // shmtx_wr_pri(4thr_4lock_1pct_write) 90.37% 5.21ns 191.85M
2018 // shmtx_rd_pri(4thr_4lock_1pct_write) 89.94% 5.24ns 190.95M
2019 // folly_rwspin(8thr_8lock_1pct_write) 2.33ns 429.91M
2020 // shmtx_wr_pri(8thr_8lock_1pct_write) 90.67% 2.57ns 389.80M
2021 // shmtx_rd_pri(8thr_8lock_1pct_write) 90.61% 2.57ns 389.55M
2022 // folly_rwspin(16thr_16lock_1pct_write) 1.10ns 905.23M
2023 // shmtx_wr_pri(16thr_16lock_1pct_write) 91.96% 1.20ns 832.46M
2024 // shmtx_rd_pri(16thr_16lock_1pct_write) 92.29% 1.20ns 835.42M
2025 // folly_rwspin(32thr_32lock_1pct_write) 1.14ns 879.85M
2026 // shmtx_wr_pri(32thr_32lock_1pct_write) 93.41% 1.22ns 821.86M
2027 // shmtx_rd_pri(32thr_32lock_1pct_write) 94.18% 1.21ns 828.66M
2028 // folly_rwspin(64thr_64lock_1pct_write) 1.34ns 748.83M
2029 // shmtx_wr_pri(64thr_64lock_1pct_write) 94.39% 1.41ns 706.84M
2030 // shmtx_rd_pri(64thr_64lock_1pct_write) 94.02% 1.42ns 704.06M
2031 // ----------------------------------------------------------------------------
2032 // ----------------------------------------------------------------------------
2033 // folly_rwspin_ping_pong(burn0) 605.63ns 1.65M
2034 // shmtx_w_bare_ping_pong(burn0) 102.17% 592.76ns 1.69M
2035 // shmtx_r_bare_ping_pong(burn0) 88.75% 682.44ns 1.47M
2036 // folly_ticket_ping_pong(burn0) 63.92% 947.56ns 1.06M
2037 // boost_shared_ping_pong(burn0) 8.52% 7.11us 140.73K
2038 // pthrd_rwlock_ping_pong(burn0) 7.88% 7.68us 130.15K
2039 // ----------------------------------------------------------------------------
2040 // folly_rwspin_ping_pong(burn100k) 727.76ns 1.37M
2041 // shmtx_w_bare_ping_pong(burn100k) 100.79% 722.09ns 1.38M
2042 // shmtx_r_bare_ping_pong(burn100k) 101.98% 713.61ns 1.40M
2043 // folly_ticket_ping_pong(burn100k) 102.80% 707.95ns 1.41M
2044 // boost_shared_ping_pong(burn100k) 81.49% 893.02ns 1.12M
2045 // pthrd_rwlock_ping_pong(burn100k) 71.05% 1.02us 976.30K
2046 // ----------------------------------------------------------------------------
2047 // folly_rwspin_ping_pong(burn300k) 2.11us 473.46K
2048 // shmtx_w_bare_ping_pong(burn300k) 100.06% 2.11us 473.72K
2049 // shmtx_r_bare_ping_pong(burn300k) 98.93% 2.13us 468.39K
2050 // folly_ticket_ping_pong(burn300k) 96.68% 2.18us 457.73K
2051 // boost_shared_ping_pong(burn300k) 84.72% 2.49us 401.13K
2052 // pthrd_rwlock_ping_pong(burn300k) 84.62% 2.50us 400.66K
2053 // ----------------------------------------------------------------------------
2054 // folly_rwspin_ping_pong(burn1M) 709.70ns 1.41M
2055 // shmtx_w_bare_ping_pong(burn1M) 100.28% 707.73ns 1.41M
2056 // shmtx_r_bare_ping_pong(burn1M) 99.63% 712.37ns 1.40M
2057 // folly_ticket_ping_pong(burn1M) 100.09% 709.05ns 1.41M
2058 // boost_shared_ping_pong(burn1M) 94.09% 754.29ns 1.33M
2059 // pthrd_rwlock_ping_pong(burn1M) 96.32% 736.82ns 1.36M
2060 // ============================================================================
2062 int main(int argc, char** argv) {
2063 (void)folly_rwspin_reads;
2064 (void)shmtx_wr_pri_reads;
2065 (void)shmtx_w_bare_reads;
2066 (void)shmtx_rd_pri_reads;
2067 (void)shmtx_r_bare_reads;
2068 (void)folly_ticket_reads;
2069 (void)boost_shared_reads;
2070 (void)pthrd_rwlock_reads;
2080 (void)folly_rwspin_ping_pong;
2081 (void)shmtx_w_bare_ping_pong;
2082 (void)shmtx_r_bare_ping_pong;
2083 (void)folly_ticket_ping_pong;
2084 (void)boost_shared_ping_pong;
2085 (void)pthrd_rwlock_ping_pong;
2087 testing::InitGoogleTest(&argc, argv);
2088 gflags::ParseCommandLineFlags(&argc, &argv, true);
2089 int rv = RUN_ALL_TESTS();
2090 folly::runBenchmarksOnFlag();