5 class FollySyncTest_Parallel: public cds_test::stress_fixture {
7 static size_t s_nThreadCount;
8 // Simulate as the data protected by the lock.
9 static size_t locked_data;
10 static std::atomic<RcuData*> rcu_data;
11 // For RCU, we mostly want to benchmark the readers (cause it's designed for
12 // very fast readers and occasional writers). We have a writer thread that
13 // runs nonstop until all other reader threads are done.
14 static std::atomic_bool rcu_readers_done;
16 static size_t s_nMicroLockPassCount;
18 static size_t s_nMicroSpinLockPassCount;
20 static size_t s_nPicoSpinLockPassCount;
22 static size_t s_nSharedMutexPassCount;
24 static size_t s_nRWSpinLockPassCount;
26 static size_t s_nRWTicketSpinLockPassCount;
28 static size_t s_nRcuReaderPassCount;
29 static size_t s_nRcuWriterPassCount;
30 static size_t s_nRcuWriterFrequency;
32 static unsigned s_nSharedMutexWritePercentage;
33 static unsigned s_nRWSpinLockWritePercentage;
34 static unsigned s_nRWTicketSpinLockWritePercentage;
36 static void SetUpTestCase() {
37 const cds_test::config& cfg = get_config("ParallelFollySync");
38 GetConfigNonZeroExpected(ThreadCount, 4);
39 GetConfigNonZeroExpected(MicroLockPassCount, 2000000000);
40 GetConfigNonZeroExpected(MicroSpinLockPassCount, 1500000000);
41 GetConfigNonZeroExpected(PicoSpinLockPassCount, 2700000000);
42 GetConfigNonZeroExpected(SharedMutexPassCount, 5000000);
43 GetConfigNonZeroExpected(RWSpinLockPassCount, 5000000);
44 GetConfigNonZeroExpected(RWTicketSpinLockPassCount, 5000000);
45 GetConfigNonZeroExpected(RcuReaderPassCount, 10000);
46 GetConfigNonZeroExpected(RcuWriterPassCount, 500);
47 // Every 100 ms by default there will be a writer.
48 GetConfigNonZeroExpected(RcuWriterFrequency, 100);
50 GetConfigNonZeroExpected(SharedMutexWritePercentage, 5);
51 GetConfigNonZeroExpected(RWSpinLockWritePercentage, 5);
52 GetConfigNonZeroExpected(RWTicketSpinLockWritePercentage, 5);
54 rcu_data.store(new RcuData(), std::memory_order_relaxed);
57 static void run_rcu_writer_sync() {
58 while (!rcu_readers_done.load(std::memory_order_acquire)) {
59 auto *old_data = rcu_data.load(std::memory_order_relaxed);
60 auto *new_data = new RcuData(*old_data);
63 rcu_data.store(new_data, std::memory_order_relaxed);
64 folly::synchronize_rcu();
66 std::this_thread::sleep_for(
67 std::chrono::milliseconds(s_nRcuWriterFrequency));
71 static void run_rcu_writer_no_sync() {
72 while (!rcu_readers_done.load(std::memory_order_acquire)) {
73 auto *old_data = rcu_data.load(std::memory_order_relaxed);
74 auto *new_data = new RcuData(*old_data);
77 rcu_data.store(new_data, std::memory_order_relaxed);
78 folly::rcu_retire(old_data);
79 std::this_thread::sleep_for(
80 std::chrono::milliseconds(s_nRcuWriterFrequency));
84 static void run_rcu_reader(size_t pass_count) {
86 for (size_t count = 0; count < pass_count; count++) {
88 auto *data = rcu_data.load(std::memory_order_relaxed);
89 sum += (data->d1 + data->d2);
91 std::cout << "Reader done" << std::endl;
92 // Just want to simulate the reading.
96 template <typename Lock>
97 static void run_rw_lock(Lock *l, size_t pass_count,
98 unsigned write_percentage) {
100 for (size_t count = 0; count < pass_count; count++) {
101 if (rand(100) < write_percentage) {
111 EXPECT_GE(sum, pass_count * write_percentage / 100);
114 template <typename Lock>
115 static void run_small_lock(Lock* l, size_t pass_count) {
116 for (size_t count = 0; count < pass_count; count++) {
123 template <typename... Args>
124 static void FollySyncThreading(Args... args) {
125 std::unique_ptr<std::thread[]> threads(new std::thread[s_nThreadCount]);
126 for (size_t i = 0; i < s_nThreadCount; i++) {
127 threads[i] = std::thread(args...);
129 for (size_t i = 0; i < s_nThreadCount; i++) {
134 template <typename WriterFunc>
135 static void FollyRcuThreading(WriterFunc writer_func) {
136 // One of the threads is a writer.
137 size_t reader_thrd_cnt = s_nThreadCount - 1;
138 rcu_readers_done.store(false, std::memory_order_release);
139 std::unique_ptr<std::thread[]> reader_threads(
140 new std::thread[reader_thrd_cnt]);
141 std::thread writer_thread(writer_func);
142 for (size_t i = 0; i < reader_thrd_cnt; i++) {
143 reader_threads[i] = std::thread(run_rcu_reader, s_nRcuReaderPassCount);
145 for (size_t i = 0; i < reader_thrd_cnt; i++) {
146 reader_threads[i].join();
148 rcu_readers_done.store(true, std::memory_order_release);
149 writer_thread.join();
152 template <typename SmallLockType>
153 static void FollySmallLockThreading(size_t pass_count) {
154 std::unique_ptr<SmallLockType> l(new SmallLockType());
157 FollySyncThreading(run_small_lock<SmallLockType>, l.get(), pass_count);
158 EXPECT_EQ(locked_data, pass_count * s_nThreadCount);
161 template <typename RWLockType>
162 static void FollyRWLockThreading(size_t pass_count, unsigned write_percentage) {
163 std::unique_ptr<RWLockType> l(new RWLockType());
165 FollySyncThreading(run_rw_lock<RWLockType>, l.get(), pass_count,
170 size_t FollySyncTest_Parallel::locked_data;
171 std::atomic<RcuData*> FollySyncTest_Parallel::rcu_data;
172 std::atomic_bool FollySyncTest_Parallel::rcu_readers_done;
173 size_t FollySyncTest_Parallel::s_nThreadCount;
174 size_t FollySyncTest_Parallel::s_nMicroLockPassCount;
175 size_t FollySyncTest_Parallel::s_nMicroSpinLockPassCount;
176 size_t FollySyncTest_Parallel::s_nPicoSpinLockPassCount;
177 size_t FollySyncTest_Parallel::s_nSharedMutexPassCount;
178 size_t FollySyncTest_Parallel::s_nRWSpinLockPassCount;
179 size_t FollySyncTest_Parallel::s_nRWTicketSpinLockPassCount;
181 size_t FollySyncTest_Parallel::s_nRcuReaderPassCount;
182 size_t FollySyncTest_Parallel::s_nRcuWriterPassCount;
183 size_t FollySyncTest_Parallel::s_nRcuWriterFrequency;
185 unsigned FollySyncTest_Parallel::s_nSharedMutexWritePercentage;
186 unsigned FollySyncTest_Parallel::s_nRWSpinLockWritePercentage;
187 unsigned FollySyncTest_Parallel::s_nRWTicketSpinLockWritePercentage;
189 TEST_F(FollySyncTest_Parallel, FollyRCU_Sync) {
190 FollyRcuThreading(run_rcu_writer_sync);
193 TEST_F(FollySyncTest_Parallel, FollyRCU_NoSync) {
194 FollyRcuThreading(run_rcu_writer_no_sync);
197 TEST_F(FollySyncTest_Parallel, FollyRWTicketSpinLock_32) {
198 FollyRWLockThreading<RWTicketSpinLock32>(s_nRWTicketSpinLockPassCount,
199 s_nRWTicketSpinLockWritePercentage);
202 TEST_F(FollySyncTest_Parallel, FollyRWTicketSpinLock_64) {
203 FollyRWLockThreading<RWTicketSpinLock64>(s_nRWTicketSpinLockPassCount,
204 s_nRWTicketSpinLockWritePercentage);
207 TEST_F(FollySyncTest_Parallel, FollyRWSpinLock) {
208 FollyRWLockThreading<RWSpinLock>(s_nRWSpinLockPassCount,
209 s_nRWSpinLockWritePercentage);
212 TEST_F(FollySyncTest_Parallel, FollySharedMutex_ReadPriority) {
213 FollyRWLockThreading<SharedMutexReadPriority>(s_nSharedMutexPassCount,
214 s_nSharedMutexWritePercentage);
217 TEST_F(FollySyncTest_Parallel, FollySharedMutex_WritePriority) {
218 FollyRWLockThreading<SharedMutexWritePriority>(s_nSharedMutexPassCount,
219 s_nSharedMutexWritePercentage);
222 TEST_F(FollySyncTest_Parallel, FollyMicroSpinLock) {
223 FollySmallLockThreading<MicroSpinLock>(s_nMicroSpinLockPassCount);
226 TEST_F(FollySyncTest_Parallel, FollyPicoSpinLock) {
227 FollySmallLockThreading<PicoSpinLock>(s_nPicoSpinLockPassCount);
230 TEST_F(FollySyncTest_Parallel, FollyMicroLock) {
231 FollySmallLockThreading<MicroLock>(s_nMicroLockPassCount);
234 } // namespace folly_test