f9b4980d440f8f5a53dfce7fd1d42a40db2448c3
[folly.git] / folly / experimental / flat_combining / test / FlatCombiningTestHelpers.h
1 /*
2  * Copyright 2017 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 #pragma once
18
19 #include <folly/experimental/flat_combining/test/FlatCombiningExamples.h>
20
21 #include <folly/Benchmark.h>
22 #include <glog/logging.h>
23
24 #include <atomic>
25 #include <chrono>
26 #include <thread>
27
28 namespace folly {
29 namespace test {
30
31 void doWork(int work) {
32   uint64_t a = 0;
33   for (int i = work; i > 0; --i) {
34     a += i;
35   }
36   folly::doNotOptimizeAway(a);
37 }
38
39 template <
40     typename Example,
41     typename Req = bool,
42     typename Mutex = std::mutex,
43     template <typename> class Atom = std::atomic>
44 uint64_t fc_test(
45     int nthreads,
46     int lines,
47     int numRecs,
48     int work,
49     int ops,
50     bool combining,
51     bool dedicated,
52     bool tc,
53     bool syncops,
54     bool excl = false,
55     bool allocAll = false) {
56   using FC = FlatCombining<Example, Mutex, Atom, Req>;
57   using Rec = typename FC::Rec;
58
59   folly::BenchmarkSuspender susp;
60
61   std::atomic<bool> start{false};
62   std::atomic<int> started{0};
63   Example ex(lines, dedicated, numRecs);
64   std::atomic<uint64_t> total{0};
65   bool mutex = false;
66
67   if (allocAll) {
68     std::vector<Rec*> v(numRecs);
69     for (int i = 0; i < numRecs; ++i) {
70       v[i] = ex.allocRec();
71     }
72     for (int i = numRecs; i > 0; --i) {
73       ex.freeRec(v[i - 1]);
74     }
75   }
76
77   std::vector<std::thread> threads(nthreads);
78   for (int tid = 0; tid < nthreads; ++tid) {
79     threads[tid] = std::thread([&, tid] {
80       started.fetch_add(1);
81       Rec* myrec = (combining && tc) ? ex.allocRec() : nullptr;
82       uint64_t sum = 0;
83       while (!start.load())
84         ;
85
86       if (!combining) {
87         // no combining
88         for (int i = tid; i < ops; i += nthreads) {
89           sum += ex.fetchAddNoFC(1);
90           doWork(work); // unrelated work
91         }
92       } else if (syncops) {
93         // sync combining
94         for (int i = tid; i < ops; i += nthreads) {
95           sum += ex.fetchAdd(1, myrec);
96           doWork(work); // unrelated work
97         }
98       } else {
99         // async combining
100         for (int i = tid; i < ops; i += nthreads) {
101           ex.add(1, myrec);
102           doWork(work); // unrelated work
103         }
104       }
105
106       if (excl) {
107         // test of unstructured exclusive access
108         ex.acquireExclusive();
109         {
110           CHECK(!mutex);
111           mutex = true;
112           VLOG(2) << tid << " " << ex.getVal() << " ...........";
113           using namespace std::chrono_literals;
114           /* sleep override */ // for coverage
115           std::this_thread::sleep_for(10ms);
116           VLOG(2) << tid << " " << ex.getVal() << " ===========";
117           CHECK(mutex);
118           mutex = false;
119         }
120         ex.releaseExclusive();
121       }
122
123       total.fetch_add(sum);
124       if (combining && tc) {
125         ex.freeRec(myrec);
126       }
127     });
128   }
129
130   while (started.load() < nthreads)
131     ;
132   auto tbegin = std::chrono::steady_clock::now();
133
134   // begin time measurement
135   susp.dismiss();
136   start.store(true);
137
138   for (auto& t : threads) {
139     t.join();
140   }
141
142   if (!syncops) {
143     // complete any pending asynch ops
144     ex.drainAll();
145   }
146
147   // end time measurement
148   uint64_t duration = 0;
149   BENCHMARK_SUSPEND {
150     auto tend = std::chrono::steady_clock::now();
151     CHECK_EQ(ops, ex.getVal());
152     if (syncops) {
153       uint64_t n = (uint64_t)ops;
154       uint64_t expected = n * (n - 1) / 2;
155       CHECK_EQ(expected, total);
156     }
157     duration =
158         std::chrono::duration_cast<std::chrono::nanoseconds>(tend - tbegin)
159             .count();
160   }
161   return duration;
162 }
163
164 uint64_t run_test(
165     int nthreads,
166     int lines,
167     int numRecs,
168     int work,
169     int ops,
170     bool combining,
171     bool simple,
172     bool dedicated,
173     bool tc,
174     bool syncops,
175     bool excl = false,
176     bool allocAll = false) {
177   using M = std::mutex;
178   if (simple) {
179     using Example = FcSimpleExample<M>;
180     return fc_test<Example, bool, M>(
181         nthreads,
182         lines,
183         numRecs,
184         work,
185         ops,
186         combining,
187         dedicated,
188         tc,
189         syncops,
190         excl,
191         allocAll);
192   } else {
193     using Example = FcCustomExample<Req, M>;
194     return fc_test<Example, Req, M>(
195         nthreads,
196         lines,
197         numRecs,
198         work,
199         ops,
200         combining,
201         dedicated,
202         tc,
203         syncops,
204         excl,
205         allocAll);
206   }
207 }
208
209 } // namespace test {
210 } // namespace folly {