1 //
2 //
3 // Copyright 2017 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 // Test various closure related operations
20
21 #include <benchmark/benchmark.h>
22 #include <grpc/grpc.h>
23
24 #include <sstream>
25
26 #include "src/core/lib/iomgr/closure.h"
27 #include "src/core/lib/iomgr/combiner.h"
28 #include "src/core/lib/iomgr/exec_ctx.h"
29 #include "src/core/util/spinlock.h"
30 #include "test/core/test_util/test_config.h"
31 #include "test/cpp/microbenchmarks/helpers.h"
32 #include "test/cpp/util/test_config.h"
33
BM_NoOpExecCtx(benchmark::State & state)34 static void BM_NoOpExecCtx(benchmark::State& state) {
35 for (auto _ : state) {
36 grpc_core::ExecCtx exec_ctx;
37 }
38 }
39 BENCHMARK(BM_NoOpExecCtx);
40
BM_WellFlushed(benchmark::State & state)41 static void BM_WellFlushed(benchmark::State& state) {
42 grpc_core::ExecCtx exec_ctx;
43 for (auto _ : state) {
44 grpc_core::ExecCtx::Get()->Flush();
45 }
46 }
47 BENCHMARK(BM_WellFlushed);
48
DoNothing(void *,grpc_error_handle)49 static void DoNothing(void* /*arg*/, grpc_error_handle /*error*/) {}
50
BM_ClosureInitAgainstExecCtx(benchmark::State & state)51 static void BM_ClosureInitAgainstExecCtx(benchmark::State& state) {
52 grpc_closure c;
53 for (auto _ : state) {
54 benchmark::DoNotOptimize(
55 GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx));
56 }
57 }
58 BENCHMARK(BM_ClosureInitAgainstExecCtx);
59
BM_ClosureInitAgainstCombiner(benchmark::State & state)60 static void BM_ClosureInitAgainstCombiner(benchmark::State& state) {
61 grpc_core::Combiner* combiner = grpc_combiner_create(
62 grpc_event_engine::experimental::CreateEventEngine());
63 grpc_closure c;
64 grpc_core::ExecCtx exec_ctx;
65 for (auto _ : state) {
66 benchmark::DoNotOptimize(
67 GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, nullptr));
68 }
69 GRPC_COMBINER_UNREF(combiner, "finished");
70 }
71 BENCHMARK(BM_ClosureInitAgainstCombiner);
72
BM_ClosureRun(benchmark::State & state)73 static void BM_ClosureRun(benchmark::State& state) {
74 grpc_closure c;
75 GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
76 grpc_core::ExecCtx exec_ctx;
77 for (auto _ : state) {
78 grpc_core::Closure::Run(DEBUG_LOCATION, &c, absl::OkStatus());
79 }
80 }
81 BENCHMARK(BM_ClosureRun);
82
BM_ClosureCreateAndRun(benchmark::State & state)83 static void BM_ClosureCreateAndRun(benchmark::State& state) {
84 grpc_core::ExecCtx exec_ctx;
85 for (auto _ : state) {
86 grpc_core::Closure::Run(
87 DEBUG_LOCATION,
88 GRPC_CLOSURE_CREATE(DoNothing, nullptr, grpc_schedule_on_exec_ctx),
89 absl::OkStatus());
90 }
91 }
92 BENCHMARK(BM_ClosureCreateAndRun);
93
BM_ClosureInitAndRun(benchmark::State & state)94 static void BM_ClosureInitAndRun(benchmark::State& state) {
95 grpc_core::ExecCtx exec_ctx;
96 grpc_closure c;
97 for (auto _ : state) {
98 grpc_core::Closure::Run(
99 DEBUG_LOCATION,
100 GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx),
101 absl::OkStatus());
102 }
103 }
104 BENCHMARK(BM_ClosureInitAndRun);
105
BM_ClosureSchedOnExecCtx(benchmark::State & state)106 static void BM_ClosureSchedOnExecCtx(benchmark::State& state) {
107 grpc_closure c;
108 GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
109 grpc_core::ExecCtx exec_ctx;
110 for (auto _ : state) {
111 grpc_core::ExecCtx::Run(DEBUG_LOCATION, &c, absl::OkStatus());
112 grpc_core::ExecCtx::Get()->Flush();
113 }
114 }
115 BENCHMARK(BM_ClosureSchedOnExecCtx);
116
BM_ClosureSched2OnExecCtx(benchmark::State & state)117 static void BM_ClosureSched2OnExecCtx(benchmark::State& state) {
118 grpc_closure c1;
119 grpc_closure c2;
120 GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
121 GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
122 grpc_core::ExecCtx exec_ctx;
123 for (auto _ : state) {
124 grpc_core::ExecCtx::Run(DEBUG_LOCATION, &c1, absl::OkStatus());
125 grpc_core::ExecCtx::Run(DEBUG_LOCATION, &c2, absl::OkStatus());
126 grpc_core::ExecCtx::Get()->Flush();
127 }
128 }
129 BENCHMARK(BM_ClosureSched2OnExecCtx);
130
BM_ClosureSched3OnExecCtx(benchmark::State & state)131 static void BM_ClosureSched3OnExecCtx(benchmark::State& state) {
132 grpc_closure c1;
133 grpc_closure c2;
134 grpc_closure c3;
135 GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
136 GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
137 GRPC_CLOSURE_INIT(&c3, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
138 grpc_core::ExecCtx exec_ctx;
139 for (auto _ : state) {
140 grpc_core::ExecCtx::Run(DEBUG_LOCATION, &c1, absl::OkStatus());
141 grpc_core::ExecCtx::Run(DEBUG_LOCATION, &c2, absl::OkStatus());
142 grpc_core::ExecCtx::Run(DEBUG_LOCATION, &c3, absl::OkStatus());
143 grpc_core::ExecCtx::Get()->Flush();
144 }
145 }
146 BENCHMARK(BM_ClosureSched3OnExecCtx);
147
BM_AcquireMutex(benchmark::State & state)148 static void BM_AcquireMutex(benchmark::State& state) {
149 // for comparison with the combiner stuff below
150 gpr_mu mu;
151 gpr_mu_init(&mu);
152 grpc_core::ExecCtx exec_ctx;
153 for (auto _ : state) {
154 gpr_mu_lock(&mu);
155 DoNothing(nullptr, absl::OkStatus());
156 gpr_mu_unlock(&mu);
157 }
158 gpr_mu_destroy(&mu);
159 }
160 BENCHMARK(BM_AcquireMutex);
161
BM_TryAcquireMutex(benchmark::State & state)162 static void BM_TryAcquireMutex(benchmark::State& state) {
163 // for comparison with the combiner stuff below
164 gpr_mu mu;
165 gpr_mu_init(&mu);
166 grpc_core::ExecCtx exec_ctx;
167 for (auto _ : state) {
168 if (gpr_mu_trylock(&mu)) {
169 DoNothing(nullptr, absl::OkStatus());
170 gpr_mu_unlock(&mu);
171 } else {
172 abort();
173 }
174 }
175 gpr_mu_destroy(&mu);
176 }
177 BENCHMARK(BM_TryAcquireMutex);
178
BM_AcquireSpinlock(benchmark::State & state)179 static void BM_AcquireSpinlock(benchmark::State& state) {
180 // for comparison with the combiner stuff below
181 gpr_spinlock mu = GPR_SPINLOCK_INITIALIZER;
182 grpc_core::ExecCtx exec_ctx;
183 for (auto _ : state) {
184 gpr_spinlock_lock(&mu);
185 DoNothing(nullptr, absl::OkStatus());
186 gpr_spinlock_unlock(&mu);
187 }
188 }
189 BENCHMARK(BM_AcquireSpinlock);
190
BM_TryAcquireSpinlock(benchmark::State & state)191 static void BM_TryAcquireSpinlock(benchmark::State& state) {
192 // for comparison with the combiner stuff below
193 gpr_spinlock mu = GPR_SPINLOCK_INITIALIZER;
194 grpc_core::ExecCtx exec_ctx;
195 for (auto _ : state) {
196 if (gpr_spinlock_trylock(&mu)) {
197 DoNothing(nullptr, absl::OkStatus());
198 gpr_spinlock_unlock(&mu);
199 } else {
200 abort();
201 }
202 }
203 }
204 BENCHMARK(BM_TryAcquireSpinlock);
205
BM_ClosureSchedOnCombiner(benchmark::State & state)206 static void BM_ClosureSchedOnCombiner(benchmark::State& state) {
207 grpc_core::Combiner* combiner = grpc_combiner_create(
208 grpc_event_engine::experimental::CreateEventEngine());
209 grpc_closure c;
210 GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, nullptr);
211 grpc_core::ExecCtx exec_ctx;
212 for (auto _ : state) {
213 combiner->Run(&c, absl::OkStatus());
214 grpc_core::ExecCtx::Get()->Flush();
215 }
216 GRPC_COMBINER_UNREF(combiner, "finished");
217 }
218 BENCHMARK(BM_ClosureSchedOnCombiner);
219
BM_ClosureSched2OnCombiner(benchmark::State & state)220 static void BM_ClosureSched2OnCombiner(benchmark::State& state) {
221 grpc_core::Combiner* combiner = grpc_combiner_create(
222 grpc_event_engine::experimental::CreateEventEngine());
223 grpc_closure c1;
224 grpc_closure c2;
225 GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, nullptr);
226 GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, nullptr);
227 grpc_core::ExecCtx exec_ctx;
228 for (auto _ : state) {
229 combiner->Run(&c1, absl::OkStatus());
230 combiner->Run(&c2, absl::OkStatus());
231 grpc_core::ExecCtx::Get()->Flush();
232 }
233 GRPC_COMBINER_UNREF(combiner, "finished");
234 }
235 BENCHMARK(BM_ClosureSched2OnCombiner);
236
BM_ClosureSched3OnCombiner(benchmark::State & state)237 static void BM_ClosureSched3OnCombiner(benchmark::State& state) {
238 grpc_core::Combiner* combiner = grpc_combiner_create(
239 grpc_event_engine::experimental::CreateEventEngine());
240 grpc_closure c1;
241 grpc_closure c2;
242 grpc_closure c3;
243 GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, nullptr);
244 GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, nullptr);
245 GRPC_CLOSURE_INIT(&c3, DoNothing, nullptr, nullptr);
246 grpc_core::ExecCtx exec_ctx;
247 for (auto _ : state) {
248 combiner->Run(&c1, absl::OkStatus());
249 combiner->Run(&c2, absl::OkStatus());
250 combiner->Run(&c3, absl::OkStatus());
251 grpc_core::ExecCtx::Get()->Flush();
252 }
253 GRPC_COMBINER_UNREF(combiner, "finished");
254 }
255 BENCHMARK(BM_ClosureSched3OnCombiner);
256
BM_ClosureSched2OnTwoCombiners(benchmark::State & state)257 static void BM_ClosureSched2OnTwoCombiners(benchmark::State& state) {
258 std::shared_ptr<grpc_event_engine::experimental::EventEngine> engine =
259 grpc_event_engine::experimental::CreateEventEngine();
260 grpc_core::Combiner* combiner1 = grpc_combiner_create(engine);
261 grpc_core::Combiner* combiner2 = grpc_combiner_create(engine);
262 grpc_closure c1;
263 grpc_closure c2;
264 GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, nullptr);
265 GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, nullptr);
266 grpc_core::ExecCtx exec_ctx;
267 for (auto _ : state) {
268 combiner1->Run(&c1, absl::OkStatus());
269 combiner2->Run(&c2, absl::OkStatus());
270 grpc_core::ExecCtx::Get()->Flush();
271 }
272 GRPC_COMBINER_UNREF(combiner1, "finished");
273 GRPC_COMBINER_UNREF(combiner2, "finished");
274 }
275 BENCHMARK(BM_ClosureSched2OnTwoCombiners);
276
BM_ClosureSched4OnTwoCombiners(benchmark::State & state)277 static void BM_ClosureSched4OnTwoCombiners(benchmark::State& state) {
278 std::shared_ptr<grpc_event_engine::experimental::EventEngine> engine =
279 grpc_event_engine::experimental::CreateEventEngine();
280 grpc_core::Combiner* combiner1 = grpc_combiner_create(engine);
281 grpc_core::Combiner* combiner2 = grpc_combiner_create(engine);
282 grpc_closure c1;
283 grpc_closure c2;
284 grpc_closure c3;
285 grpc_closure c4;
286 GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, nullptr);
287 GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, nullptr);
288 GRPC_CLOSURE_INIT(&c3, DoNothing, nullptr, nullptr);
289 GRPC_CLOSURE_INIT(&c4, DoNothing, nullptr, nullptr);
290 grpc_core::ExecCtx exec_ctx;
291 for (auto _ : state) {
292 combiner1->Run(&c1, absl::OkStatus());
293 combiner2->Run(&c2, absl::OkStatus());
294 combiner1->Run(&c3, absl::OkStatus());
295 combiner2->Run(&c4, absl::OkStatus());
296 grpc_core::ExecCtx::Get()->Flush();
297 }
298 GRPC_COMBINER_UNREF(combiner1, "finished");
299 GRPC_COMBINER_UNREF(combiner2, "finished");
300 }
301 BENCHMARK(BM_ClosureSched4OnTwoCombiners);
302
303 // Helper that continuously reschedules the same closure against something until
304 // the benchmark is complete
305 class Rescheduler {
306 public:
Rescheduler(benchmark::State & state)307 explicit Rescheduler(benchmark::State& state) : state_(state) {
308 GRPC_CLOSURE_INIT(&closure_, Step, this, nullptr);
309 }
310
ScheduleFirst()311 void ScheduleFirst() {
312 grpc_core::ExecCtx::Run(DEBUG_LOCATION, &closure_, absl::OkStatus());
313 }
314
ScheduleFirstAgainstDifferentScheduler()315 void ScheduleFirstAgainstDifferentScheduler() {
316 grpc_core::ExecCtx::Run(DEBUG_LOCATION,
317 GRPC_CLOSURE_CREATE(Step, this, nullptr),
318 absl::OkStatus());
319 }
320
321 private:
322 benchmark::State& state_;
323 grpc_closure closure_;
324
Step(void * arg,grpc_error_handle)325 static void Step(void* arg, grpc_error_handle /*error*/) {
326 Rescheduler* self = static_cast<Rescheduler*>(arg);
327 if (self->state_.KeepRunning()) {
328 grpc_core::ExecCtx::Run(DEBUG_LOCATION, &self->closure_,
329 absl::OkStatus());
330 }
331 }
332 };
333
BM_ClosureReschedOnExecCtx(benchmark::State & state)334 static void BM_ClosureReschedOnExecCtx(benchmark::State& state) {
335 grpc_core::ExecCtx exec_ctx;
336 Rescheduler r(state);
337 r.ScheduleFirst();
338 grpc_core::ExecCtx::Get()->Flush();
339 }
340 BENCHMARK(BM_ClosureReschedOnExecCtx);
341
342 // Some distros have RunSpecifiedBenchmarks under the benchmark namespace,
343 // and others do not. This allows us to support both modes.
344 namespace benchmark {
RunTheBenchmarksNamespaced()345 void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
346 } // namespace benchmark
347
main(int argc,char ** argv)348 int main(int argc, char** argv) {
349 grpc::testing::TestEnvironment env(&argc, argv);
350 LibraryInitializer libInit;
351 ::benchmark::Initialize(&argc, argv);
352 grpc::testing::InitTest(&argc, &argv, false);
353 benchmark::RunTheBenchmarksNamespaced();
354 return 0;
355 }
356