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/LifoSem.h>
19 #include <semaphore.h>
22 #include <gtest/gtest.h>
24 #include <folly/Benchmark.h>
25 #include <folly/Random.h>
26 #include <folly/portability/Asm.h>
27 #include <folly/portability/GFlags.h>
28 #include <folly/test/DeterministicSchedule.h>
30 using namespace folly;
31 using namespace folly::test;
33 typedef LifoSemImpl<DeterministicAtomic> DLifoSem;
34 typedef DeterministicSchedule DSched;
36 LIFOSEM_DECLARE_POOL(DeterministicAtomic, 100000)
38 TEST(LifoSem, basic) {
40 EXPECT_FALSE(sem.tryWait());
42 EXPECT_TRUE(sem.tryWait());
47 TEST(LifoSem, multi) {
50 const int opsPerThread = 10000;
51 std::thread threads[10];
52 std::atomic<int> blocks(0);
54 for (auto& thr : threads) {
55 thr = std::thread([&]{
57 for (int i = 0; i < opsPerThread; ++i) {
71 for (auto& thr : threads) {
75 LOG(INFO) << opsPerThread * sizeof(threads)/sizeof(threads[0])
76 << " post/wait pairs, " << blocks << " blocked";
79 TEST(LifoSem, pingpong) {
80 DSched sched(DSched::uniform(0));
82 const int iters = 100;
84 for (int pass = 0; pass < 10; ++pass) {
88 auto thr = DSched::thread([&]{
89 for (int i = 0; i < iters; ++i) {
91 // main thread can't be running here
92 EXPECT_EQ(a.valueGuess(), 0);
93 EXPECT_EQ(b.valueGuess(), 0);
97 for (int i = 0; i < iters; ++i) {
100 // child thread can't be running here
101 EXPECT_EQ(a.valueGuess(), 0);
102 EXPECT_EQ(b.valueGuess(), 0);
108 TEST(LifoSem, mutex) {
109 DSched sched(DSched::uniform(0));
111 const int iters = 100;
113 for (int pass = 0; pass < 10; ++pass) {
116 auto thr = DSched::thread([&]{
117 for (int i = 0; i < iters; ++i) {
122 for (int i = 0; i < iters; ++i) {
132 TEST(LifoSem, no_blocking) {
133 long seed = folly::randomNumberSeed() % 10000;
134 LOG(INFO) << "seed=" << seed;
135 DSched sched(DSched::uniform(seed));
137 const int iters = 100;
138 const int numThreads = 2;
139 const int width = 10;
141 for (int pass = 0; pass < 10; ++pass) {
144 std::vector<std::thread> threads;
145 while (threads.size() < numThreads) {
146 threads.emplace_back(DSched::thread([&]{
147 for (int i = 0; i < iters; ++i) {
149 for (int w = 0; w < width; ++w) {
155 for (auto& thr : threads) {
161 TEST(LifoSem, one_way) {
162 long seed = folly::randomNumberSeed() % 10000;
163 LOG(INFO) << "seed=" << seed;
164 DSched sched(DSched::uniformSubset(seed, 1, 6));
166 const int iters = 1000;
168 for (int pass = 0; pass < 10; ++pass) {
171 auto thr = DSched::thread([&]{
172 for (int i = 0; i < iters; ++i) {
176 for (int i = 0; i < iters; ++i) {
183 TEST(LifoSem, shutdown_race) {
184 long seed = folly::randomNumberSeed() % 10000;
185 LOG(INFO) << "seed=" << seed;
187 bool shutdownWon = false;
188 bool shutdownLost = false;
189 for (int pass = 0; pass < 1000; ++pass) {
190 DSched sched(DSched::uniformSubset(seed + pass, 1, 1 + (pass % 50)));
194 auto thr = DSched::thread([&]{
200 } catch (ShutdownSemError& x) {
202 EXPECT_TRUE(a.isShutdown());
205 EXPECT_TRUE(!a.isShutdown());
207 EXPECT_TRUE(a.isShutdown());
210 EXPECT_EQ(1, waitCount + a.valueGuess());
211 if (waitCount == 0) {
217 EXPECT_TRUE(shutdownWon);
218 EXPECT_TRUE(shutdownLost);
221 TEST(LifoSem, shutdown_multi) {
222 DSched sched(DSched::uniform(0));
224 for (int pass = 0; pass < 10; ++pass) {
226 std::vector<std::thread> threads;
227 while (threads.size() < 20) {
228 threads.push_back(DSched::thread([&]{
232 } catch (ShutdownSemError& x) {
234 EXPECT_TRUE(a.isShutdown());
239 for (auto& thr : threads) {
245 TEST(LifoSem, multi_try_wait_simple) {
248 auto n = sem.tryWait(10); // this used to trigger an assert
252 TEST(LifoSem, multi_try_wait) {
253 long seed = folly::randomNumberSeed() % 10000;
254 LOG(INFO) << "seed=" << seed;
255 DSched sched(DSched::uniform(seed));
258 const int NPOSTS = 1000;
261 for (int i=0; i<NPOSTS; ++i) {
266 DeterministicAtomic<bool> consumer_stop(false);
272 stop = consumer_stop.load();
281 std::thread producer_thread(DSched::thread(producer));
282 std::thread consumer_thread(DSched::thread(consumer));
283 DSched::join(producer_thread);
284 consumer_stop.store(true);
285 DSched::join(consumer_thread);
287 ASSERT_EQ(NPOSTS, consumed);
290 BENCHMARK(lifo_sem_pingpong, iters) {
293 auto thr = std::thread([&]{
294 for (size_t i = 0; i < iters; ++i) {
299 for (size_t i = 0; i < iters; ++i) {
306 BENCHMARK(lifo_sem_oneway, iters) {
308 auto thr = std::thread([&]{
309 for (size_t i = 0; i < iters; ++i) {
313 for (size_t i = 0; i < iters; ++i) {
319 BENCHMARK(single_thread_lifo_post, iters) {
321 for (size_t n = 0; n < iters; ++n) {
323 asm_volatile_memory();
327 BENCHMARK(single_thread_lifo_wait, iters) {
329 for (size_t n = 0; n < iters; ++n) {
331 asm_volatile_memory();
335 BENCHMARK(single_thread_lifo_postwait, iters) {
337 for (size_t n = 0; n < iters; ++n) {
339 asm_volatile_memory();
341 asm_volatile_memory();
345 BENCHMARK(single_thread_lifo_trywait, iters) {
347 for (size_t n = 0; n < iters; ++n) {
348 EXPECT_FALSE(sem.tryWait());
349 asm_volatile_memory();
353 BENCHMARK(single_thread_posix_postwait, iters) {
355 EXPECT_EQ(sem_init(&sem, 0, 0), 0);
356 for (size_t n = 0; n < iters; ++n) {
357 EXPECT_EQ(sem_post(&sem), 0);
358 EXPECT_EQ(sem_wait(&sem), 0);
360 EXPECT_EQ(sem_destroy(&sem), 0);
363 BENCHMARK(single_thread_posix_trywait, iters) {
365 EXPECT_EQ(sem_init(&sem, 0, 0), 0);
366 for (size_t n = 0; n < iters; ++n) {
367 EXPECT_EQ(sem_trywait(&sem), -1);
369 EXPECT_EQ(sem_destroy(&sem), 0);
372 static void contendedUse(uint32_t n, int posters, int waiters) {
373 LifoSemImpl<std::atomic> sem;
375 std::vector<std::thread> threads;
376 std::atomic<bool> go(false);
379 for (int t = 0; t < waiters; ++t) {
380 threads.emplace_back([=,&sem] {
381 for (uint32_t i = t; i < n; i += waiters) {
386 for (int t = 0; t < posters; ++t) {
387 threads.emplace_back([=,&sem,&go] {
389 std::this_thread::yield();
391 for (uint32_t i = t; i < n; i += posters) {
399 for (auto& thr : threads) {
404 BENCHMARK_DRAW_LINE()
405 BENCHMARK_NAMED_PARAM(contendedUse, 1_to_1, 1, 1)
406 BENCHMARK_NAMED_PARAM(contendedUse, 1_to_4, 1, 4)
407 BENCHMARK_NAMED_PARAM(contendedUse, 1_to_32, 1, 32)
408 BENCHMARK_NAMED_PARAM(contendedUse, 4_to_1, 4, 1)
409 BENCHMARK_NAMED_PARAM(contendedUse, 4_to_24, 4, 24)
410 BENCHMARK_NAMED_PARAM(contendedUse, 8_to_100, 8, 100)
411 BENCHMARK_NAMED_PARAM(contendedUse, 32_to_1, 31, 1)
412 BENCHMARK_NAMED_PARAM(contendedUse, 16_to_16, 16, 16)
413 BENCHMARK_NAMED_PARAM(contendedUse, 32_to_32, 32, 32)
414 BENCHMARK_NAMED_PARAM(contendedUse, 32_to_1000, 32, 1000)
416 // sudo nice -n -20 _build/opt/folly/test/LifoSemTests
417 // --benchmark --bm_min_iters=10000000 --gtest_filter=-\*
418 // ============================================================================
419 // folly/test/LifoSemTests.cpp relative time/iter iters/s
420 // ============================================================================
421 // lifo_sem_pingpong 1.31us 762.40K
422 // lifo_sem_oneway 193.89ns 5.16M
423 // single_thread_lifo_post 15.37ns 65.08M
424 // single_thread_lifo_wait 13.60ns 73.53M
425 // single_thread_lifo_postwait 29.43ns 33.98M
426 // single_thread_lifo_trywait 677.69ps 1.48G
427 // single_thread_posix_postwait 25.03ns 39.95M
428 // single_thread_posix_trywait 7.30ns 136.98M
429 // ----------------------------------------------------------------------------
430 // contendedUse(1_to_1) 158.22ns 6.32M
431 // contendedUse(1_to_4) 574.73ns 1.74M
432 // contendedUse(1_to_32) 592.94ns 1.69M
433 // contendedUse(4_to_1) 118.28ns 8.45M
434 // contendedUse(4_to_24) 667.62ns 1.50M
435 // contendedUse(8_to_100) 701.46ns 1.43M
436 // contendedUse(32_to_1) 165.06ns 6.06M
437 // contendedUse(16_to_16) 238.57ns 4.19M
438 // contendedUse(32_to_32) 219.82ns 4.55M
439 // contendedUse(32_to_1000) 777.42ns 1.29M
440 // ============================================================================
442 int main(int argc, char ** argv) {
443 testing::InitGoogleTest(&argc, argv);
444 gflags::ParseCommandLineFlags(&argc, &argv, true);
445 int rv = RUN_ALL_TESTS();
446 folly::runBenchmarksOnFlag();