proxygen
AtomicSharedPtrPerformance.cpp
Go to the documentation of this file.
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 
17 // AtomicSharedPtr-detail.h only works with libstdc++, so skip these tests for
18 // other vendors
19 #ifdef FOLLY_USE_LIBSTDCPP
20 
22 
23 #include <sys/time.h>
24 #include <atomic>
25 #include <chrono>
26 #include <condition_variable>
27 #include <iostream>
28 #include <mutex>
29 #include <thread>
30 #include <vector>
31 
32 using std::atomic;
33 using std::cerr;
34 using std::condition_variable;
35 using std::cout;
36 using std::endl;
37 using std::is_same;
38 using std::make_shared;
39 using std::memory_order;
40 using std::memory_order_acq_rel;
41 using std::memory_order_acquire;
42 using std::memory_order_relaxed;
43 using std::memory_order_release;
44 using std::memory_order_seq_cst;
45 using std::move;
46 using std::mutex;
47 using std::ref;
48 using std::shared_ptr;
49 using std::thread;
50 using std::unique_lock;
51 using std::vector;
52 using std::chrono::duration_cast;
53 using std::chrono::microseconds;
54 using std::chrono::steady_clock;
55 
56 static uint64_t nowMicro() {
57  return duration_cast<microseconds>(steady_clock::now().time_since_epoch())
58  .count();
59 }
60 
61 static const char* memoryOrder(memory_order order) {
62  switch (order) {
63  case memory_order_relaxed:
64  return "relaxed";
65  case memory_order_acquire:
66  return "acquire";
67  case memory_order_release:
68  return "release";
69  case memory_order_acq_rel:
70  return "acq_rel";
71  case memory_order_seq_cst:
72  return "seq_cst";
73  default:
74  return "";
75  }
76 }
77 
78 template <typename T>
79 void uncontended_read_write(
80  size_t readers,
81  size_t writers,
82  memory_order readOrder = memory_order_seq_cst,
83  memory_order writeOrder = memory_order_seq_cst) {
84  std::shared_ptr<int> zero = std::make_shared<int>(0);
85  T a(zero);
86  auto time1 = nowMicro();
87  for (size_t i = 0; i < 10000000; ++i) {
88  for (size_t j = 0; j < readers; ++j) {
89  a.load(readOrder);
90  }
91  for (size_t j = 0; j < writers; ++j) {
92  a.store(zero, writeOrder);
93  }
94  }
95  auto time2 = nowMicro();
96  cout << "Uncontended Read(" << readers << "," << memoryOrder(readOrder)
97  << ")/Write(" << writers << "," << memoryOrder(writeOrder)
98  << "): " << (time2 - time1) << " \u03BCs" << endl;
99 }
100 
101 template <typename T>
102 void read_asp(
103  unique_lock<mutex> lock,
104  condition_variable& cvar,
105  atomic<bool>& go,
106  T& aptr,
107  memory_order order) {
108  cvar.wait(lock, [&go]() {
109  return atomic_load_explicit(&go, memory_order_acquire);
110  });
111  lock.unlock();
112  for (size_t i = 0; i < 1000000; ++i) {
113  aptr.load(order);
114  }
115 }
116 
117 template <typename T>
118 void write_asp(
119  unique_lock<mutex> lock,
120  condition_variable& cvar,
121  atomic<bool>& go,
122  T& aptr,
123  memory_order order) {
124  std::shared_ptr<int> zero = std::make_shared<int>(0);
125  cvar.wait(lock, [&go]() {
126  return atomic_load_explicit(&go, memory_order_acquire);
127  });
128  lock.unlock();
129  for (size_t i = 0; i < 1000000; ++i) {
130  aptr.store(zero, order);
131  }
132 }
133 
134 template <typename T>
135 void contended_read_write(
136  size_t readers,
137  size_t writers,
138  memory_order readOrder = memory_order_seq_cst,
139  memory_order writeOrder = memory_order_seq_cst) {
141  mutex lock;
142  condition_variable cvar;
143  atomic<bool> go{false};
144  T aptr(std::make_shared<int>());
145  for (size_t i = 0; i < readers; ++i) {
146  unique_lock<mutex> ulock(lock);
147  threads.emplace_back(
148  &read_asp<T>, move(ulock), ref(cvar), ref(go), ref(aptr), readOrder);
149  }
150  for (size_t i = 0; i < writers; ++i) {
151  unique_lock<mutex> ulock(lock);
152  threads.emplace_back(
153  &write_asp<T>, move(ulock), ref(cvar), ref(go), ref(aptr), writeOrder);
154  }
155  unique_lock<mutex> ulock(lock);
156  ulock.unlock();
157  atomic_store_explicit(&go, true, memory_order_release);
158  auto time1 = nowMicro();
159  cvar.notify_all();
160  for (auto& thread : threads) {
161  thread.join();
162  }
163  auto time2 = nowMicro();
164  cout << "Contended Read(" << readers << "," << memoryOrder(readOrder)
165  << ")/Write(" << writers << "," << memoryOrder(writeOrder)
166  << "): " << (time2 - time1) << " \u03BCs" << endl;
167 }
168 
169 template <typename T>
170 void document_noexcept() {
171  shared_ptr<int> ptr = make_shared<int>(0);
172  T aptr{};
173  cout << " ctor () is " << (noexcept(T()) ? "" : "not ") << "noexcept."
174  << endl;
175  cout << " ctor (ptr) is " << (noexcept(T(ptr)) ? "" : "not ") << "noexcept."
176  << endl;
177 #define _(A) \
178  do { \
179  cout << " " #A " is " << (noexcept(aptr.A) ? "" : "not ") << "noexcept." \
180  << endl; \
181  } while (0)
182  _(operator=(ptr));
183 
184  _(is_lock_free());
185 
186  _(store(ptr));
187  _(store(ptr, memory_order_seq_cst));
188 
189  _(load());
190  _(load(memory_order_seq_cst));
191 
192  _(exchange(ptr));
193  _(exchange(ptr, memory_order_seq_cst));
194 
195  _(compare_exchange_strong(ptr, ptr));
196  _(compare_exchange_strong(ptr, ptr, memory_order_seq_cst));
197  _(compare_exchange_strong(
198  ptr, ptr, memory_order_seq_cst, memory_order_seq_cst));
199 
200  _(compare_exchange_weak(ptr, ptr));
201  _(compare_exchange_weak(ptr, ptr, memory_order_seq_cst));
202  _(compare_exchange_weak(
203  ptr, ptr, memory_order_seq_cst, memory_order_seq_cst));
204 
205 #undef _
206  cout << " operator std::shared_ptr<T>() is "
207  << (noexcept(ptr = aptr) ? "" : "not ") << "noexcept." << endl;
208 }
209 
210 template <typename T>
211 void runSuite() {
212  document_noexcept<T>();
213  uncontended_read_write<T>(10, 0);
214  uncontended_read_write<T>(0, 10);
215  uncontended_read_write<T>(10, 10);
216  uncontended_read_write<T>(10, 10, memory_order_relaxed, memory_order_relaxed);
217  uncontended_read_write<T>(10, 10, memory_order_acquire, memory_order_release);
218  contended_read_write<T>(10, 0);
219  contended_read_write<T>(0, 10);
220  contended_read_write<T>(1, 1);
221  contended_read_write<T>(5, 1);
222  contended_read_write<T>(10, 1);
223  contended_read_write<T>(100, 1);
224  contended_read_write<T>(100, 1, memory_order_relaxed, memory_order_relaxed);
225  contended_read_write<T>(100, 1, memory_order_acquire, memory_order_release);
226 }
227 
228 int main(int, char**) {
229  cout << endl << "Folly implementation. Is lock free: 1" << endl;
230  runSuite<folly::atomic_shared_ptr<int>>();
231  return 0;
232 }
233 
234 #else // #ifdef FOLLY_USE_LIBSTDCPP
235 
236 int main(int, char**) {
237  return 1;
238 }
239 
240 #endif // #ifdef FOLLY_USE_LIBSTDCPP
void * ptr
#define T(v)
Definition: http_parser.c:233
std::chrono::steady_clock::time_point now()
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
int main(int, char **)
requires E e noexcept(noexcept(s.error(std::move(e))))
def load()
Definition: deadlock.py:441
std::vector< std::thread::id > threads
auto lock(SynchronizedLocker...lockersIn) -> std::tuple< typename SynchronizedLocker::LockedPtr... >
Definition: Synchronized.h:871
auto ulock(Synchronized &synchronized, Args &&...args)
Definition: Synchronized.h:945
char a
Definition: Traits.h:588
uint64_t nowMicro()
int * count
T exchange(T &obj, U &&new_value)
Definition: Utility.h:120
std::mutex mutex
const internal::AnythingMatcher _
int order