2 * Copyright 2016 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.
17 #include <folly/SharedMutex.h>
23 #include <gtest/gtest.h>
24 #include <boost/optional.hpp>
25 #include <boost/thread/shared_mutex.hpp>
27 #include <folly/Benchmark.h>
28 #include <folly/MPMCQueue.h>
29 #include <folly/RWSpinLock.h>
30 #include <folly/Random.h>
31 #include <folly/portability/GFlags.h>
32 #include <folly/test/DeterministicSchedule.h>
34 using namespace folly;
35 using namespace folly::test;
37 using namespace chrono;
39 typedef DeterministicSchedule DSched;
40 typedef SharedMutexImpl<true, void, DeterministicAtomic, true>
41 DSharedMutexReadPriority;
42 typedef SharedMutexImpl<false, void, DeterministicAtomic, true>
43 DSharedMutexWritePriority;
45 COMMON_CONCURRENCY_SHARED_MUTEX_DECLARE_STATIC_STORAGE(
46 DSharedMutexReadPriority);
47 COMMON_CONCURRENCY_SHARED_MUTEX_DECLARE_STATIC_STORAGE(
48 DSharedMutexWritePriority);
50 template <typename Lock>
53 SharedMutexToken token1;
54 SharedMutexToken token2;
55 SharedMutexToken token3;
57 EXPECT_TRUE(lock.try_lock());
58 EXPECT_FALSE(lock.try_lock());
59 EXPECT_FALSE(lock.try_lock_shared(token1));
62 EXPECT_TRUE(lock.try_lock_shared(token1));
63 EXPECT_FALSE(lock.try_lock());
64 EXPECT_TRUE(lock.try_lock_shared(token2));
65 lock.lock_shared(token3);
66 lock.unlock_shared(token3);
67 lock.unlock_shared(token2);
68 lock.unlock_shared(token1);
73 lock.lock_shared(token1);
74 lock.lock_shared(token2);
75 lock.unlock_shared(token1);
76 lock.unlock_shared(token2);
79 lock.unlock_and_lock_shared(token1);
80 lock.lock_shared(token2);
81 lock.unlock_shared(token2);
82 lock.unlock_shared(token1);
85 TEST(SharedMutex, basic) {
86 runBasicTest<SharedMutexReadPriority>();
87 runBasicTest<SharedMutexWritePriority>();
90 template <typename Lock>
91 void runBasicHoldersTest() {
93 SharedMutexToken token;
96 // create an exclusive write lock via holder
97 typename Lock::WriteHolder holder(lock);
98 EXPECT_FALSE(lock.try_lock());
99 EXPECT_FALSE(lock.try_lock_shared(token));
101 // move ownership to another write holder via move constructor
102 typename Lock::WriteHolder holder2(std::move(holder));
103 EXPECT_FALSE(lock.try_lock());
104 EXPECT_FALSE(lock.try_lock_shared(token));
106 // move ownership to another write holder via assign operator
107 typename Lock::WriteHolder holder3;
108 holder3 = std::move(holder2);
109 EXPECT_FALSE(lock.try_lock());
110 EXPECT_FALSE(lock.try_lock_shared(token));
112 // downgrade from exclusive to upgrade lock via move constructor
113 typename Lock::UpgradeHolder holder4(std::move(holder3));
115 // ensure we can lock from a shared source
116 EXPECT_FALSE(lock.try_lock());
117 EXPECT_TRUE(lock.try_lock_shared(token));
118 lock.unlock_shared(token);
120 // promote from upgrade to exclusive lock via move constructor
121 typename Lock::WriteHolder holder5(std::move(holder4));
122 EXPECT_FALSE(lock.try_lock());
123 EXPECT_FALSE(lock.try_lock_shared(token));
125 // downgrade exclusive to shared lock via move constructor
126 typename Lock::ReadHolder holder6(std::move(holder5));
128 // ensure we can lock from another shared source
129 EXPECT_FALSE(lock.try_lock());
130 EXPECT_TRUE(lock.try_lock_shared(token));
131 lock.unlock_shared(token);
135 typename Lock::WriteHolder holder(lock);
136 EXPECT_FALSE(lock.try_lock());
140 typename Lock::ReadHolder holder(lock);
141 typename Lock::ReadHolder holder2(lock);
142 typename Lock::UpgradeHolder holder3(lock);
146 typename Lock::UpgradeHolder holder(lock);
147 typename Lock::ReadHolder holder2(lock);
148 typename Lock::ReadHolder holder3(std::move(holder));
152 TEST(SharedMutex, basic_holders) {
153 runBasicHoldersTest<SharedMutexReadPriority>();
154 runBasicHoldersTest<SharedMutexWritePriority>();
157 template <typename Lock>
158 void runManyReadLocksTestWithTokens() {
161 vector<SharedMutexToken> tokens;
162 for (int i = 0; i < 1000; ++i) {
163 tokens.emplace_back();
164 EXPECT_TRUE(lock.try_lock_shared(tokens.back()));
166 for (auto& token : tokens) {
167 lock.unlock_shared(token);
169 EXPECT_TRUE(lock.try_lock());
173 TEST(SharedMutex, many_read_locks_with_tokens) {
174 runManyReadLocksTestWithTokens<SharedMutexReadPriority>();
175 runManyReadLocksTestWithTokens<SharedMutexWritePriority>();
178 template <typename Lock>
179 void runManyReadLocksTestWithoutTokens() {
182 for (int i = 0; i < 1000; ++i) {
183 EXPECT_TRUE(lock.try_lock_shared());
185 for (int i = 0; i < 1000; ++i) {
186 lock.unlock_shared();
188 EXPECT_TRUE(lock.try_lock());
192 TEST(SharedMutex, many_read_locks_without_tokens) {
193 runManyReadLocksTestWithoutTokens<SharedMutexReadPriority>();
194 runManyReadLocksTestWithoutTokens<SharedMutexWritePriority>();
197 template <typename Lock>
198 void runTimeoutInPastTest() {
201 EXPECT_TRUE(lock.try_lock_for(milliseconds(0)));
203 EXPECT_TRUE(lock.try_lock_for(milliseconds(-1)));
205 EXPECT_TRUE(lock.try_lock_shared_for(milliseconds(0)));
206 lock.unlock_shared();
207 EXPECT_TRUE(lock.try_lock_shared_for(milliseconds(-1)));
208 lock.unlock_shared();
209 EXPECT_TRUE(lock.try_lock_until(system_clock::now() - milliseconds(1)));
212 lock.try_lock_shared_until(system_clock::now() - milliseconds(1)));
213 lock.unlock_shared();
214 EXPECT_TRUE(lock.try_lock_until(steady_clock::now() - milliseconds(1)));
217 lock.try_lock_shared_until(steady_clock::now() - milliseconds(1)));
218 lock.unlock_shared();
221 TEST(SharedMutex, timeout_in_past) {
222 runTimeoutInPastTest<SharedMutexReadPriority>();
223 runTimeoutInPastTest<SharedMutexWritePriority>();
226 template <class Func>
227 bool funcHasDuration(milliseconds expectedDuration, Func func) {
228 // elapsed time should eventually fall within expectedDuration +- 25%
229 for (int tries = 0; tries < 100; ++tries) {
230 auto start = steady_clock::now();
232 auto elapsed = steady_clock::now() - start;
233 if (elapsed > expectedDuration - expectedDuration / 4 &&
234 elapsed < expectedDuration + expectedDuration / 4) {
241 template <typename Lock>
242 void runFailingTryTimeoutTest() {
245 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
246 EXPECT_FALSE(lock.try_lock_for(milliseconds(10)));
248 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
249 typename Lock::Token token;
250 EXPECT_FALSE(lock.try_lock_shared_for(milliseconds(10), token));
252 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
253 EXPECT_FALSE(lock.try_lock_upgrade_for(milliseconds(10)));
255 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
256 EXPECT_FALSE(lock.try_lock_until(steady_clock::now() + milliseconds(10)));
258 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
259 typename Lock::Token token;
260 EXPECT_FALSE(lock.try_lock_shared_until(
261 steady_clock::now() + milliseconds(10), token));
263 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
265 lock.try_lock_upgrade_until(steady_clock::now() + milliseconds(10)));
267 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
268 EXPECT_FALSE(lock.try_lock_until(system_clock::now() + milliseconds(10)));
270 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
271 typename Lock::Token token;
272 EXPECT_FALSE(lock.try_lock_shared_until(
273 system_clock::now() + milliseconds(10), token));
275 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
277 lock.try_lock_upgrade_until(system_clock::now() + milliseconds(10)));
282 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
283 EXPECT_FALSE(lock.try_lock_for(milliseconds(10)));
285 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
286 EXPECT_FALSE(lock.try_lock_until(steady_clock::now() + milliseconds(10)));
288 EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
289 EXPECT_FALSE(lock.try_lock_until(system_clock::now() + milliseconds(10)));
291 lock.unlock_shared();
294 for (int p = 0; p < 8; ++p) {
295 EXPECT_FALSE(lock.try_lock_for(nanoseconds(1 << p)));
299 for (int p = 0; p < 8; ++p) {
300 typename Lock::ReadHolder holder1(lock);
301 typename Lock::ReadHolder holder2(lock);
302 typename Lock::ReadHolder holder3(lock);
303 EXPECT_FALSE(lock.try_lock_for(nanoseconds(1 << p)));
307 TEST(SharedMutex, failing_try_timeout) {
308 runFailingTryTimeoutTest<SharedMutexReadPriority>();
309 runFailingTryTimeoutTest<SharedMutexWritePriority>();
312 template <typename Lock>
313 void runBasicUpgradeTest() {
315 typename Lock::Token token1;
316 typename Lock::Token token2;
319 EXPECT_FALSE(lock.try_lock());
320 EXPECT_TRUE(lock.try_lock_shared(token1));
321 lock.unlock_shared(token1);
322 lock.unlock_upgrade();
325 lock.unlock_upgrade_and_lock();
326 EXPECT_FALSE(lock.try_lock_shared(token1));
330 lock.unlock_upgrade_and_lock_shared(token1);
332 lock.unlock_upgrade_and_lock_shared(token2);
333 lock.unlock_shared(token1);
334 lock.unlock_shared(token2);
337 lock.unlock_and_lock_upgrade();
338 EXPECT_TRUE(lock.try_lock_shared(token1));
339 lock.unlock_upgrade();
340 lock.unlock_shared(token1);
343 TEST(SharedMutex, basic_upgrade_tests) {
344 runBasicUpgradeTest<SharedMutexReadPriority>();
345 runBasicUpgradeTest<SharedMutexWritePriority>();
348 TEST(SharedMutex, read_has_prio) {
349 SharedMutexReadPriority lock;
350 SharedMutexToken token1;
351 SharedMutexToken token2;
352 lock.lock_shared(token1);
353 bool exclusiveAcquired = false;
354 auto writer = thread([&] {
356 exclusiveAcquired = true;
360 // lock() can't complete until we unlock token1, but it should stake
361 // its claim with regards to other exclusive or upgrade locks. We can
362 // use try_lock_upgrade to poll for that eventuality.
363 while (lock.try_lock_upgrade()) {
364 lock.unlock_upgrade();
365 this_thread::yield();
367 EXPECT_FALSE(exclusiveAcquired);
369 // Even though lock() is stuck we should be able to get token2
370 EXPECT_TRUE(lock.try_lock_shared(token2));
371 lock.unlock_shared(token1);
372 lock.unlock_shared(token2);
374 EXPECT_TRUE(exclusiveAcquired);
377 TEST(SharedMutex, write_has_prio) {
378 SharedMutexWritePriority lock;
379 SharedMutexToken token1;
380 SharedMutexToken token2;
381 lock.lock_shared(token1);
382 auto writer = thread([&] {
387 // eventually lock() should block readers
388 while (lock.try_lock_shared(token2)) {
389 lock.unlock_shared(token2);
390 this_thread::yield();
393 lock.unlock_shared(token1);
398 SharedMutexToken token;
400 template <typename T>
405 template <typename T>
406 void unlock(T* lock) {
410 template <typename T>
411 void lock_shared(T* lock) {
412 lock->lock_shared(token);
415 template <typename T>
416 void unlock_shared(T* lock) {
417 lock->unlock_shared(token);
422 template <typename T>
427 template <typename T>
428 void unlock(T* lock) {
432 template <typename T>
433 void lock_shared(T* lock) {
437 template <typename T>
438 void unlock_shared(T* lock) {
439 lock->unlock_shared();
444 template <typename T>
449 template <typename T>
450 void unlock(T* lock) {
454 template <typename T>
455 void lock_shared(T* lock) {
459 template <typename T>
460 void unlock_shared(T* lock) {
466 pthread_rwlock_t lock_;
468 PosixRWLock() { pthread_rwlock_init(&lock_, nullptr); }
470 ~PosixRWLock() { pthread_rwlock_destroy(&lock_); }
472 void lock() { pthread_rwlock_wrlock(&lock_); }
474 void unlock() { pthread_rwlock_unlock(&lock_); }
476 void lock_shared() { pthread_rwlock_rdlock(&lock_); }
478 void unlock_shared() { pthread_rwlock_unlock(&lock_); }
482 pthread_mutex_t lock_;
484 PosixMutex() { pthread_mutex_init(&lock_, nullptr); }
486 ~PosixMutex() { pthread_mutex_destroy(&lock_); }
488 void lock() { pthread_mutex_lock(&lock_); }
490 void unlock() { pthread_mutex_unlock(&lock_); }
492 void lock_shared() { pthread_mutex_lock(&lock_); }
494 void unlock_shared() { pthread_mutex_unlock(&lock_); }
497 template <template <typename> class Atom, typename Lock, typename Locker>
498 static void runContendedReaders(size_t numOps,
500 bool useSeparateLocks) {
504 int valueProtectedByLock = 10;
507 Atom<bool> go(false);
508 Atom<bool>* goPtr = &go; // workaround for clang bug
509 vector<thread> threads(numThreads);
512 for (size_t t = 0; t < numThreads; ++t) {
513 threads[t] = DSched::thread([&, t, numThreads] {
515 Lock* lock = useSeparateLocks ? &privateLock : &globalLock;
517 while (!goPtr->load()) {
518 this_thread::yield();
520 for (size_t op = t; op < numOps; op += numThreads) {
521 locker.lock_shared(lock);
522 // note: folly::doNotOptimizeAway reads and writes to its arg,
523 // so the following two lines are very different than a call
524 // to folly::doNotOptimizeAway(valueProtectedByLock);
525 auto copy = valueProtectedByLock;
526 folly::doNotOptimizeAway(copy);
527 locker.unlock_shared(lock);
534 for (auto& thr : threads) {
539 static void folly_rwspin_reads(uint numOps,
541 bool useSeparateLocks) {
542 runContendedReaders<atomic, RWSpinLock, Locker>(
543 numOps, numThreads, useSeparateLocks);
546 static void shmtx_wr_pri_reads(uint numOps,
548 bool useSeparateLocks) {
549 runContendedReaders<atomic, SharedMutexWritePriority, TokenLocker>(
550 numOps, numThreads, useSeparateLocks);
553 static void shmtx_w_bare_reads(uint numOps,
555 bool useSeparateLocks) {
556 runContendedReaders<atomic, SharedMutexWritePriority, Locker>(
557 numOps, numThreads, useSeparateLocks);
560 static void shmtx_rd_pri_reads(uint numOps,
562 bool useSeparateLocks) {
563 runContendedReaders<atomic, SharedMutexReadPriority, TokenLocker>(
564 numOps, numThreads, useSeparateLocks);
567 static void shmtx_r_bare_reads(uint numOps,
569 bool useSeparateLocks) {
570 runContendedReaders<atomic, SharedMutexReadPriority, Locker>(
571 numOps, numThreads, useSeparateLocks);
574 static void folly_ticket_reads(uint numOps,
576 bool useSeparateLocks) {
577 runContendedReaders<atomic, RWTicketSpinLock64, Locker>(
578 numOps, numThreads, useSeparateLocks);
581 static void boost_shared_reads(uint numOps,
583 bool useSeparateLocks) {
584 runContendedReaders<atomic, boost::shared_mutex, Locker>(
585 numOps, numThreads, useSeparateLocks);
588 static void pthrd_rwlock_reads(uint numOps,
590 bool useSeparateLocks) {
591 runContendedReaders<atomic, PosixRWLock, Locker>(
592 numOps, numThreads, useSeparateLocks);
595 template <template <typename> class Atom, typename Lock, typename Locker>
596 static void runMixed(size_t numOps,
598 double writeFraction,
599 bool useSeparateLocks) {
603 int valueProtectedByLock = 0;
606 Atom<bool> go(false);
607 Atom<bool>* goPtr = &go; // workaround for clang bug
608 vector<thread> threads(numThreads);
611 for (size_t t = 0; t < numThreads; ++t) {
612 threads[t] = DSched::thread([&, t, numThreads] {
613 struct drand48_data buffer;
614 srand48_r(t, &buffer);
615 long writeThreshold = writeFraction * 0x7fffffff;
617 Lock* lock = useSeparateLocks ? &privateLock : &globalLock;
619 while (!goPtr->load()) {
620 this_thread::yield();
622 for (size_t op = t; op < numOps; op += numThreads) {
624 lrand48_r(&buffer, &randVal);
625 bool writeOp = randVal < writeThreshold;
628 if (!useSeparateLocks) {
629 ++valueProtectedByLock;
633 locker.lock_shared(lock);
634 auto v = valueProtectedByLock;
635 folly::doNotOptimizeAway(v);
636 locker.unlock_shared(lock);
644 for (auto& thr : threads) {
649 static void folly_rwspin(size_t numOps,
651 double writeFraction,
652 bool useSeparateLocks) {
653 runMixed<atomic, RWSpinLock, Locker>(
654 numOps, numThreads, writeFraction, useSeparateLocks);
657 static void shmtx_wr_pri(uint numOps,
659 double writeFraction,
660 bool useSeparateLocks) {
661 runMixed<atomic, SharedMutexWritePriority, TokenLocker>(
662 numOps, numThreads, writeFraction, useSeparateLocks);
665 static void shmtx_w_bare(uint numOps,
667 double writeFraction,
668 bool useSeparateLocks) {
669 runMixed<atomic, SharedMutexWritePriority, Locker>(
670 numOps, numThreads, writeFraction, useSeparateLocks);
673 static void shmtx_rd_pri(uint numOps,
675 double writeFraction,
676 bool useSeparateLocks) {
677 runMixed<atomic, SharedMutexReadPriority, TokenLocker>(
678 numOps, numThreads, writeFraction, useSeparateLocks);
681 static void shmtx_r_bare(uint numOps,
683 double writeFraction,
684 bool useSeparateLocks) {
685 runMixed<atomic, SharedMutexReadPriority, Locker>(
686 numOps, numThreads, writeFraction, useSeparateLocks);
689 static void folly_ticket(size_t numOps,
691 double writeFraction,
692 bool useSeparateLocks) {
693 runMixed<atomic, RWTicketSpinLock64, Locker>(
694 numOps, numThreads, writeFraction, useSeparateLocks);
697 static void boost_shared(size_t numOps,
699 double writeFraction,
700 bool useSeparateLocks) {
701 runMixed<atomic, boost::shared_mutex, Locker>(
702 numOps, numThreads, writeFraction, useSeparateLocks);
705 static void pthrd_rwlock(size_t numOps,
707 double writeFraction,
708 bool useSeparateLocks) {
709 runMixed<atomic, PosixRWLock, Locker>(
710 numOps, numThreads, writeFraction, useSeparateLocks);
713 static void pthrd_mutex_(size_t numOps,
715 double writeFraction,
716 bool useSeparateLocks) {
717 runMixed<atomic, PosixMutex, Locker>(
718 numOps, numThreads, writeFraction, useSeparateLocks);
721 template <typename Lock, template <typename> class Atom>
722 static void runAllAndValidate(size_t numOps, size_t numThreads) {
724 Atom<int> globalExclusiveCount(0);
725 Atom<int> globalUpgradeCount(0);
726 Atom<int> globalSharedCount(0);
728 Atom<bool> go(false);
730 // clang crashes on access to Atom<> captured by ref in closure
731 Atom<int>* globalExclusiveCountPtr = &globalExclusiveCount;
732 Atom<int>* globalUpgradeCountPtr = &globalUpgradeCount;
733 Atom<int>* globalSharedCountPtr = &globalSharedCount;
734 Atom<bool>* goPtr = &go;
736 vector<thread> threads(numThreads);
739 for (size_t t = 0; t < numThreads; ++t) {
740 threads[t] = DSched::thread([&, t, numThreads] {
741 struct drand48_data buffer;
742 srand48_r(t, &buffer);
744 bool exclusive = false;
745 bool upgrade = false;
747 bool ourGlobalTokenUsed = false;
748 SharedMutexToken ourGlobalToken;
751 vector<SharedMutexToken> privateTokens;
753 while (!goPtr->load()) {
754 this_thread::yield();
756 for (size_t op = t; op < numOps; op += numThreads) {
757 // randVal in [0,1000)
759 lrand48_r(&buffer, &randVal);
760 randVal = (long)((randVal * (uint64_t)1000) / 0x7fffffff);
762 // make as many assertions as possible about the global state
764 EXPECT_EQ(1, globalExclusiveCountPtr->load(memory_order_acquire));
765 EXPECT_EQ(0, globalUpgradeCountPtr->load(memory_order_acquire));
766 EXPECT_EQ(0, globalSharedCountPtr->load(memory_order_acquire));
769 EXPECT_EQ(0, globalExclusiveCountPtr->load(memory_order_acquire));
770 EXPECT_EQ(1, globalUpgradeCountPtr->load(memory_order_acquire));
773 EXPECT_EQ(0, globalExclusiveCountPtr->load(memory_order_acquire));
774 EXPECT_TRUE(globalSharedCountPtr->load(memory_order_acquire) > 0);
776 EXPECT_FALSE(ourGlobalTokenUsed);
779 // independent 20% chance we do something to the private lock
781 // it's okay to take multiple private shared locks because
782 // we never take an exclusive lock, so reader versus writer
783 // priority doesn't cause deadlocks
784 if (randVal < 100 && privateTokens.size() > 0) {
785 auto i = randVal % privateTokens.size();
786 privateLock.unlock_shared(privateTokens[i]);
787 privateTokens.erase(privateTokens.begin() + i);
789 SharedMutexToken token;
790 privateLock.lock_shared(token);
791 privateTokens.push_back(token);
796 // if we've got a lock, the only thing we can do is release it
797 // or transform it into a different kind of lock
800 --*globalExclusiveCountPtr;
803 } else if (randVal < 700) {
804 globalLock.unlock_and_lock_shared();
805 ++*globalSharedCountPtr;
807 } else if (randVal < 900) {
808 globalLock.unlock_and_lock_shared(ourGlobalToken);
809 ++*globalSharedCountPtr;
811 ourGlobalTokenUsed = true;
813 globalLock.unlock_and_lock_upgrade();
814 ++*globalUpgradeCountPtr;
817 } else if (upgrade) {
819 --*globalUpgradeCountPtr;
821 globalLock.unlock_upgrade();
822 } else if (randVal < 700) {
823 globalLock.unlock_upgrade_and_lock_shared();
824 ++*globalSharedCountPtr;
826 } else if (randVal < 900) {
827 globalLock.unlock_upgrade_and_lock_shared(ourGlobalToken);
828 ++*globalSharedCountPtr;
830 ourGlobalTokenUsed = true;
832 globalLock.unlock_upgrade_and_lock();
833 ++*globalExclusiveCountPtr;
838 --*globalSharedCountPtr;
839 if (ourGlobalTokenUsed) {
840 globalLock.unlock_shared(ourGlobalToken);
841 ourGlobalTokenUsed = false;
843 globalLock.unlock_shared();
845 } else if (randVal < 400) {
846 // 40% chance of shared lock with token, 5 ways to get it
848 // delta t goes from -1 millis to 7 millis
849 auto dt = microseconds(10 * (randVal - 100));
852 globalLock.lock_shared(ourGlobalToken);
854 } else if (randVal < 500) {
855 shared = globalLock.try_lock_shared(ourGlobalToken);
856 } else if (randVal < 600) {
857 shared = globalLock.try_lock_shared_for(dt, ourGlobalToken);
858 } else if (randVal < 800) {
859 shared = globalLock.try_lock_shared_until(
860 system_clock::now() + dt, ourGlobalToken);
863 ourGlobalTokenUsed = true;
864 ++*globalSharedCountPtr;
866 } else if (randVal < 800) {
867 // 40% chance of shared lock without token
868 auto dt = microseconds(10 * (randVal - 100));
870 globalLock.lock_shared();
872 } else if (randVal < 500) {
873 shared = globalLock.try_lock_shared();
874 } else if (randVal < 600) {
875 shared = globalLock.try_lock_shared_for(dt);
876 } else if (randVal < 800) {
877 shared = globalLock.try_lock_shared_until(
878 system_clock::now() + dt);
881 ++*globalSharedCountPtr;
883 } else if (randVal < 900) {
884 // 10% change of upgrade lock
885 globalLock.lock_upgrade();
887 ++*globalUpgradeCountPtr;
889 // 10% chance of exclusive lock, 5 ways to get it
891 // delta t goes from -1 millis to 9 millis
892 auto dt = microseconds(100 * (randVal - 910));
897 } else if (randVal < 500) {
898 exclusive = globalLock.try_lock();
899 } else if (randVal < 600) {
900 exclusive = globalLock.try_lock_for(dt);
901 } else if (randVal < 700) {
902 exclusive = globalLock.try_lock_until(steady_clock::now() + dt);
904 exclusive = globalLock.try_lock_until(system_clock::now() + dt);
907 ++*globalExclusiveCountPtr;
913 --*globalExclusiveCountPtr;
917 --*globalUpgradeCountPtr;
918 globalLock.unlock_upgrade();
921 --*globalSharedCountPtr;
922 if (ourGlobalTokenUsed) {
923 globalLock.unlock_shared(ourGlobalToken);
924 ourGlobalTokenUsed = false;
926 globalLock.unlock_shared();
929 for (auto& token : privateTokens) {
930 privateLock.unlock_shared(token);
937 for (auto& thr : threads) {
942 TEST(SharedMutex, deterministic_concurrent_readers_of_one_lock_read_prio) {
943 for (int pass = 0; pass < 3; ++pass) {
944 DSched sched(DSched::uniform(pass));
945 runContendedReaders<DeterministicAtomic,
946 DSharedMutexReadPriority,
947 Locker>(1000, 3, false);
951 TEST(SharedMutex, deterministic_concurrent_readers_of_one_lock_write_prio) {
952 for (int pass = 0; pass < 3; ++pass) {
953 DSched sched(DSched::uniform(pass));
954 runContendedReaders<DeterministicAtomic,
955 DSharedMutexWritePriority,
956 Locker>(1000, 3, false);
960 TEST(SharedMutex, concurrent_readers_of_one_lock_read_prio) {
961 for (int pass = 0; pass < 10; ++pass) {
962 runContendedReaders<atomic, SharedMutexReadPriority, Locker>(
967 TEST(SharedMutex, concurrent_readers_of_one_lock_write_prio) {
968 for (int pass = 0; pass < 10; ++pass) {
969 runContendedReaders<atomic, SharedMutexWritePriority, Locker>(
974 TEST(SharedMutex, deterministic_readers_of_concurrent_locks_read_prio) {
975 for (int pass = 0; pass < 3; ++pass) {
976 DSched sched(DSched::uniform(pass));
977 runContendedReaders<DeterministicAtomic,
978 DSharedMutexReadPriority,
979 Locker>(1000, 3, true);
983 TEST(SharedMutex, deterministic_readers_of_concurrent_locks_write_prio) {
984 for (int pass = 0; pass < 3; ++pass) {
985 DSched sched(DSched::uniform(pass));
986 runContendedReaders<DeterministicAtomic,
987 DSharedMutexWritePriority,
988 Locker>(1000, 3, true);
992 TEST(SharedMutex, readers_of_concurrent_locks_read_prio) {
993 for (int pass = 0; pass < 10; ++pass) {
994 runContendedReaders<atomic, SharedMutexReadPriority, TokenLocker>(
999 TEST(SharedMutex, readers_of_concurrent_locks_write_prio) {
1000 for (int pass = 0; pass < 10; ++pass) {
1001 runContendedReaders<atomic, SharedMutexWritePriority, TokenLocker>(
1006 TEST(SharedMutex, deterministic_mixed_mostly_read_read_prio) {
1007 for (int pass = 0; pass < 3; ++pass) {
1008 DSched sched(DSched::uniform(pass));
1009 runMixed<DeterministicAtomic, DSharedMutexReadPriority, Locker>(
1010 1000, 3, 0.1, false);
1014 TEST(SharedMutex, deterministic_mixed_mostly_read_write_prio) {
1015 for (int pass = 0; pass < 3; ++pass) {
1016 DSched sched(DSched::uniform(pass));
1017 runMixed<DeterministicAtomic, DSharedMutexWritePriority, Locker>(
1018 1000, 3, 0.1, false);
1022 TEST(SharedMutex, mixed_mostly_read_read_prio) {
1023 for (int pass = 0; pass < 5; ++pass) {
1024 runMixed<atomic, SharedMutexReadPriority, TokenLocker>(
1025 10000, 32, 0.1, false);
1029 TEST(SharedMutex, mixed_mostly_read_write_prio) {
1030 for (int pass = 0; pass < 5; ++pass) {
1031 runMixed<atomic, SharedMutexWritePriority, TokenLocker>(
1032 10000, 32, 0.1, false);
1036 TEST(SharedMutex, deterministic_mixed_mostly_write_read_prio) {
1037 for (int pass = 0; pass < 1; ++pass) {
1038 DSched sched(DSched::uniform(pass));
1039 runMixed<DeterministicAtomic, DSharedMutexReadPriority, TokenLocker>(
1040 1000, 10, 0.9, false);
1044 TEST(SharedMutex, deterministic_mixed_mostly_write_write_prio) {
1045 for (int pass = 0; pass < 1; ++pass) {
1046 DSched sched(DSched::uniform(pass));
1047 runMixed<DeterministicAtomic, DSharedMutexWritePriority, TokenLocker>(
1048 1000, 10, 0.9, false);
1052 TEST(SharedMutex, deterministic_lost_wakeup_write_prio) {
1053 for (int pass = 0; pass < 10; ++pass) {
1054 DSched sched(DSched::uniformSubset(pass, 2, 200));
1055 runMixed<DeterministicAtomic, DSharedMutexWritePriority, TokenLocker>(
1056 1000, 3, 1.0, false);
1060 TEST(SharedMutex, mixed_mostly_write_read_prio) {
1061 for (int pass = 0; pass < 5; ++pass) {
1062 runMixed<atomic, SharedMutexReadPriority, TokenLocker>(
1063 50000, 300, 0.9, false);
1067 TEST(SharedMutex, mixed_mostly_write_write_prio) {
1068 for (int pass = 0; pass < 5; ++pass) {
1069 runMixed<atomic, SharedMutexWritePriority, TokenLocker>(
1070 50000, 300, 0.9, false);
1074 TEST(SharedMutex, deterministic_all_ops_read_prio) {
1075 for (int pass = 0; pass < 5; ++pass) {
1076 DSched sched(DSched::uniform(pass));
1077 runAllAndValidate<DSharedMutexReadPriority, DeterministicAtomic>(1000, 8);
1081 TEST(SharedMutex, deterministic_all_ops_write_prio) {
1082 for (int pass = 0; pass < 5; ++pass) {
1083 DSched sched(DSched::uniform(pass));
1084 runAllAndValidate<DSharedMutexWritePriority, DeterministicAtomic>(1000, 8);
1088 TEST(SharedMutex, all_ops_read_prio) {
1089 for (int pass = 0; pass < 5; ++pass) {
1090 runAllAndValidate<SharedMutexReadPriority, atomic>(100000, 32);
1094 TEST(SharedMutex, all_ops_write_prio) {
1095 for (int pass = 0; pass < 5; ++pass) {
1096 runAllAndValidate<SharedMutexWritePriority, atomic>(100000, 32);
1100 FOLLY_ASSUME_FBVECTOR_COMPATIBLE(
1101 boost::optional<boost::optional<SharedMutexToken>>)
1103 // Setup is a set of threads that either grab a shared lock, or exclusive
1104 // and then downgrade it, or upgrade then upgrade and downgrade, then
1105 // enqueue the shared lock to a second set of threads that just performs
1106 // unlocks. Half of the shared locks use tokens, the others don't.
1107 template <typename Lock, template <typename> class Atom>
1108 static void runRemoteUnlock(size_t numOps,
1109 double preWriteFraction,
1110 double preUpgradeFraction,
1111 size_t numSendingThreads,
1112 size_t numReceivingThreads) {
1114 MPMCQueue<boost::optional<boost::optional<SharedMutexToken>>, Atom>
1116 auto queuePtr = &queue; // workaround for clang crash
1118 Atom<bool> go(false);
1119 auto goPtr = &go; // workaround for clang crash
1120 Atom<int> pendingSenders(numSendingThreads);
1121 auto pendingSendersPtr = &pendingSenders; // workaround for clang crash
1122 vector<thread> threads(numSendingThreads + numReceivingThreads);
1125 for (size_t t = 0; t < threads.size(); ++t) {
1126 threads[t] = DSched::thread([&, t, numSendingThreads] {
1127 if (t >= numSendingThreads) {
1129 typename decltype(queue)::value_type elem;
1131 queuePtr->blockingRead(elem);
1133 // EOF, pass the EOF token
1134 queuePtr->blockingWrite(std::move(elem));
1138 globalLock.unlock_shared(**elem);
1140 globalLock.unlock_shared();
1145 // else we're a sender
1147 struct drand48_data buffer;
1148 srand48_r(t, &buffer);
1150 while (!goPtr->load()) {
1151 this_thread::yield();
1153 for (size_t op = t; op < numOps; op += numSendingThreads) {
1154 long unscaledRandVal;
1155 lrand48_r(&buffer, &unscaledRandVal);
1158 double randVal = ((double)unscaledRandVal) / 0x7fffffff;
1160 // extract a bit and rescale
1161 bool useToken = randVal >= 0.5;
1162 randVal = (randVal - (useToken ? 0.5 : 0.0)) * 2;
1164 boost::optional<SharedMutexToken> maybeToken;
1167 SharedMutexToken token;
1168 if (randVal < preWriteFraction) {
1170 globalLock.unlock_and_lock_shared(token);
1171 } else if (randVal < preWriteFraction + preUpgradeFraction / 2) {
1172 globalLock.lock_upgrade();
1173 globalLock.unlock_upgrade_and_lock_shared(token);
1174 } else if (randVal < preWriteFraction + preUpgradeFraction) {
1175 globalLock.lock_upgrade();
1176 globalLock.unlock_upgrade_and_lock();
1177 globalLock.unlock_and_lock_shared(token);
1179 globalLock.lock_shared(token);
1183 if (randVal < preWriteFraction) {
1185 globalLock.unlock_and_lock_shared();
1186 } else if (randVal < preWriteFraction + preUpgradeFraction / 2) {
1187 globalLock.lock_upgrade();
1188 globalLock.unlock_upgrade_and_lock_shared();
1189 } else if (randVal < preWriteFraction + preUpgradeFraction) {
1190 globalLock.lock_upgrade();
1191 globalLock.unlock_upgrade_and_lock();
1192 globalLock.unlock_and_lock_shared();
1194 globalLock.lock_shared();
1198 // blockingWrite is emplace-like, so this automatically adds
1199 // another level of wrapping
1200 queuePtr->blockingWrite(maybeToken);
1202 if (--*pendingSendersPtr == 0) {
1203 queuePtr->blockingWrite(boost::none);
1210 for (auto& thr : threads) {
1215 TEST(SharedMutex, deterministic_remote_write_prio) {
1216 for (int pass = 0; pass < 1; ++pass) {
1217 DSched sched(DSched::uniform(pass));
1218 runRemoteUnlock<DSharedMutexWritePriority, DeterministicAtomic>(
1219 500, 0.1, 0.1, 5, 5);
1223 TEST(SharedMutex, deterministic_remote_read_prio) {
1224 for (int pass = 0; pass < 1; ++pass) {
1225 DSched sched(DSched::uniform(pass));
1226 runRemoteUnlock<DSharedMutexReadPriority, DeterministicAtomic>(
1227 500, 0.1, 0.1, 5, 5);
1231 TEST(SharedMutex, remote_write_prio) {
1232 for (int pass = 0; pass < 10; ++pass) {
1233 runRemoteUnlock<SharedMutexWritePriority, atomic>(100000, 0.1, 0.1, 5, 5);
1237 TEST(SharedMutex, remote_read_prio) {
1238 for (int pass = 0; pass < 100; ++pass) {
1239 runRemoteUnlock<SharedMutexReadPriority, atomic>(100000, 0.1, 0.1, 5, 5);
1243 static void burn(size_t n) {
1244 for (size_t i = 0; i < n; ++i) {
1245 folly::doNotOptimizeAway(i);
1249 // Two threads and three locks, arranged so that they have to proceed
1250 // in turn with reader/writer conflict
1251 template <typename Lock, template <typename> class Atom = atomic>
1252 static void runPingPong(size_t numRounds, size_t burnCount) {
1255 pair<Lock, char[56]> locks[3];
1260 auto availPtr = &avail; // workaround for clang crash
1261 Atom<bool> go(false);
1262 auto goPtr = &go; // workaround for clang crash
1263 vector<thread> threads(2);
1265 locks[0].first.lock();
1266 locks[1].first.lock();
1267 locks[2].first.lock_shared();
1270 threads[0] = DSched::thread([&] {
1272 while (!goPtr->load()) {
1273 this_thread::yield();
1275 for (size_t i = 0; i < numRounds; ++i) {
1276 locks[i % 3].first.unlock();
1277 locks[(i + 2) % 3].first.lock();
1281 threads[1] = DSched::thread([&] {
1283 while (!goPtr->load()) {
1284 this_thread::yield();
1286 for (size_t i = 0; i < numRounds; ++i) {
1287 locks[i % 3].first.lock_shared();
1289 locks[(i + 2) % 3].first.unlock_shared();
1293 while (avail.load() < 2) {
1294 this_thread::yield();
1299 for (auto& thr : threads) {
1302 locks[numRounds % 3].first.unlock();
1303 locks[(numRounds + 1) % 3].first.unlock();
1304 locks[(numRounds + 2) % 3].first.unlock_shared();
1307 static void folly_rwspin_ping_pong(size_t n, size_t scale, size_t burnCount) {
1308 runPingPong<RWSpinLock>(n / scale, burnCount);
1311 static void shmtx_w_bare_ping_pong(size_t n, size_t scale, size_t burnCount) {
1312 runPingPong<SharedMutexWritePriority>(n / scale, burnCount);
1315 static void shmtx_r_bare_ping_pong(size_t n, size_t scale, size_t burnCount) {
1316 runPingPong<SharedMutexReadPriority>(n / scale, burnCount);
1319 static void folly_ticket_ping_pong(size_t n, size_t scale, size_t burnCount) {
1320 runPingPong<RWTicketSpinLock64>(n / scale, burnCount);
1323 static void boost_shared_ping_pong(size_t n, size_t scale, size_t burnCount) {
1324 runPingPong<boost::shared_mutex>(n / scale, burnCount);
1327 static void pthrd_rwlock_ping_pong(size_t n, size_t scale, size_t burnCount) {
1328 runPingPong<PosixRWLock>(n / scale, burnCount);
1331 TEST(SharedMutex, deterministic_ping_pong_write_prio) {
1332 for (int pass = 0; pass < 1; ++pass) {
1333 DSched sched(DSched::uniform(pass));
1334 runPingPong<DSharedMutexWritePriority, DeterministicAtomic>(500, 0);
1338 TEST(SharedMutex, deterministic_ping_pong_read_prio) {
1339 for (int pass = 0; pass < 1; ++pass) {
1340 DSched sched(DSched::uniform(pass));
1341 runPingPong<DSharedMutexReadPriority, DeterministicAtomic>(500, 0);
1345 TEST(SharedMutex, ping_pong_write_prio) {
1346 for (int pass = 0; pass < 1; ++pass) {
1347 runPingPong<SharedMutexWritePriority, atomic>(50000, 0);
1351 TEST(SharedMutex, ping_pong_read_prio) {
1352 for (int pass = 0; pass < 1; ++pass) {
1353 runPingPong<SharedMutexReadPriority, atomic>(50000, 0);
1357 // This is here so you can tell how much of the runtime reported by the
1358 // more complex harnesses is due to the harness, although due to the
1359 // magic of compiler optimization it may also be slower
1360 BENCHMARK(single_thread_lock_shared_unlock_shared, iters) {
1362 for (size_t n = 0; n < iters; ++n) {
1363 SharedMutex::Token token;
1364 lock.lock_shared(token);
1365 folly::doNotOptimizeAway(0);
1366 lock.unlock_shared(token);
1370 BENCHMARK(single_thread_lock_unlock, iters) {
1372 for (size_t n = 0; n < iters; ++n) {
1374 folly::doNotOptimizeAway(0);
1379 #define BENCH_BASE(args...) BENCHMARK_NAMED_PARAM(args)
1380 #define BENCH_REL(args...) BENCHMARK_RELATIVE_NAMED_PARAM(args)
1382 // 100% reads. Best-case scenario for deferred locks. Lock is colocated
1383 // with read data, so inline lock takes cache miss every time but deferred
1384 // lock has only cache hits and local access.
1385 BENCHMARK_DRAW_LINE()
1386 BENCHMARK_DRAW_LINE()
1387 BENCH_BASE(folly_rwspin_reads, 1thread, 1, false)
1388 BENCH_REL (shmtx_wr_pri_reads, 1thread, 1, false)
1389 BENCH_REL (shmtx_w_bare_reads, 1thread, 1, false)
1390 BENCH_REL (shmtx_rd_pri_reads, 1thread, 1, false)
1391 BENCH_REL (shmtx_r_bare_reads, 1thread, 1, false)
1392 BENCH_REL (folly_ticket_reads, 1thread, 1, false)
1393 BENCH_REL (boost_shared_reads, 1thread, 1, false)
1394 BENCH_REL (pthrd_rwlock_reads, 1thread, 1, false)
1395 BENCHMARK_DRAW_LINE()
1396 BENCH_BASE(folly_rwspin_reads, 2thread, 2, false)
1397 BENCH_REL (shmtx_wr_pri_reads, 2thread, 2, false)
1398 BENCH_REL (shmtx_w_bare_reads, 2thread, 2, false)
1399 BENCH_REL (shmtx_rd_pri_reads, 2thread, 2, false)
1400 BENCH_REL (shmtx_r_bare_reads, 2thread, 2, false)
1401 BENCH_REL (folly_ticket_reads, 2thread, 2, false)
1402 BENCH_REL (boost_shared_reads, 2thread, 2, false)
1403 BENCH_REL (pthrd_rwlock_reads, 2thread, 2, false)
1404 BENCHMARK_DRAW_LINE()
1405 BENCH_BASE(folly_rwspin_reads, 4thread, 4, false)
1406 BENCH_REL (shmtx_wr_pri_reads, 4thread, 4, false)
1407 BENCH_REL (shmtx_w_bare_reads, 4thread, 4, false)
1408 BENCH_REL (shmtx_rd_pri_reads, 4thread, 4, false)
1409 BENCH_REL (shmtx_r_bare_reads, 4thread, 4, false)
1410 BENCH_REL (folly_ticket_reads, 4thread, 4, false)
1411 BENCH_REL (boost_shared_reads, 4thread, 4, false)
1412 BENCH_REL (pthrd_rwlock_reads, 4thread, 4, false)
1413 BENCHMARK_DRAW_LINE()
1414 BENCH_BASE(folly_rwspin_reads, 8thread, 8, false)
1415 BENCH_REL (shmtx_wr_pri_reads, 8thread, 8, false)
1416 BENCH_REL (shmtx_w_bare_reads, 8thread, 8, false)
1417 BENCH_REL (shmtx_rd_pri_reads, 8thread, 8, false)
1418 BENCH_REL (shmtx_r_bare_reads, 8thread, 8, false)
1419 BENCH_REL (folly_ticket_reads, 8thread, 8, false)
1420 BENCH_REL (boost_shared_reads, 8thread, 8, false)
1421 BENCH_REL (pthrd_rwlock_reads, 8thread, 8, false)
1422 BENCHMARK_DRAW_LINE()
1423 BENCH_BASE(folly_rwspin_reads, 16thread, 16, false)
1424 BENCH_REL (shmtx_wr_pri_reads, 16thread, 16, false)
1425 BENCH_REL (shmtx_w_bare_reads, 16thread, 16, false)
1426 BENCH_REL (shmtx_rd_pri_reads, 16thread, 16, false)
1427 BENCH_REL (shmtx_r_bare_reads, 16thread, 16, false)
1428 BENCH_REL (folly_ticket_reads, 16thread, 16, false)
1429 BENCH_REL (boost_shared_reads, 16thread, 16, false)
1430 BENCH_REL (pthrd_rwlock_reads, 16thread, 16, false)
1431 BENCHMARK_DRAW_LINE()
1432 BENCH_BASE(folly_rwspin_reads, 32thread, 32, false)
1433 BENCH_REL (shmtx_wr_pri_reads, 32thread, 32, false)
1434 BENCH_REL (shmtx_w_bare_reads, 32thread, 32, false)
1435 BENCH_REL (shmtx_rd_pri_reads, 32thread, 32, false)
1436 BENCH_REL (shmtx_r_bare_reads, 32thread, 32, false)
1437 BENCH_REL (folly_ticket_reads, 32thread, 32, false)
1438 BENCH_REL (boost_shared_reads, 32thread, 32, false)
1439 BENCH_REL (pthrd_rwlock_reads, 32thread, 32, false)
1440 BENCHMARK_DRAW_LINE()
1441 BENCH_BASE(folly_rwspin_reads, 64thread, 64, false)
1442 BENCH_REL (shmtx_wr_pri_reads, 64thread, 64, false)
1443 BENCH_REL (shmtx_w_bare_reads, 64thread, 64, false)
1444 BENCH_REL (shmtx_rd_pri_reads, 64thread, 64, false)
1445 BENCH_REL (shmtx_r_bare_reads, 64thread, 64, false)
1446 BENCH_REL (folly_ticket_reads, 64thread, 64, false)
1447 BENCH_REL (boost_shared_reads, 64thread, 64, false)
1448 BENCH_REL (pthrd_rwlock_reads, 64thread, 64, false)
1450 // 1 lock used by everybody, 100% writes. Threads only hurt, but it is
1451 // good to not fail catastrophically. Compare to single_thread_lock_unlock
1452 // to see the overhead of the generic driver (and its pseudo-random number
1453 // generator). pthrd_mutex_ is a pthread_mutex_t (default, not adaptive),
1454 // which is better than any of the reader-writer locks for this scenario.
1455 BENCHMARK_DRAW_LINE()
1456 BENCHMARK_DRAW_LINE()
1457 BENCH_BASE(folly_rwspin, 1thread_all_write, 1, 1.0, false)
1458 BENCH_REL (shmtx_wr_pri, 1thread_all_write, 1, 1.0, false)
1459 BENCH_REL (shmtx_rd_pri, 1thread_all_write, 1, 1.0, false)
1460 BENCH_REL (folly_ticket, 1thread_all_write, 1, 1.0, false)
1461 BENCH_REL (boost_shared, 1thread_all_write, 1, 1.0, false)
1462 BENCH_REL (pthrd_rwlock, 1thread_all_write, 1, 1.0, false)
1463 BENCH_REL (pthrd_mutex_, 1thread_all_write, 1, 1.0, false)
1464 BENCHMARK_DRAW_LINE()
1465 BENCH_BASE(folly_rwspin, 2thread_all_write, 2, 1.0, false)
1466 BENCH_REL (shmtx_wr_pri, 2thread_all_write, 2, 1.0, false)
1467 BENCH_REL (shmtx_rd_pri, 2thread_all_write, 2, 1.0, false)
1468 BENCH_REL (folly_ticket, 2thread_all_write, 2, 1.0, false)
1469 BENCH_REL (boost_shared, 2thread_all_write, 2, 1.0, false)
1470 BENCH_REL (pthrd_rwlock, 2thread_all_write, 2, 1.0, false)
1471 BENCH_REL (pthrd_mutex_, 2thread_all_write, 2, 1.0, false)
1472 BENCHMARK_DRAW_LINE()
1473 BENCH_BASE(folly_rwspin, 4thread_all_write, 4, 1.0, false)
1474 BENCH_REL (shmtx_wr_pri, 4thread_all_write, 4, 1.0, false)
1475 BENCH_REL (shmtx_rd_pri, 4thread_all_write, 4, 1.0, false)
1476 BENCH_REL (folly_ticket, 4thread_all_write, 4, 1.0, false)
1477 BENCH_REL (boost_shared, 4thread_all_write, 4, 1.0, false)
1478 BENCH_REL (pthrd_rwlock, 4thread_all_write, 4, 1.0, false)
1479 BENCH_REL (pthrd_mutex_, 4thread_all_write, 4, 1.0, false)
1480 BENCHMARK_DRAW_LINE()
1481 BENCH_BASE(folly_rwspin, 8thread_all_write, 8, 1.0, false)
1482 BENCH_REL (shmtx_wr_pri, 8thread_all_write, 8, 1.0, false)
1483 BENCH_REL (shmtx_rd_pri, 8thread_all_write, 8, 1.0, false)
1484 BENCH_REL (folly_ticket, 8thread_all_write, 8, 1.0, false)
1485 BENCH_REL (boost_shared, 8thread_all_write, 8, 1.0, false)
1486 BENCH_REL (pthrd_rwlock, 8thread_all_write, 8, 1.0, false)
1487 BENCH_REL (pthrd_mutex_, 8thread_all_write, 8, 1.0, false)
1488 BENCHMARK_DRAW_LINE()
1489 BENCH_BASE(folly_rwspin, 16thread_all_write, 16, 1.0, false)
1490 BENCH_REL (shmtx_wr_pri, 16thread_all_write, 16, 1.0, false)
1491 BENCH_REL (shmtx_rd_pri, 16thread_all_write, 16, 1.0, false)
1492 BENCH_REL (folly_ticket, 16thread_all_write, 16, 1.0, false)
1493 BENCH_REL (boost_shared, 16thread_all_write, 16, 1.0, false)
1494 BENCH_REL (pthrd_rwlock, 16thread_all_write, 16, 1.0, false)
1495 BENCH_REL (pthrd_mutex_, 16thread_all_write, 16, 1.0, false)
1496 BENCHMARK_DRAW_LINE()
1497 BENCH_BASE(folly_rwspin, 32thread_all_write, 32, 1.0, false)
1498 BENCH_REL (shmtx_wr_pri, 32thread_all_write, 32, 1.0, false)
1499 BENCH_REL (shmtx_rd_pri, 32thread_all_write, 32, 1.0, false)
1500 BENCH_REL (folly_ticket, 32thread_all_write, 32, 1.0, false)
1501 BENCH_REL (boost_shared, 32thread_all_write, 32, 1.0, false)
1502 BENCH_REL (pthrd_rwlock, 32thread_all_write, 32, 1.0, false)
1503 BENCH_REL (pthrd_mutex_, 32thread_all_write, 32, 1.0, false)
1504 BENCHMARK_DRAW_LINE()
1505 BENCH_BASE(folly_rwspin, 64thread_all_write, 64, 1.0, false)
1506 BENCH_REL (shmtx_wr_pri, 64thread_all_write, 64, 1.0, false)
1507 BENCH_REL (shmtx_rd_pri, 64thread_all_write, 64, 1.0, false)
1508 BENCH_REL (folly_ticket, 64thread_all_write, 64, 1.0, false)
1509 BENCH_REL (boost_shared, 64thread_all_write, 64, 1.0, false)
1510 BENCH_REL (pthrd_rwlock, 64thread_all_write, 64, 1.0, false)
1511 BENCH_REL (pthrd_mutex_, 64thread_all_write, 64, 1.0, false)
1513 // 1 lock used by everybody, 10% writes. Not much scaling to be had. Perf
1514 // is best at 1 thread, once you've got multiple threads > 8 threads hurts.
1515 BENCHMARK_DRAW_LINE()
1516 BENCHMARK_DRAW_LINE()
1517 BENCH_BASE(folly_rwspin, 1thread_10pct_write, 1, 0.10, false)
1518 BENCH_REL (shmtx_wr_pri, 1thread_10pct_write, 1, 0.10, false)
1519 BENCH_REL (shmtx_rd_pri, 1thread_10pct_write, 1, 0.10, false)
1520 BENCH_REL (folly_ticket, 1thread_10pct_write, 1, 0.10, false)
1521 BENCH_REL (boost_shared, 1thread_10pct_write, 1, 0.10, false)
1522 BENCH_REL (pthrd_rwlock, 1thread_10pct_write, 1, 0.10, false)
1523 BENCHMARK_DRAW_LINE()
1524 BENCH_BASE(folly_rwspin, 2thread_10pct_write, 2, 0.10, false)
1525 BENCH_REL (shmtx_wr_pri, 2thread_10pct_write, 2, 0.10, false)
1526 BENCH_REL (shmtx_rd_pri, 2thread_10pct_write, 2, 0.10, false)
1527 BENCH_REL (folly_ticket, 2thread_10pct_write, 2, 0.10, false)
1528 BENCH_REL (boost_shared, 2thread_10pct_write, 2, 0.10, false)
1529 BENCH_REL (pthrd_rwlock, 2thread_10pct_write, 2, 0.10, false)
1530 BENCHMARK_DRAW_LINE()
1531 BENCH_BASE(folly_rwspin, 4thread_10pct_write, 4, 0.10, false)
1532 BENCH_REL (shmtx_wr_pri, 4thread_10pct_write, 4, 0.10, false)
1533 BENCH_REL (shmtx_rd_pri, 4thread_10pct_write, 4, 0.10, false)
1534 BENCH_REL (folly_ticket, 4thread_10pct_write, 4, 0.10, false)
1535 BENCH_REL (boost_shared, 4thread_10pct_write, 4, 0.10, false)
1536 BENCH_REL (pthrd_rwlock, 4thread_10pct_write, 4, 0.10, false)
1537 BENCHMARK_DRAW_LINE()
1538 BENCH_BASE(folly_rwspin, 8thread_10pct_write, 8, 0.10, false)
1539 BENCH_REL (shmtx_wr_pri, 8thread_10pct_write, 8, 0.10, false)
1540 BENCH_REL (shmtx_rd_pri, 8thread_10pct_write, 8, 0.10, false)
1541 BENCH_REL (folly_ticket, 8thread_10pct_write, 8, 0.10, false)
1542 BENCH_REL (boost_shared, 8thread_10pct_write, 8, 0.10, false)
1543 BENCH_REL (pthrd_rwlock, 8thread_10pct_write, 8, 0.10, false)
1544 BENCHMARK_DRAW_LINE()
1545 BENCH_BASE(folly_rwspin, 16thread_10pct_write, 16, 0.10, false)
1546 BENCH_REL (shmtx_wr_pri, 16thread_10pct_write, 16, 0.10, false)
1547 BENCH_REL (shmtx_rd_pri, 16thread_10pct_write, 16, 0.10, false)
1548 BENCH_REL (folly_ticket, 16thread_10pct_write, 16, 0.10, false)
1549 BENCH_REL (boost_shared, 16thread_10pct_write, 16, 0.10, false)
1550 BENCH_REL (pthrd_rwlock, 16thread_10pct_write, 16, 0.10, false)
1551 BENCHMARK_DRAW_LINE()
1552 BENCH_BASE(folly_rwspin, 32thread_10pct_write, 32, 0.10, false)
1553 BENCH_REL (shmtx_wr_pri, 32thread_10pct_write, 32, 0.10, false)
1554 BENCH_REL (shmtx_rd_pri, 32thread_10pct_write, 32, 0.10, false)
1555 BENCH_REL (folly_ticket, 32thread_10pct_write, 32, 0.10, false)
1556 BENCH_REL (boost_shared, 32thread_10pct_write, 32, 0.10, false)
1557 BENCH_REL (pthrd_rwlock, 32thread_10pct_write, 32, 0.10, false)
1558 BENCHMARK_DRAW_LINE()
1559 BENCH_BASE(folly_rwspin, 64thread_10pct_write, 64, 0.10, false)
1560 BENCH_REL (shmtx_wr_pri, 64thread_10pct_write, 64, 0.10, false)
1561 BENCH_REL (shmtx_rd_pri, 64thread_10pct_write, 64, 0.10, false)
1562 BENCH_REL (folly_ticket, 64thread_10pct_write, 64, 0.10, false)
1563 BENCH_REL (boost_shared, 64thread_10pct_write, 64, 0.10, false)
1564 BENCH_REL (pthrd_rwlock, 64thread_10pct_write, 64, 0.10, false)
1566 // 1 lock used by everybody, 1% writes. This is a more realistic example
1567 // than the concurrent_*_reads benchmark, but still shows SharedMutex locks
1568 // winning over all of the others
1569 BENCHMARK_DRAW_LINE()
1570 BENCHMARK_DRAW_LINE()
1571 BENCH_BASE(folly_rwspin, 1thread_1pct_write, 1, 0.01, false)
1572 BENCH_REL (shmtx_wr_pri, 1thread_1pct_write, 1, 0.01, false)
1573 BENCH_REL (shmtx_w_bare, 1thread_1pct_write, 1, 0.01, false)
1574 BENCH_REL (shmtx_rd_pri, 1thread_1pct_write, 1, 0.01, false)
1575 BENCH_REL (shmtx_r_bare, 1thread_1pct_write, 1, 0.01, false)
1576 BENCH_REL (folly_ticket, 1thread_1pct_write, 1, 0.01, false)
1577 BENCH_REL (boost_shared, 1thread_1pct_write, 1, 0.01, false)
1578 BENCH_REL (pthrd_rwlock, 1thread_1pct_write, 1, 0.01, false)
1579 BENCHMARK_DRAW_LINE()
1580 BENCH_BASE(folly_rwspin, 2thread_1pct_write, 2, 0.01, false)
1581 BENCH_REL (shmtx_wr_pri, 2thread_1pct_write, 2, 0.01, false)
1582 BENCH_REL (shmtx_w_bare, 2thread_1pct_write, 2, 0.01, false)
1583 BENCH_REL (shmtx_rd_pri, 2thread_1pct_write, 2, 0.01, false)
1584 BENCH_REL (shmtx_r_bare, 2thread_1pct_write, 2, 0.01, false)
1585 BENCH_REL (folly_ticket, 2thread_1pct_write, 2, 0.01, false)
1586 BENCH_REL (boost_shared, 2thread_1pct_write, 2, 0.01, false)
1587 BENCH_REL (pthrd_rwlock, 2thread_1pct_write, 2, 0.01, false)
1588 BENCHMARK_DRAW_LINE()
1589 BENCH_BASE(folly_rwspin, 4thread_1pct_write, 4, 0.01, false)
1590 BENCH_REL (shmtx_wr_pri, 4thread_1pct_write, 4, 0.01, false)
1591 BENCH_REL (shmtx_w_bare, 4thread_1pct_write, 4, 0.01, false)
1592 BENCH_REL (shmtx_rd_pri, 4thread_1pct_write, 4, 0.01, false)
1593 BENCH_REL (shmtx_r_bare, 4thread_1pct_write, 4, 0.01, false)
1594 BENCH_REL (folly_ticket, 4thread_1pct_write, 4, 0.01, false)
1595 BENCH_REL (boost_shared, 4thread_1pct_write, 4, 0.01, false)
1596 BENCH_REL (pthrd_rwlock, 4thread_1pct_write, 4, 0.01, false)
1597 BENCHMARK_DRAW_LINE()
1598 BENCH_BASE(folly_rwspin, 8thread_1pct_write, 8, 0.01, false)
1599 BENCH_REL (shmtx_wr_pri, 8thread_1pct_write, 8, 0.01, false)
1600 BENCH_REL (shmtx_w_bare, 8thread_1pct_write, 8, 0.01, false)
1601 BENCH_REL (shmtx_rd_pri, 8thread_1pct_write, 8, 0.01, false)
1602 BENCH_REL (shmtx_r_bare, 8thread_1pct_write, 8, 0.01, false)
1603 BENCH_REL (folly_ticket, 8thread_1pct_write, 8, 0.01, false)
1604 BENCH_REL (boost_shared, 8thread_1pct_write, 8, 0.01, false)
1605 BENCH_REL (pthrd_rwlock, 8thread_1pct_write, 8, 0.01, false)
1606 BENCHMARK_DRAW_LINE()
1607 BENCH_BASE(folly_rwspin, 16thread_1pct_write, 16, 0.01, false)
1608 BENCH_REL (shmtx_wr_pri, 16thread_1pct_write, 16, 0.01, false)
1609 BENCH_REL (shmtx_w_bare, 16thread_1pct_write, 16, 0.01, false)
1610 BENCH_REL (shmtx_rd_pri, 16thread_1pct_write, 16, 0.01, false)
1611 BENCH_REL (shmtx_r_bare, 16thread_1pct_write, 16, 0.01, false)
1612 BENCH_REL (folly_ticket, 16thread_1pct_write, 16, 0.01, false)
1613 BENCH_REL (boost_shared, 16thread_1pct_write, 16, 0.01, false)
1614 BENCH_REL (pthrd_rwlock, 16thread_1pct_write, 16, 0.01, false)
1615 BENCHMARK_DRAW_LINE()
1616 BENCH_BASE(folly_rwspin, 32thread_1pct_write, 32, 0.01, false)
1617 BENCH_REL (shmtx_wr_pri, 32thread_1pct_write, 32, 0.01, false)
1618 BENCH_REL (shmtx_w_bare, 32thread_1pct_write, 32, 0.01, false)
1619 BENCH_REL (shmtx_rd_pri, 32thread_1pct_write, 32, 0.01, false)
1620 BENCH_REL (shmtx_r_bare, 32thread_1pct_write, 32, 0.01, false)
1621 BENCH_REL (folly_ticket, 32thread_1pct_write, 32, 0.01, false)
1622 BENCH_REL (boost_shared, 32thread_1pct_write, 32, 0.01, false)
1623 BENCH_REL (pthrd_rwlock, 32thread_1pct_write, 32, 0.01, false)
1624 BENCHMARK_DRAW_LINE()
1625 BENCH_BASE(folly_rwspin, 64thread_1pct_write, 64, 0.01, false)
1626 BENCH_REL (shmtx_wr_pri, 64thread_1pct_write, 64, 0.01, false)
1627 BENCH_REL (shmtx_w_bare, 64thread_1pct_write, 64, 0.01, false)
1628 BENCH_REL (shmtx_rd_pri, 64thread_1pct_write, 64, 0.01, false)
1629 BENCH_REL (shmtx_r_bare, 64thread_1pct_write, 64, 0.01, false)
1630 BENCH_REL (folly_ticket, 64thread_1pct_write, 64, 0.01, false)
1631 BENCH_REL (boost_shared, 64thread_1pct_write, 64, 0.01, false)
1632 BENCH_REL (pthrd_rwlock, 64thread_1pct_write, 64, 0.01, false)
1634 // Worst case scenario for deferred locks. No actual sharing, likely that
1635 // read operations will have to first set the kDeferredReadersPossibleBit,
1636 // and likely that writers will have to scan deferredReaders[].
1637 BENCHMARK_DRAW_LINE()
1638 BENCH_BASE(folly_rwspin, 2thr_2lock_50pct_write, 2, 0.50, true)
1639 BENCH_REL (shmtx_wr_pri, 2thr_2lock_50pct_write, 2, 0.50, true)
1640 BENCH_REL (shmtx_rd_pri, 2thr_2lock_50pct_write, 2, 0.50, true)
1641 BENCH_BASE(folly_rwspin, 4thr_4lock_50pct_write, 4, 0.50, true)
1642 BENCH_REL (shmtx_wr_pri, 4thr_4lock_50pct_write, 4, 0.50, true)
1643 BENCH_REL (shmtx_rd_pri, 4thr_4lock_50pct_write, 4, 0.50, true)
1644 BENCH_BASE(folly_rwspin, 8thr_8lock_50pct_write, 8, 0.50, true)
1645 BENCH_REL (shmtx_wr_pri, 8thr_8lock_50pct_write, 8, 0.50, true)
1646 BENCH_REL (shmtx_rd_pri, 8thr_8lock_50pct_write, 8, 0.50, true)
1647 BENCH_BASE(folly_rwspin, 16thr_16lock_50pct_write, 16, 0.50, true)
1648 BENCH_REL (shmtx_wr_pri, 16thr_16lock_50pct_write, 16, 0.50, true)
1649 BENCH_REL (shmtx_rd_pri, 16thr_16lock_50pct_write, 16, 0.50, true)
1650 BENCH_BASE(folly_rwspin, 32thr_32lock_50pct_write, 32, 0.50, true)
1651 BENCH_REL (shmtx_wr_pri, 32thr_32lock_50pct_write, 32, 0.50, true)
1652 BENCH_REL (shmtx_rd_pri, 32thr_32lock_50pct_write, 32, 0.50, true)
1653 BENCH_BASE(folly_rwspin, 64thr_64lock_50pct_write, 64, 0.50, true)
1654 BENCH_REL (shmtx_wr_pri, 64thr_64lock_50pct_write, 64, 0.50, true)
1655 BENCH_REL (shmtx_rd_pri, 64thr_64lock_50pct_write, 64, 0.50, true)
1656 BENCHMARK_DRAW_LINE()
1657 BENCH_BASE(folly_rwspin, 2thr_2lock_10pct_write, 2, 0.10, true)
1658 BENCH_REL (shmtx_wr_pri, 2thr_2lock_10pct_write, 2, 0.10, true)
1659 BENCH_REL (shmtx_rd_pri, 2thr_2lock_10pct_write, 2, 0.10, true)
1660 BENCH_BASE(folly_rwspin, 4thr_4lock_10pct_write, 4, 0.10, true)
1661 BENCH_REL (shmtx_wr_pri, 4thr_4lock_10pct_write, 4, 0.10, true)
1662 BENCH_REL (shmtx_rd_pri, 4thr_4lock_10pct_write, 4, 0.10, true)
1663 BENCH_BASE(folly_rwspin, 8thr_8lock_10pct_write, 8, 0.10, true)
1664 BENCH_REL (shmtx_wr_pri, 8thr_8lock_10pct_write, 8, 0.10, true)
1665 BENCH_REL (shmtx_rd_pri, 8thr_8lock_10pct_write, 8, 0.10, true)
1666 BENCH_BASE(folly_rwspin, 16thr_16lock_10pct_write, 16, 0.10, true)
1667 BENCH_REL (shmtx_wr_pri, 16thr_16lock_10pct_write, 16, 0.10, true)
1668 BENCH_REL (shmtx_rd_pri, 16thr_16lock_10pct_write, 16, 0.10, true)
1669 BENCH_BASE(folly_rwspin, 32thr_32lock_10pct_write, 32, 0.10, true)
1670 BENCH_REL (shmtx_wr_pri, 32thr_32lock_10pct_write, 32, 0.10, true)
1671 BENCH_REL (shmtx_rd_pri, 32thr_32lock_10pct_write, 32, 0.10, true)
1672 BENCH_BASE(folly_rwspin, 64thr_64lock_10pct_write, 64, 0.10, true)
1673 BENCH_REL (shmtx_wr_pri, 64thr_64lock_10pct_write, 64, 0.10, true)
1674 BENCH_REL (shmtx_rd_pri, 64thr_64lock_10pct_write, 64, 0.10, true)
1675 BENCHMARK_DRAW_LINE()
1676 BENCH_BASE(folly_rwspin, 2thr_2lock_1pct_write, 2, 0.01, true)
1677 BENCH_REL (shmtx_wr_pri, 2thr_2lock_1pct_write, 2, 0.01, true)
1678 BENCH_REL (shmtx_rd_pri, 2thr_2lock_1pct_write, 2, 0.01, true)
1679 BENCH_BASE(folly_rwspin, 4thr_4lock_1pct_write, 4, 0.01, true)
1680 BENCH_REL (shmtx_wr_pri, 4thr_4lock_1pct_write, 4, 0.01, true)
1681 BENCH_REL (shmtx_rd_pri, 4thr_4lock_1pct_write, 4, 0.01, true)
1682 BENCH_BASE(folly_rwspin, 8thr_8lock_1pct_write, 8, 0.01, true)
1683 BENCH_REL (shmtx_wr_pri, 8thr_8lock_1pct_write, 8, 0.01, true)
1684 BENCH_REL (shmtx_rd_pri, 8thr_8lock_1pct_write, 8, 0.01, true)
1685 BENCH_BASE(folly_rwspin, 16thr_16lock_1pct_write, 16, 0.01, true)
1686 BENCH_REL (shmtx_wr_pri, 16thr_16lock_1pct_write, 16, 0.01, true)
1687 BENCH_REL (shmtx_rd_pri, 16thr_16lock_1pct_write, 16, 0.01, true)
1688 BENCH_BASE(folly_rwspin, 32thr_32lock_1pct_write, 32, 0.01, true)
1689 BENCH_REL (shmtx_wr_pri, 32thr_32lock_1pct_write, 32, 0.01, true)
1690 BENCH_REL (shmtx_rd_pri, 32thr_32lock_1pct_write, 32, 0.01, true)
1691 BENCH_BASE(folly_rwspin, 64thr_64lock_1pct_write, 64, 0.01, true)
1692 BENCH_REL (shmtx_wr_pri, 64thr_64lock_1pct_write, 64, 0.01, true)
1693 BENCH_REL (shmtx_rd_pri, 64thr_64lock_1pct_write, 64, 0.01, true)
1695 // Ping-pong tests have a scaled number of iterations, because their burn
1696 // loop would make them too slow otherwise. Ping-pong with burn count of
1697 // 100k or 300k shows the advantage of soft-spin, reducing the cost of
1698 // each wakeup by about 20 usec. (Take benchmark reported difference,
1699 // ~400 nanos, multiply by the scale of 100, then divide by 2 because
1700 // each round has two wakeups.)
1701 BENCHMARK_DRAW_LINE()
1702 BENCHMARK_DRAW_LINE()
1703 BENCH_BASE(folly_rwspin_ping_pong, burn0, 1, 0)
1704 BENCH_REL (shmtx_w_bare_ping_pong, burn0, 1, 0)
1705 BENCH_REL (shmtx_r_bare_ping_pong, burn0, 1, 0)
1706 BENCH_REL (folly_ticket_ping_pong, burn0, 1, 0)
1707 BENCH_REL (boost_shared_ping_pong, burn0, 1, 0)
1708 BENCH_REL (pthrd_rwlock_ping_pong, burn0, 1, 0)
1709 BENCHMARK_DRAW_LINE()
1710 BENCH_BASE(folly_rwspin_ping_pong, burn100k, 100, 100000)
1711 BENCH_REL (shmtx_w_bare_ping_pong, burn100k, 100, 100000)
1712 BENCH_REL (shmtx_r_bare_ping_pong, burn100k, 100, 100000)
1713 BENCH_REL (folly_ticket_ping_pong, burn100k, 100, 100000)
1714 BENCH_REL (boost_shared_ping_pong, burn100k, 100, 100000)
1715 BENCH_REL (pthrd_rwlock_ping_pong, burn100k, 100, 100000)
1716 BENCHMARK_DRAW_LINE()
1717 BENCH_BASE(folly_rwspin_ping_pong, burn300k, 100, 300000)
1718 BENCH_REL (shmtx_w_bare_ping_pong, burn300k, 100, 300000)
1719 BENCH_REL (shmtx_r_bare_ping_pong, burn300k, 100, 300000)
1720 BENCH_REL (folly_ticket_ping_pong, burn300k, 100, 300000)
1721 BENCH_REL (boost_shared_ping_pong, burn300k, 100, 300000)
1722 BENCH_REL (pthrd_rwlock_ping_pong, burn300k, 100, 300000)
1723 BENCHMARK_DRAW_LINE()
1724 BENCH_BASE(folly_rwspin_ping_pong, burn1M, 1000, 1000000)
1725 BENCH_REL (shmtx_w_bare_ping_pong, burn1M, 1000, 1000000)
1726 BENCH_REL (shmtx_r_bare_ping_pong, burn1M, 1000, 1000000)
1727 BENCH_REL (folly_ticket_ping_pong, burn1M, 1000, 1000000)
1728 BENCH_REL (boost_shared_ping_pong, burn1M, 1000, 1000000)
1729 BENCH_REL (pthrd_rwlock_ping_pong, burn1M, 1000, 1000000)
1731 // Reproduce with 10 minutes and
1733 // shared_mutex_test --benchmark --bm_min_iters=1000000
1735 // Comparison use folly::RWSpinLock as the baseline, with the
1736 // following row being the default SharedMutex (using *Holder or
1737 // Token-ful methods).
1738 // ============================================================================
1739 // folly/experimental/test/SharedMutexTest.cpp relative time/iter iters/s
1740 // ============================================================================
1741 // single_thread_lock_shared_unlock_shared 22.78ns 43.89M
1742 // single_thread_lock_unlock 26.01ns 38.45M
1743 // ----------------------------------------------------------------------------
1744 // ----------------------------------------------------------------------------
1745 // folly_rwspin_reads(1thread) 15.09ns 66.25M
1746 // shmtx_wr_pri_reads(1thread) 69.89% 21.60ns 46.30M
1747 // shmtx_w_bare_reads(1thread) 58.25% 25.91ns 38.59M
1748 // shmtx_rd_pri_reads(1thread) 72.50% 20.82ns 48.03M
1749 // shmtx_r_bare_reads(1thread) 58.27% 25.91ns 38.60M
1750 // folly_ticket_reads(1thread) 54.80% 27.55ns 36.30M
1751 // boost_shared_reads(1thread) 10.88% 138.80ns 7.20M
1752 // pthrd_rwlock_reads(1thread) 40.68% 37.11ns 26.95M
1753 // ----------------------------------------------------------------------------
1754 // folly_rwspin_reads(2thread) 92.63ns 10.80M
1755 // shmtx_wr_pri_reads(2thread) 462.86% 20.01ns 49.97M
1756 // shmtx_w_bare_reads(2thread) 430.53% 21.51ns 46.48M
1757 // shmtx_rd_pri_reads(2thread) 487.13% 19.01ns 52.59M
1758 // shmtx_r_bare_reads(2thread) 433.35% 21.37ns 46.79M
1759 // folly_ticket_reads(2thread) 69.82% 132.67ns 7.54M
1760 // boost_shared_reads(2thread) 36.66% 252.63ns 3.96M
1761 // pthrd_rwlock_reads(2thread) 127.76% 72.50ns 13.79M
1762 // ----------------------------------------------------------------------------
1763 // folly_rwspin_reads(4thread) 97.45ns 10.26M
1764 // shmtx_wr_pri_reads(4thread) 978.22% 9.96ns 100.38M
1765 // shmtx_w_bare_reads(4thread) 908.35% 10.73ns 93.21M
1766 // shmtx_rd_pri_reads(4thread) 1032.29% 9.44ns 105.93M
1767 // shmtx_r_bare_reads(4thread) 912.38% 10.68ns 93.63M
1768 // folly_ticket_reads(4thread) 46.08% 211.46ns 4.73M
1769 // boost_shared_reads(4thread) 25.00% 389.74ns 2.57M
1770 // pthrd_rwlock_reads(4thread) 47.53% 205.01ns 4.88M
1771 // ----------------------------------------------------------------------------
1772 // folly_rwspin_reads(8thread) 147.24ns 6.79M
1773 // shmtx_wr_pri_reads(8thread) 2915.66% 5.05ns 198.02M
1774 // shmtx_w_bare_reads(8thread) 2699.32% 5.45ns 183.32M
1775 // shmtx_rd_pri_reads(8thread) 3092.58% 4.76ns 210.03M
1776 // shmtx_r_bare_reads(8thread) 2744.63% 5.36ns 186.40M
1777 // folly_ticket_reads(8thread) 54.84% 268.47ns 3.72M
1778 // boost_shared_reads(8thread) 42.40% 347.30ns 2.88M
1779 // pthrd_rwlock_reads(8thread) 78.90% 186.63ns 5.36M
1780 // ----------------------------------------------------------------------------
1781 // folly_rwspin_reads(16thread) 166.25ns 6.02M
1782 // shmtx_wr_pri_reads(16thread) 6133.03% 2.71ns 368.91M
1783 // shmtx_w_bare_reads(16thread) 5936.05% 2.80ns 357.06M
1784 // shmtx_rd_pri_reads(16thread) 6786.57% 2.45ns 408.22M
1785 // shmtx_r_bare_reads(16thread) 5995.54% 2.77ns 360.64M
1786 // folly_ticket_reads(16thread) 56.35% 295.01ns 3.39M
1787 // boost_shared_reads(16thread) 51.62% 322.08ns 3.10M
1788 // pthrd_rwlock_reads(16thread) 92.47% 179.79ns 5.56M
1789 // ----------------------------------------------------------------------------
1790 // folly_rwspin_reads(32thread) 107.72ns 9.28M
1791 // shmtx_wr_pri_reads(32thread) 6772.80% 1.59ns 628.77M
1792 // shmtx_w_bare_reads(32thread) 6236.13% 1.73ns 578.94M
1793 // shmtx_rd_pri_reads(32thread) 8143.32% 1.32ns 756.00M
1794 // shmtx_r_bare_reads(32thread) 6485.18% 1.66ns 602.06M
1795 // folly_ticket_reads(32thread) 35.12% 306.73ns 3.26M
1796 // boost_shared_reads(32thread) 28.19% 382.17ns 2.62M
1797 // pthrd_rwlock_reads(32thread) 65.29% 164.99ns 6.06M
1798 // ----------------------------------------------------------------------------
1799 // folly_rwspin_reads(64thread) 119.46ns 8.37M
1800 // shmtx_wr_pri_reads(64thread) 6744.92% 1.77ns 564.60M
1801 // shmtx_w_bare_reads(64thread) 6268.50% 1.91ns 524.72M
1802 // shmtx_rd_pri_reads(64thread) 7508.56% 1.59ns 628.52M
1803 // shmtx_r_bare_reads(64thread) 6299.53% 1.90ns 527.32M
1804 // folly_ticket_reads(64thread) 37.42% 319.26ns 3.13M
1805 // boost_shared_reads(64thread) 32.58% 366.70ns 2.73M
1806 // pthrd_rwlock_reads(64thread) 73.64% 162.24ns 6.16M
1807 // ----------------------------------------------------------------------------
1808 // ----------------------------------------------------------------------------
1809 // folly_rwspin(1thread_all_write) 25.51ns 39.19M
1810 // shmtx_wr_pri(1thread_all_write) 97.38% 26.20ns 38.17M
1811 // shmtx_rd_pri(1thread_all_write) 97.55% 26.16ns 38.23M
1812 // folly_ticket(1thread_all_write) 90.98% 28.04ns 35.66M
1813 // boost_shared(1thread_all_write) 16.80% 151.89ns 6.58M
1814 // pthrd_rwlock(1thread_all_write) 63.86% 39.96ns 25.03M
1815 // pthrd_mutex_(1thread_all_write) 82.05% 31.09ns 32.16M
1816 // ----------------------------------------------------------------------------
1817 // folly_rwspin(2thread_all_write) 100.70ns 9.93M
1818 // shmtx_wr_pri(2thread_all_write) 40.83% 246.61ns 4.05M
1819 // shmtx_rd_pri(2thread_all_write) 40.53% 248.44ns 4.03M
1820 // folly_ticket(2thread_all_write) 58.49% 172.17ns 5.81M
1821 // boost_shared(2thread_all_write) 24.26% 415.00ns 2.41M
1822 // pthrd_rwlock(2thread_all_write) 41.35% 243.49ns 4.11M
1823 // pthrd_mutex_(2thread_all_write) 146.91% 68.55ns 14.59M
1824 // ----------------------------------------------------------------------------
1825 // folly_rwspin(4thread_all_write) 199.52ns 5.01M
1826 // shmtx_wr_pri(4thread_all_write) 51.71% 385.86ns 2.59M
1827 // shmtx_rd_pri(4thread_all_write) 49.43% 403.62ns 2.48M
1828 // folly_ticket(4thread_all_write) 117.88% 169.26ns 5.91M
1829 // boost_shared(4thread_all_write) 9.81% 2.03us 491.48K
1830 // pthrd_rwlock(4thread_all_write) 28.23% 706.69ns 1.42M
1831 // pthrd_mutex_(4thread_all_write) 111.54% 178.88ns 5.59M
1832 // ----------------------------------------------------------------------------
1833 // folly_rwspin(8thread_all_write) 304.61ns 3.28M
1834 // shmtx_wr_pri(8thread_all_write) 69.77% 436.59ns 2.29M
1835 // shmtx_rd_pri(8thread_all_write) 66.58% 457.51ns 2.19M
1836 // folly_ticket(8thread_all_write) 141.00% 216.03ns 4.63M
1837 // boost_shared(8thread_all_write) 6.11% 4.99us 200.59K
1838 // pthrd_rwlock(8thread_all_write) 38.03% 800.88ns 1.25M
1839 // pthrd_mutex_(8thread_all_write) 177.66% 171.45ns 5.83M
1840 // ----------------------------------------------------------------------------
1841 // folly_rwspin(16thread_all_write) 576.97ns 1.73M
1842 // shmtx_wr_pri(16thread_all_write) 105.72% 545.77ns 1.83M
1843 // shmtx_rd_pri(16thread_all_write) 105.13% 548.83ns 1.82M
1844 // folly_ticket(16thread_all_write) 161.70% 356.82ns 2.80M
1845 // boost_shared(16thread_all_write) 7.73% 7.46us 134.03K
1846 // pthrd_rwlock(16thread_all_write) 96.88% 595.54ns 1.68M
1847 // pthrd_mutex_(16thread_all_write) 330.44% 174.61ns 5.73M
1848 // ----------------------------------------------------------------------------
1849 // folly_rwspin(32thread_all_write) 1.41us 707.76K
1850 // shmtx_wr_pri(32thread_all_write) 240.46% 587.58ns 1.70M
1851 // shmtx_rd_pri(32thread_all_write) 393.71% 358.87ns 2.79M
1852 // folly_ticket(32thread_all_write) 325.07% 434.65ns 2.30M
1853 // boost_shared(32thread_all_write) 18.57% 7.61us 131.43K
1854 // pthrd_rwlock(32thread_all_write) 266.78% 529.62ns 1.89M
1855 // pthrd_mutex_(32thread_all_write) 877.89% 160.94ns 6.21M
1856 // ----------------------------------------------------------------------------
1857 // folly_rwspin(64thread_all_write) 1.76us 566.94K
1858 // shmtx_wr_pri(64thread_all_write) 255.67% 689.91ns 1.45M
1859 // shmtx_rd_pri(64thread_all_write) 468.82% 376.23ns 2.66M
1860 // folly_ticket(64thread_all_write) 294.72% 598.49ns 1.67M
1861 // boost_shared(64thread_all_write) 23.39% 7.54us 132.58K
1862 // pthrd_rwlock(64thread_all_write) 321.39% 548.83ns 1.82M
1863 // pthrd_mutex_(64thread_all_write) 1165.04% 151.40ns 6.61M
1864 // ----------------------------------------------------------------------------
1865 // ----------------------------------------------------------------------------
1866 // folly_rwspin(1thread_10pct_write) 19.51ns 51.26M
1867 // shmtx_wr_pri(1thread_10pct_write) 83.25% 23.43ns 42.67M
1868 // shmtx_rd_pri(1thread_10pct_write) 83.31% 23.42ns 42.71M
1869 // folly_ticket(1thread_10pct_write) 70.88% 27.52ns 36.34M
1870 // boost_shared(1thread_10pct_write) 13.09% 148.99ns 6.71M
1871 // pthrd_rwlock(1thread_10pct_write) 47.41% 41.15ns 24.30M
1872 // ----------------------------------------------------------------------------
1873 // folly_rwspin(2thread_10pct_write) 159.42ns 6.27M
1874 // shmtx_wr_pri(2thread_10pct_write) 188.44% 84.60ns 11.82M
1875 // shmtx_rd_pri(2thread_10pct_write) 188.29% 84.67ns 11.81M
1876 // folly_ticket(2thread_10pct_write) 140.28% 113.64ns 8.80M
1877 // boost_shared(2thread_10pct_write) 42.09% 378.81ns 2.64M
1878 // pthrd_rwlock(2thread_10pct_write) 103.86% 153.49ns 6.51M
1879 // ----------------------------------------------------------------------------
1880 // folly_rwspin(4thread_10pct_write) 193.35ns 5.17M
1881 // shmtx_wr_pri(4thread_10pct_write) 184.30% 104.91ns 9.53M
1882 // shmtx_rd_pri(4thread_10pct_write) 163.76% 118.07ns 8.47M
1883 // folly_ticket(4thread_10pct_write) 124.07% 155.84ns 6.42M
1884 // boost_shared(4thread_10pct_write) 16.32% 1.18us 843.92K
1885 // pthrd_rwlock(4thread_10pct_write) 48.59% 397.94ns 2.51M
1886 // ----------------------------------------------------------------------------
1887 // folly_rwspin(8thread_10pct_write) 373.17ns 2.68M
1888 // shmtx_wr_pri(8thread_10pct_write) 252.02% 148.08ns 6.75M
1889 // shmtx_rd_pri(8thread_10pct_write) 203.59% 183.30ns 5.46M
1890 // folly_ticket(8thread_10pct_write) 184.37% 202.40ns 4.94M
1891 // boost_shared(8thread_10pct_write) 15.85% 2.35us 424.72K
1892 // pthrd_rwlock(8thread_10pct_write) 83.03% 449.45ns 2.22M
1893 // ----------------------------------------------------------------------------
1894 // folly_rwspin(16thread_10pct_write) 742.87ns 1.35M
1895 // shmtx_wr_pri(16thread_10pct_write) 344.27% 215.78ns 4.63M
1896 // shmtx_rd_pri(16thread_10pct_write) 287.04% 258.80ns 3.86M
1897 // folly_ticket(16thread_10pct_write) 277.25% 267.94ns 3.73M
1898 // boost_shared(16thread_10pct_write) 15.33% 4.85us 206.30K
1899 // pthrd_rwlock(16thread_10pct_write) 158.34% 469.16ns 2.13M
1900 // ----------------------------------------------------------------------------
1901 // folly_rwspin(32thread_10pct_write) 799.97ns 1.25M
1902 // shmtx_wr_pri(32thread_10pct_write) 351.40% 227.65ns 4.39M
1903 // shmtx_rd_pri(32thread_10pct_write) 341.71% 234.11ns 4.27M
1904 // folly_ticket(32thread_10pct_write) 245.91% 325.31ns 3.07M
1905 // boost_shared(32thread_10pct_write) 7.72% 10.36us 96.56K
1906 // pthrd_rwlock(32thread_10pct_write) 165.87% 482.30ns 2.07M
1907 // ----------------------------------------------------------------------------
1908 // folly_rwspin(64thread_10pct_write) 1.12us 892.01K
1909 // shmtx_wr_pri(64thread_10pct_write) 429.84% 260.81ns 3.83M
1910 // shmtx_rd_pri(64thread_10pct_write) 456.93% 245.35ns 4.08M
1911 // folly_ticket(64thread_10pct_write) 219.21% 511.42ns 1.96M
1912 // boost_shared(64thread_10pct_write) 5.43% 20.65us 48.44K
1913 // pthrd_rwlock(64thread_10pct_write) 233.93% 479.23ns 2.09M
1914 // ----------------------------------------------------------------------------
1915 // ----------------------------------------------------------------------------
1916 // folly_rwspin(1thread_1pct_write) 18.88ns 52.98M
1917 // shmtx_wr_pri(1thread_1pct_write) 81.53% 23.15ns 43.19M
1918 // shmtx_w_bare(1thread_1pct_write) 67.90% 27.80ns 35.97M
1919 // shmtx_rd_pri(1thread_1pct_write) 81.50% 23.16ns 43.18M
1920 // shmtx_r_bare(1thread_1pct_write) 67.74% 27.86ns 35.89M
1921 // folly_ticket(1thread_1pct_write) 68.68% 27.48ns 36.39M
1922 // boost_shared(1thread_1pct_write) 12.80% 147.51ns 6.78M
1923 // pthrd_rwlock(1thread_1pct_write) 45.81% 41.20ns 24.27M
1924 // ----------------------------------------------------------------------------
1925 // folly_rwspin(2thread_1pct_write) 125.85ns 7.95M
1926 // shmtx_wr_pri(2thread_1pct_write) 359.04% 35.05ns 28.53M
1927 // shmtx_w_bare(2thread_1pct_write) 475.60% 26.46ns 37.79M
1928 // shmtx_rd_pri(2thread_1pct_write) 332.75% 37.82ns 26.44M
1929 // shmtx_r_bare(2thread_1pct_write) 115.64% 108.83ns 9.19M
1930 // folly_ticket(2thread_1pct_write) 140.24% 89.74ns 11.14M
1931 // boost_shared(2thread_1pct_write) 40.62% 309.82ns 3.23M
1932 // pthrd_rwlock(2thread_1pct_write) 134.67% 93.45ns 10.70M
1933 // ----------------------------------------------------------------------------
1934 // folly_rwspin(4thread_1pct_write) 126.70ns 7.89M
1935 // shmtx_wr_pri(4thread_1pct_write) 422.20% 30.01ns 33.32M
1936 // shmtx_w_bare(4thread_1pct_write) 403.52% 31.40ns 31.85M
1937 // shmtx_rd_pri(4thread_1pct_write) 282.50% 44.85ns 22.30M
1938 // shmtx_r_bare(4thread_1pct_write) 66.30% 191.10ns 5.23M
1939 // folly_ticket(4thread_1pct_write) 91.93% 137.83ns 7.26M
1940 // boost_shared(4thread_1pct_write) 22.74% 557.10ns 1.80M
1941 // pthrd_rwlock(4thread_1pct_write) 55.66% 227.62ns 4.39M
1942 // ----------------------------------------------------------------------------
1943 // folly_rwspin(8thread_1pct_write) 169.42ns 5.90M
1944 // shmtx_wr_pri(8thread_1pct_write) 567.81% 29.84ns 33.51M
1945 // shmtx_w_bare(8thread_1pct_write) 519.18% 32.63ns 30.64M
1946 // shmtx_rd_pri(8thread_1pct_write) 172.36% 98.30ns 10.17M
1947 // shmtx_r_bare(8thread_1pct_write) 75.56% 224.21ns 4.46M
1948 // folly_ticket(8thread_1pct_write) 104.03% 162.85ns 6.14M
1949 // boost_shared(8thread_1pct_write) 22.01% 769.73ns 1.30M
1950 // pthrd_rwlock(8thread_1pct_write) 71.79% 235.99ns 4.24M
1951 // ----------------------------------------------------------------------------
1952 // folly_rwspin(16thread_1pct_write) 385.88ns 2.59M
1953 // shmtx_wr_pri(16thread_1pct_write) 1039.03% 37.14ns 26.93M
1954 // shmtx_w_bare(16thread_1pct_write) 997.26% 38.69ns 25.84M
1955 // shmtx_rd_pri(16thread_1pct_write) 263.60% 146.39ns 6.83M
1956 // shmtx_r_bare(16thread_1pct_write) 173.16% 222.85ns 4.49M
1957 // folly_ticket(16thread_1pct_write) 179.37% 215.13ns 4.65M
1958 // boost_shared(16thread_1pct_write) 26.95% 1.43us 698.42K
1959 // pthrd_rwlock(16thread_1pct_write) 166.70% 231.48ns 4.32M
1960 // ----------------------------------------------------------------------------
1961 // folly_rwspin(32thread_1pct_write) 382.49ns 2.61M
1962 // shmtx_wr_pri(32thread_1pct_write) 1046.64% 36.54ns 27.36M
1963 // shmtx_w_bare(32thread_1pct_write) 922.87% 41.45ns 24.13M
1964 // shmtx_rd_pri(32thread_1pct_write) 251.93% 151.82ns 6.59M
1965 // shmtx_r_bare(32thread_1pct_write) 176.44% 216.78ns 4.61M
1966 // folly_ticket(32thread_1pct_write) 131.07% 291.82ns 3.43M
1967 // boost_shared(32thread_1pct_write) 12.77% 2.99us 333.95K
1968 // pthrd_rwlock(32thread_1pct_write) 173.43% 220.55ns 4.53M
1969 // ----------------------------------------------------------------------------
1970 // folly_rwspin(64thread_1pct_write) 510.54ns 1.96M
1971 // shmtx_wr_pri(64thread_1pct_write) 1378.27% 37.04ns 27.00M
1972 // shmtx_w_bare(64thread_1pct_write) 1178.24% 43.33ns 23.08M
1973 // shmtx_rd_pri(64thread_1pct_write) 325.29% 156.95ns 6.37M
1974 // shmtx_r_bare(64thread_1pct_write) 247.82% 206.02ns 4.85M
1975 // folly_ticket(64thread_1pct_write) 117.87% 433.13ns 2.31M
1976 // boost_shared(64thread_1pct_write) 9.45% 5.40us 185.09K
1977 // pthrd_rwlock(64thread_1pct_write) 236.72% 215.68ns 4.64M
1978 // ----------------------------------------------------------------------------
1979 // folly_rwspin(2thr_2lock_50pct_write) 10.85ns 92.15M
1980 // shmtx_wr_pri(2thr_2lock_50pct_write) 81.73% 13.28ns 75.32M
1981 // shmtx_rd_pri(2thr_2lock_50pct_write) 81.82% 13.26ns 75.40M
1982 // folly_rwspin(4thr_4lock_50pct_write) 5.29ns 188.90M
1983 // shmtx_wr_pri(4thr_4lock_50pct_write) 80.89% 6.54ns 152.80M
1984 // shmtx_rd_pri(4thr_4lock_50pct_write) 81.07% 6.53ns 153.14M
1985 // folly_rwspin(8thr_8lock_50pct_write) 2.63ns 380.57M
1986 // shmtx_wr_pri(8thr_8lock_50pct_write) 80.56% 3.26ns 306.57M
1987 // shmtx_rd_pri(8thr_8lock_50pct_write) 80.29% 3.27ns 305.54M
1988 // folly_rwspin(16thr_16lock_50pct_write) 1.31ns 764.70M
1989 // shmtx_wr_pri(16thr_16lock_50pct_write) 79.32% 1.65ns 606.54M
1990 // shmtx_rd_pri(16thr_16lock_50pct_write) 79.62% 1.64ns 608.84M
1991 // folly_rwspin(32thr_32lock_50pct_write) 1.20ns 836.75M
1992 // shmtx_wr_pri(32thr_32lock_50pct_write) 91.67% 1.30ns 767.07M
1993 // shmtx_rd_pri(32thr_32lock_50pct_write) 92.00% 1.30ns 769.82M
1994 // folly_rwspin(64thr_64lock_50pct_write) 1.39ns 717.80M
1995 // shmtx_wr_pri(64thr_64lock_50pct_write) 93.21% 1.49ns 669.08M
1996 // shmtx_rd_pri(64thr_64lock_50pct_write) 92.49% 1.51ns 663.89M
1997 // ----------------------------------------------------------------------------
1998 // folly_rwspin(2thr_2lock_10pct_write) 10.24ns 97.70M
1999 // shmtx_wr_pri(2thr_2lock_10pct_write) 76.46% 13.39ns 74.70M
2000 // shmtx_rd_pri(2thr_2lock_10pct_write) 76.35% 13.41ns 74.60M
2001 // folly_rwspin(4thr_4lock_10pct_write) 5.02ns 199.03M
2002 // shmtx_wr_pri(4thr_4lock_10pct_write) 75.83% 6.63ns 150.91M
2003 // shmtx_rd_pri(4thr_4lock_10pct_write) 76.10% 6.60ns 151.46M
2004 // folly_rwspin(8thr_8lock_10pct_write) 2.47ns 405.50M
2005 // shmtx_wr_pri(8thr_8lock_10pct_write) 74.54% 3.31ns 302.27M
2006 // shmtx_rd_pri(8thr_8lock_10pct_write) 74.85% 3.29ns 303.52M
2007 // folly_rwspin(16thr_16lock_10pct_write) 1.22ns 818.68M
2008 // shmtx_wr_pri(16thr_16lock_10pct_write) 73.35% 1.67ns 600.47M
2009 // shmtx_rd_pri(16thr_16lock_10pct_write) 73.38% 1.66ns 600.73M
2010 // folly_rwspin(32thr_32lock_10pct_write) 1.21ns 827.95M
2011 // shmtx_wr_pri(32thr_32lock_10pct_write) 96.13% 1.26ns 795.89M
2012 // shmtx_rd_pri(32thr_32lock_10pct_write) 96.01% 1.26ns 794.95M
2013 // folly_rwspin(64thr_64lock_10pct_write) 1.40ns 716.17M
2014 // shmtx_wr_pri(64thr_64lock_10pct_write) 96.91% 1.44ns 694.03M
2015 // shmtx_rd_pri(64thr_64lock_10pct_write) 96.85% 1.44ns 693.64M
2016 // ----------------------------------------------------------------------------
2017 // folly_rwspin(2thr_2lock_1pct_write) 10.11ns 98.91M
2018 // shmtx_wr_pri(2thr_2lock_1pct_write) 75.07% 13.47ns 74.25M
2019 // shmtx_rd_pri(2thr_2lock_1pct_write) 74.98% 13.48ns 74.16M
2020 // folly_rwspin(4thr_4lock_1pct_write) 4.96ns 201.77M
2021 // shmtx_wr_pri(4thr_4lock_1pct_write) 74.59% 6.64ns 150.49M
2022 // shmtx_rd_pri(4thr_4lock_1pct_write) 74.60% 6.64ns 150.51M
2023 // folly_rwspin(8thr_8lock_1pct_write) 2.44ns 410.42M
2024 // shmtx_wr_pri(8thr_8lock_1pct_write) 73.68% 3.31ns 302.41M
2025 // shmtx_rd_pri(8thr_8lock_1pct_write) 73.38% 3.32ns 301.16M
2026 // folly_rwspin(16thr_16lock_1pct_write) 1.21ns 827.53M
2027 // shmtx_wr_pri(16thr_16lock_1pct_write) 72.11% 1.68ns 596.74M
2028 // shmtx_rd_pri(16thr_16lock_1pct_write) 72.23% 1.67ns 597.73M
2029 // folly_rwspin(32thr_32lock_1pct_write) 1.22ns 819.53M
2030 // shmtx_wr_pri(32thr_32lock_1pct_write) 98.17% 1.24ns 804.50M
2031 // shmtx_rd_pri(32thr_32lock_1pct_write) 98.21% 1.24ns 804.86M
2032 // folly_rwspin(64thr_64lock_1pct_write) 1.41ns 710.26M
2033 // shmtx_wr_pri(64thr_64lock_1pct_write) 97.81% 1.44ns 694.71M
2034 // shmtx_rd_pri(64thr_64lock_1pct_write) 99.44% 1.42ns 706.28M
2035 // ----------------------------------------------------------------------------
2036 // ----------------------------------------------------------------------------
2037 // folly_rwspin_ping_pong(burn0) 641.24ns 1.56M
2038 // shmtx_w_bare_ping_pong(burn0) 91.07% 704.12ns 1.42M
2039 // shmtx_r_bare_ping_pong(burn0) 78.70% 814.84ns 1.23M
2040 // folly_ticket_ping_pong(burn0) 85.67% 748.53ns 1.34M
2041 // boost_shared_ping_pong(burn0) 5.58% 11.50us 86.96K
2042 // pthrd_rwlock_ping_pong(burn0) 8.81% 7.28us 137.40K
2043 // ----------------------------------------------------------------------------
2044 // folly_rwspin_ping_pong(burn100k) 678.97ns 1.47M
2045 // shmtx_w_bare_ping_pong(burn100k) 99.73% 680.78ns 1.47M
2046 // shmtx_r_bare_ping_pong(burn100k) 98.67% 688.13ns 1.45M
2047 // folly_ticket_ping_pong(burn100k) 99.31% 683.68ns 1.46M
2048 // boost_shared_ping_pong(burn100k) 58.23% 1.17us 857.64K
2049 // pthrd_rwlock_ping_pong(burn100k) 57.43% 1.18us 845.86K
2050 // ----------------------------------------------------------------------------
2051 // folly_rwspin_ping_pong(burn300k) 2.03us 492.99K
2052 // shmtx_w_bare_ping_pong(burn300k) 99.98% 2.03us 492.88K
2053 // shmtx_r_bare_ping_pong(burn300k) 99.94% 2.03us 492.68K
2054 // folly_ticket_ping_pong(burn300k) 99.88% 2.03us 492.40K
2055 // boost_shared_ping_pong(burn300k) 81.43% 2.49us 401.47K
2056 // pthrd_rwlock_ping_pong(burn300k) 83.22% 2.44us 410.29K
2057 // ----------------------------------------------------------------------------
2058 // folly_rwspin_ping_pong(burn1M) 677.07ns 1.48M
2059 // shmtx_w_bare_ping_pong(burn1M) 100.50% 673.74ns 1.48M
2060 // shmtx_r_bare_ping_pong(burn1M) 100.14% 676.12ns 1.48M
2061 // folly_ticket_ping_pong(burn1M) 100.44% 674.14ns 1.48M
2062 // boost_shared_ping_pong(burn1M) 93.04% 727.72ns 1.37M
2063 // pthrd_rwlock_ping_pong(burn1M) 94.52% 716.30ns 1.40M
2064 // ============================================================================
2066 int main(int argc, char** argv) {
2067 (void)folly_rwspin_reads;
2068 (void)shmtx_wr_pri_reads;
2069 (void)shmtx_w_bare_reads;
2070 (void)shmtx_rd_pri_reads;
2071 (void)shmtx_r_bare_reads;
2072 (void)folly_ticket_reads;
2073 (void)boost_shared_reads;
2074 (void)pthrd_rwlock_reads;
2084 (void)folly_rwspin_ping_pong;
2085 (void)shmtx_w_bare_ping_pong;
2086 (void)shmtx_r_bare_ping_pong;
2087 (void)folly_ticket_ping_pong;
2088 (void)boost_shared_ping_pong;
2089 (void)pthrd_rwlock_ping_pong;
2091 testing::InitGoogleTest(&argc, argv);
2092 gflags::ParseCommandLineFlags(&argc, &argv, true);
2093 int rv = RUN_ALL_TESTS();
2094 folly::runBenchmarksOnFlag();