Fix copyright lines
[folly.git] / folly / concurrency / test / AtomicSharedPtrPerformance.cpp
1 /*
2  * Copyright 2017-present Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 #include <folly/concurrency/AtomicSharedPtr.h>
17
18 #include <sys/time.h>
19 #include <atomic>
20 #include <chrono>
21 #include <condition_variable>
22 #include <iostream>
23 #include <mutex>
24 #include <thread>
25 #include <vector>
26
27 using std::shared_ptr;
28 using std::make_shared;
29 using std::cerr;
30 using std::cout;
31 using std::endl;
32 using std::condition_variable;
33 using std::unique_lock;
34 using std::mutex;
35 using std::vector;
36 using std::thread;
37 using std::memory_order;
38 using std::memory_order_relaxed;
39 using std::memory_order_acquire;
40 using std::memory_order_release;
41 using std::memory_order_acq_rel;
42 using std::memory_order_seq_cst;
43 using std::move;
44 using std::ref;
45 using std::is_same;
46 using std::atomic;
47 using std::chrono::steady_clock;
48 using std::chrono::duration_cast;
49 using std::chrono::microseconds;
50
51 static uint64_t nowMicro() {
52   return duration_cast<microseconds>(steady_clock::now().time_since_epoch())
53       .count();
54 }
55
56 static const char* memoryOrder(memory_order order) {
57   switch (order) {
58     case memory_order_relaxed:
59       return "relaxed";
60     case memory_order_acquire:
61       return "acquire";
62     case memory_order_release:
63       return "release";
64     case memory_order_acq_rel:
65       return "acq_rel";
66     case memory_order_seq_cst:
67       return "seq_cst";
68     default:
69       return "";
70   }
71 }
72
73 template <typename T>
74 void uncontended_read_write(
75     size_t readers,
76     size_t writers,
77     memory_order readOrder = memory_order_seq_cst,
78     memory_order writeOrder = memory_order_seq_cst) {
79   std::shared_ptr<int> zero = std::make_shared<int>(0);
80   T a(zero);
81   auto time1 = nowMicro();
82   for (size_t i = 0; i < 10000000; ++i) {
83     for (size_t j = 0; j < readers; ++j) {
84       a.load(readOrder);
85     }
86     for (size_t j = 0; j < writers; ++j) {
87       a.store(zero, writeOrder);
88     }
89   }
90   auto time2 = nowMicro();
91   cout << "Uncontended Read(" << readers << "," << memoryOrder(readOrder)
92        << ")/Write(" << writers << "," << memoryOrder(writeOrder)
93        << "): " << (time2 - time1) << " \u03BCs" << endl;
94 }
95
96 template <typename T>
97 void read_asp(
98     unique_lock<mutex> lock,
99     condition_variable& cvar,
100     atomic<bool>& go,
101     T& aptr,
102     memory_order order) {
103   cvar.wait(lock, [&go]() {
104     return atomic_load_explicit(&go, memory_order_acquire);
105   });
106   lock.unlock();
107   for (size_t i = 0; i < 1000000; ++i) {
108     aptr.load(order);
109   }
110 }
111
112 template <typename T>
113 void write_asp(
114     unique_lock<mutex> lock,
115     condition_variable& cvar,
116     atomic<bool>& go,
117     T& aptr,
118     memory_order order) {
119   std::shared_ptr<int> zero = std::make_shared<int>(0);
120   cvar.wait(lock, [&go]() {
121     return atomic_load_explicit(&go, memory_order_acquire);
122   });
123   lock.unlock();
124   for (size_t i = 0; i < 1000000; ++i) {
125     aptr.store(zero, order);
126   }
127 }
128
129 template <typename T>
130 void contended_read_write(
131     size_t readers,
132     size_t writers,
133     memory_order readOrder = memory_order_seq_cst,
134     memory_order writeOrder = memory_order_seq_cst) {
135   vector<thread> threads;
136   mutex lock;
137   condition_variable cvar;
138   atomic<bool> go{false};
139   T aptr(std::make_shared<int>());
140   for (size_t i = 0; i < readers; ++i) {
141     unique_lock<mutex> ulock(lock);
142     threads.emplace_back(
143         &read_asp<T>, move(ulock), ref(cvar), ref(go), ref(aptr), readOrder);
144   }
145   for (size_t i = 0; i < writers; ++i) {
146     unique_lock<mutex> ulock(lock);
147     threads.emplace_back(
148         &write_asp<T>, move(ulock), ref(cvar), ref(go), ref(aptr), writeOrder);
149   }
150   unique_lock<mutex> ulock(lock);
151   ulock.unlock();
152   atomic_store_explicit(&go, true, memory_order_release);
153   auto time1 = nowMicro();
154   cvar.notify_all();
155   for (auto& thread : threads) {
156     thread.join();
157   }
158   auto time2 = nowMicro();
159   cout << "Contended Read(" << readers << "," << memoryOrder(readOrder)
160        << ")/Write(" << writers << "," << memoryOrder(writeOrder)
161        << "): " << (time2 - time1) << " \u03BCs" << endl;
162 }
163
164 template <typename T>
165 void document_noexcept() {
166   shared_ptr<int> ptr = make_shared<int>(0);
167   T aptr{};
168   cout << "  ctor () is " << (noexcept(T()) ? "" : "not ") << "noexcept."
169        << endl;
170   cout << "  ctor (ptr) is " << (noexcept(T(ptr)) ? "" : "not ") << "noexcept."
171        << endl;
172 #define _(A)                                                                  \
173   do {                                                                        \
174     cout << "  " #A " is " << (noexcept(aptr.A) ? "" : "not ") << "noexcept." \
175          << endl;                                                             \
176   } while (0)
177   _(operator=(ptr));
178
179   _(is_lock_free());
180
181   _(store(ptr));
182   _(store(ptr, memory_order_seq_cst));
183
184   _(load());
185   _(load(memory_order_seq_cst));
186
187   _(exchange(ptr));
188   _(exchange(ptr, memory_order_seq_cst));
189
190   _(compare_exchange_strong(ptr, ptr));
191   _(compare_exchange_strong(ptr, ptr, memory_order_seq_cst));
192   _(compare_exchange_strong(
193       ptr, ptr, memory_order_seq_cst, memory_order_seq_cst));
194
195   _(compare_exchange_weak(ptr, ptr));
196   _(compare_exchange_weak(ptr, ptr, memory_order_seq_cst));
197   _(compare_exchange_weak(
198       ptr, ptr, memory_order_seq_cst, memory_order_seq_cst));
199
200 #undef _
201   cout << "  operator std::shared_ptr<T>() is "
202        << (noexcept(ptr = aptr) ? "" : "not ") << "noexcept." << endl;
203 }
204
205 template <typename T>
206 void runSuite() {
207   document_noexcept<T>();
208   uncontended_read_write<T>(10, 0);
209   uncontended_read_write<T>(0, 10);
210   uncontended_read_write<T>(10, 10);
211   uncontended_read_write<T>(10, 10, memory_order_relaxed, memory_order_relaxed);
212   uncontended_read_write<T>(10, 10, memory_order_acquire, memory_order_release);
213   contended_read_write<T>(10, 0);
214   contended_read_write<T>(0, 10);
215   contended_read_write<T>(1, 1);
216   contended_read_write<T>(5, 1);
217   contended_read_write<T>(10, 1);
218   contended_read_write<T>(100, 1);
219   contended_read_write<T>(100, 1, memory_order_relaxed, memory_order_relaxed);
220   contended_read_write<T>(100, 1, memory_order_acquire, memory_order_release);
221 }
222
223 int main(int, char**) {
224   cout << endl << "Folly implementation.  Is lock free: 1" << endl;
225   runSuite<folly::atomic_shared_ptr<int>>();
226   return 0;
227 }