Refactors folly sync test cases
[folly.git] / folly / stress-test / stress-parallel-folly-sync.cpp
1 #include "sync_test.h"
2
3 namespace folly_test {
4
5 class FollySyncTest_Parallel: public cds_test::stress_fixture {
6 protected:
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;
15   // MicroLock
16   static size_t s_nMicroLockPassCount;
17   // MicroSpinLock
18   static size_t s_nMicroSpinLockPassCount;
19   // PicoSpinLock
20   static size_t s_nPicoSpinLockPassCount;
21   // SharedMutex
22   static size_t s_nSharedMutexPassCount;
23   // RWSpinLock
24   static size_t s_nRWSpinLockPassCount;
25   // RWTicketSpinLock
26   static size_t s_nRWTicketSpinLockPassCount;
27   // RCU
28   static size_t s_nRcuReaderPassCount;
29   static size_t s_nRcuWriterPassCount;
30   static size_t s_nRcuWriterFrequency;
31
32   static unsigned s_nSharedMutexWritePercentage;
33   static unsigned s_nRWSpinLockWritePercentage;
34   static unsigned s_nRWTicketSpinLockWritePercentage;
35
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);
49
50     GetConfigNonZeroExpected(SharedMutexWritePercentage, 5);
51     GetConfigNonZeroExpected(RWSpinLockWritePercentage, 5);
52     GetConfigNonZeroExpected(RWTicketSpinLockWritePercentage, 5);
53
54     rcu_data.store(new RcuData(), std::memory_order_relaxed);
55   }
56
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);
61       new_data->d1++;
62       new_data->d2++;
63       rcu_data.store(new_data, std::memory_order_relaxed);
64       folly::synchronize_rcu();
65       delete old_data;
66       std::this_thread::sleep_for(
67           std::chrono::milliseconds(s_nRcuWriterFrequency));
68     }
69   }
70
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);
75       new_data->d1++;
76       new_data->d2++;
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));
81     }
82   }
83
84   static void run_rcu_reader(size_t pass_count) {
85     size_t sum = 0;
86     for (size_t count = 0; count < pass_count; count++) {
87       folly::rcu_reader g;
88       auto *data = rcu_data.load(std::memory_order_relaxed);
89       sum += (data->d1 + data->d2);
90     }
91     std::cout << "Reader done" << std::endl;
92     // Just want to simulate the reading.
93     EXPECT_GT(sum, 0);
94   }
95
96   template <typename Lock>
97   static void run_rw_lock(Lock *l, size_t pass_count,
98                           unsigned write_percentage) {
99     size_t sum = 0;
100     for (size_t count = 0; count < pass_count; count++) {
101       if (rand(100) < write_percentage) {
102         l->lock();
103         locked_data++;
104         l->unlock();
105       } else {
106         l->lock_shared();
107         sum = locked_data;
108         l->unlock_shared();
109       }
110     }
111     EXPECT_GE(sum, pass_count * write_percentage / 100);
112   }
113
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++) {
117       l->lock();
118       locked_data++;
119       l->unlock();
120     }
121   }
122
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...);
128     }
129     for (size_t i = 0; i < s_nThreadCount; i++) {
130       threads[i].join();
131     }
132   }
133
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);
144     }
145     for (size_t i = 0; i < reader_thrd_cnt; i++) {
146       reader_threads[i].join();
147     }
148     rcu_readers_done.store(true, std::memory_order_release);
149     writer_thread.join();
150   }
151
152   template <typename SmallLockType>
153   static void FollySmallLockThreading(size_t pass_count) {
154     std::unique_ptr<SmallLockType> l(new SmallLockType());
155     l->init();
156     locked_data = 0;
157     FollySyncThreading(run_small_lock<SmallLockType>, l.get(), pass_count);
158     EXPECT_EQ(locked_data, pass_count * s_nThreadCount);
159   }
160
161   template <typename RWLockType>
162   static void FollyRWLockThreading(size_t pass_count, unsigned write_percentage) {
163     std::unique_ptr<RWLockType> l(new RWLockType());
164     locked_data = 0;
165     FollySyncThreading(run_rw_lock<RWLockType>, l.get(), pass_count,
166                        write_percentage);
167   }
168 };
169
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;
180
181 size_t FollySyncTest_Parallel::s_nRcuReaderPassCount;
182 size_t FollySyncTest_Parallel::s_nRcuWriterPassCount;
183 size_t FollySyncTest_Parallel::s_nRcuWriterFrequency;
184
185 unsigned FollySyncTest_Parallel::s_nSharedMutexWritePercentage;
186 unsigned FollySyncTest_Parallel::s_nRWSpinLockWritePercentage;
187 unsigned FollySyncTest_Parallel::s_nRWTicketSpinLockWritePercentage;
188
189 TEST_F(FollySyncTest_Parallel, FollyRCU_Sync) {
190   FollyRcuThreading(run_rcu_writer_sync);
191 }
192
193 TEST_F(FollySyncTest_Parallel, FollyRCU_NoSync) {
194   FollyRcuThreading(run_rcu_writer_no_sync);
195 }
196
197 TEST_F(FollySyncTest_Parallel, FollyRWTicketSpinLock_32) {
198   FollyRWLockThreading<RWTicketSpinLock32>(s_nRWTicketSpinLockPassCount,
199                                            s_nRWTicketSpinLockWritePercentage);
200 }
201
202 TEST_F(FollySyncTest_Parallel, FollyRWTicketSpinLock_64) {
203   FollyRWLockThreading<RWTicketSpinLock64>(s_nRWTicketSpinLockPassCount,
204                                            s_nRWTicketSpinLockWritePercentage);
205 }
206
207 TEST_F(FollySyncTest_Parallel, FollyRWSpinLock) {
208   FollyRWLockThreading<RWSpinLock>(s_nRWSpinLockPassCount,
209                                    s_nRWSpinLockWritePercentage);
210 }
211
212 TEST_F(FollySyncTest_Parallel, FollySharedMutex_ReadPriority) {
213   FollyRWLockThreading<SharedMutexReadPriority>(s_nSharedMutexPassCount,
214                                                 s_nSharedMutexWritePercentage);
215 }
216
217 TEST_F(FollySyncTest_Parallel, FollySharedMutex_WritePriority) {
218   FollyRWLockThreading<SharedMutexWritePriority>(s_nSharedMutexPassCount,
219                                                  s_nSharedMutexWritePercentage);
220 }
221
222 TEST_F(FollySyncTest_Parallel, FollyMicroSpinLock) {
223   FollySmallLockThreading<MicroSpinLock>(s_nMicroSpinLockPassCount);
224 }
225
226 TEST_F(FollySyncTest_Parallel, FollyPicoSpinLock) {
227   FollySmallLockThreading<PicoSpinLock>(s_nPicoSpinLockPassCount);
228 }
229
230 TEST_F(FollySyncTest_Parallel, FollyMicroLock) {
231   FollySmallLockThreading<MicroLock>(s_nMicroLockPassCount);
232 }
233
234 } // namespace folly_test