Imported Upstream version 1.21.0
[platform/upstream/grpc.git] / test / cpp / microbenchmarks / bm_closure.cc
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 #include <sstream>
24
25 #include "src/core/lib/gpr/spinlock.h"
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
30 #include "test/cpp/microbenchmarks/helpers.h"
31 #include "test/cpp/util/test_config.h"
32
33 static void BM_NoOpExecCtx(benchmark::State& state) {
34   TrackCounters track_counters;
35   while (state.KeepRunning()) {
36     grpc_core::ExecCtx exec_ctx;
37   }
38   track_counters.Finish(state);
39 }
40 BENCHMARK(BM_NoOpExecCtx);
41
42 static void BM_WellFlushed(benchmark::State& state) {
43   TrackCounters track_counters;
44   grpc_core::ExecCtx exec_ctx;
45   while (state.KeepRunning()) {
46     grpc_core::ExecCtx::Get()->Flush();
47   }
48
49   track_counters.Finish(state);
50 }
51 BENCHMARK(BM_WellFlushed);
52
53 static void DoNothing(void* arg, grpc_error* error) {}
54
55 static void BM_ClosureInitAgainstExecCtx(benchmark::State& state) {
56   TrackCounters track_counters;
57   grpc_closure c;
58   while (state.KeepRunning()) {
59     benchmark::DoNotOptimize(
60         GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx));
61   }
62   track_counters.Finish(state);
63 }
64 BENCHMARK(BM_ClosureInitAgainstExecCtx);
65
66 static void BM_ClosureInitAgainstCombiner(benchmark::State& state) {
67   TrackCounters track_counters;
68   grpc_combiner* combiner = grpc_combiner_create();
69   grpc_closure c;
70   grpc_core::ExecCtx exec_ctx;
71   while (state.KeepRunning()) {
72     benchmark::DoNotOptimize(GRPC_CLOSURE_INIT(
73         &c, DoNothing, nullptr, grpc_combiner_scheduler(combiner)));
74   }
75   GRPC_COMBINER_UNREF(combiner, "finished");
76
77   track_counters.Finish(state);
78 }
79 BENCHMARK(BM_ClosureInitAgainstCombiner);
80
81 static void BM_ClosureRunOnExecCtx(benchmark::State& state) {
82   TrackCounters track_counters;
83   grpc_closure c;
84   GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
85   grpc_core::ExecCtx exec_ctx;
86   while (state.KeepRunning()) {
87     GRPC_CLOSURE_RUN(&c, GRPC_ERROR_NONE);
88     grpc_core::ExecCtx::Get()->Flush();
89   }
90
91   track_counters.Finish(state);
92 }
93 BENCHMARK(BM_ClosureRunOnExecCtx);
94
95 static void BM_ClosureCreateAndRun(benchmark::State& state) {
96   TrackCounters track_counters;
97   grpc_core::ExecCtx exec_ctx;
98   while (state.KeepRunning()) {
99     GRPC_CLOSURE_RUN(
100         GRPC_CLOSURE_CREATE(DoNothing, nullptr, grpc_schedule_on_exec_ctx),
101         GRPC_ERROR_NONE);
102   }
103
104   track_counters.Finish(state);
105 }
106 BENCHMARK(BM_ClosureCreateAndRun);
107
108 static void BM_ClosureInitAndRun(benchmark::State& state) {
109   TrackCounters track_counters;
110   grpc_core::ExecCtx exec_ctx;
111   grpc_closure c;
112   while (state.KeepRunning()) {
113     GRPC_CLOSURE_RUN(
114         GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx),
115         GRPC_ERROR_NONE);
116   }
117
118   track_counters.Finish(state);
119 }
120 BENCHMARK(BM_ClosureInitAndRun);
121
122 static void BM_ClosureSchedOnExecCtx(benchmark::State& state) {
123   TrackCounters track_counters;
124   grpc_closure c;
125   GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
126   grpc_core::ExecCtx exec_ctx;
127   while (state.KeepRunning()) {
128     GRPC_CLOSURE_SCHED(&c, GRPC_ERROR_NONE);
129     grpc_core::ExecCtx::Get()->Flush();
130   }
131
132   track_counters.Finish(state);
133 }
134 BENCHMARK(BM_ClosureSchedOnExecCtx);
135
136 static void BM_ClosureSched2OnExecCtx(benchmark::State& state) {
137   TrackCounters track_counters;
138   grpc_closure c1;
139   grpc_closure c2;
140   GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
141   GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
142   grpc_core::ExecCtx exec_ctx;
143   while (state.KeepRunning()) {
144     GRPC_CLOSURE_SCHED(&c1, GRPC_ERROR_NONE);
145     GRPC_CLOSURE_SCHED(&c2, GRPC_ERROR_NONE);
146     grpc_core::ExecCtx::Get()->Flush();
147   }
148
149   track_counters.Finish(state);
150 }
151 BENCHMARK(BM_ClosureSched2OnExecCtx);
152
153 static void BM_ClosureSched3OnExecCtx(benchmark::State& state) {
154   TrackCounters track_counters;
155   grpc_closure c1;
156   grpc_closure c2;
157   grpc_closure c3;
158   GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
159   GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
160   GRPC_CLOSURE_INIT(&c3, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
161   grpc_core::ExecCtx exec_ctx;
162   while (state.KeepRunning()) {
163     GRPC_CLOSURE_SCHED(&c1, GRPC_ERROR_NONE);
164     GRPC_CLOSURE_SCHED(&c2, GRPC_ERROR_NONE);
165     GRPC_CLOSURE_SCHED(&c3, GRPC_ERROR_NONE);
166     grpc_core::ExecCtx::Get()->Flush();
167   }
168
169   track_counters.Finish(state);
170 }
171 BENCHMARK(BM_ClosureSched3OnExecCtx);
172
173 static void BM_AcquireMutex(benchmark::State& state) {
174   TrackCounters track_counters;
175   // for comparison with the combiner stuff below
176   gpr_mu mu;
177   gpr_mu_init(&mu);
178   grpc_core::ExecCtx exec_ctx;
179   while (state.KeepRunning()) {
180     gpr_mu_lock(&mu);
181     DoNothing(nullptr, GRPC_ERROR_NONE);
182     gpr_mu_unlock(&mu);
183   }
184   gpr_mu_destroy(&mu);
185
186   track_counters.Finish(state);
187 }
188 BENCHMARK(BM_AcquireMutex);
189
190 static void BM_TryAcquireMutex(benchmark::State& state) {
191   TrackCounters track_counters;
192   // for comparison with the combiner stuff below
193   gpr_mu mu;
194   gpr_mu_init(&mu);
195   grpc_core::ExecCtx exec_ctx;
196   while (state.KeepRunning()) {
197     if (gpr_mu_trylock(&mu)) {
198       DoNothing(nullptr, GRPC_ERROR_NONE);
199       gpr_mu_unlock(&mu);
200     } else {
201       abort();
202     }
203   }
204   gpr_mu_destroy(&mu);
205
206   track_counters.Finish(state);
207 }
208 BENCHMARK(BM_TryAcquireMutex);
209
210 static void BM_AcquireSpinlock(benchmark::State& state) {
211   TrackCounters track_counters;
212   // for comparison with the combiner stuff below
213   gpr_spinlock mu = GPR_SPINLOCK_INITIALIZER;
214   grpc_core::ExecCtx exec_ctx;
215   while (state.KeepRunning()) {
216     gpr_spinlock_lock(&mu);
217     DoNothing(nullptr, GRPC_ERROR_NONE);
218     gpr_spinlock_unlock(&mu);
219   }
220
221   track_counters.Finish(state);
222 }
223 BENCHMARK(BM_AcquireSpinlock);
224
225 static void BM_TryAcquireSpinlock(benchmark::State& state) {
226   TrackCounters track_counters;
227   // for comparison with the combiner stuff below
228   gpr_spinlock mu = GPR_SPINLOCK_INITIALIZER;
229   grpc_core::ExecCtx exec_ctx;
230   while (state.KeepRunning()) {
231     if (gpr_spinlock_trylock(&mu)) {
232       DoNothing(nullptr, GRPC_ERROR_NONE);
233       gpr_spinlock_unlock(&mu);
234     } else {
235       abort();
236     }
237   }
238
239   track_counters.Finish(state);
240 }
241 BENCHMARK(BM_TryAcquireSpinlock);
242
243 static void BM_ClosureSchedOnCombiner(benchmark::State& state) {
244   TrackCounters track_counters;
245   grpc_combiner* combiner = grpc_combiner_create();
246   grpc_closure c;
247   GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_combiner_scheduler(combiner));
248   grpc_core::ExecCtx exec_ctx;
249   while (state.KeepRunning()) {
250     GRPC_CLOSURE_SCHED(&c, GRPC_ERROR_NONE);
251     grpc_core::ExecCtx::Get()->Flush();
252   }
253   GRPC_COMBINER_UNREF(combiner, "finished");
254
255   track_counters.Finish(state);
256 }
257 BENCHMARK(BM_ClosureSchedOnCombiner);
258
259 static void BM_ClosureSched2OnCombiner(benchmark::State& state) {
260   TrackCounters track_counters;
261   grpc_combiner* combiner = grpc_combiner_create();
262   grpc_closure c1;
263   grpc_closure c2;
264   GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, grpc_combiner_scheduler(combiner));
265   GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, grpc_combiner_scheduler(combiner));
266   grpc_core::ExecCtx exec_ctx;
267   while (state.KeepRunning()) {
268     GRPC_CLOSURE_SCHED(&c1, GRPC_ERROR_NONE);
269     GRPC_CLOSURE_SCHED(&c2, GRPC_ERROR_NONE);
270     grpc_core::ExecCtx::Get()->Flush();
271   }
272   GRPC_COMBINER_UNREF(combiner, "finished");
273
274   track_counters.Finish(state);
275 }
276 BENCHMARK(BM_ClosureSched2OnCombiner);
277
278 static void BM_ClosureSched3OnCombiner(benchmark::State& state) {
279   TrackCounters track_counters;
280   grpc_combiner* combiner = grpc_combiner_create();
281   grpc_closure c1;
282   grpc_closure c2;
283   grpc_closure c3;
284   GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, grpc_combiner_scheduler(combiner));
285   GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, grpc_combiner_scheduler(combiner));
286   GRPC_CLOSURE_INIT(&c3, DoNothing, nullptr, grpc_combiner_scheduler(combiner));
287   grpc_core::ExecCtx exec_ctx;
288   while (state.KeepRunning()) {
289     GRPC_CLOSURE_SCHED(&c1, GRPC_ERROR_NONE);
290     GRPC_CLOSURE_SCHED(&c2, GRPC_ERROR_NONE);
291     GRPC_CLOSURE_SCHED(&c3, GRPC_ERROR_NONE);
292     grpc_core::ExecCtx::Get()->Flush();
293   }
294   GRPC_COMBINER_UNREF(combiner, "finished");
295
296   track_counters.Finish(state);
297 }
298 BENCHMARK(BM_ClosureSched3OnCombiner);
299
300 static void BM_ClosureSched2OnTwoCombiners(benchmark::State& state) {
301   TrackCounters track_counters;
302   grpc_combiner* combiner1 = grpc_combiner_create();
303   grpc_combiner* combiner2 = grpc_combiner_create();
304   grpc_closure c1;
305   grpc_closure c2;
306   GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr,
307                     grpc_combiner_scheduler(combiner1));
308   GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr,
309                     grpc_combiner_scheduler(combiner2));
310   grpc_core::ExecCtx exec_ctx;
311   while (state.KeepRunning()) {
312     GRPC_CLOSURE_SCHED(&c1, GRPC_ERROR_NONE);
313     GRPC_CLOSURE_SCHED(&c2, GRPC_ERROR_NONE);
314     grpc_core::ExecCtx::Get()->Flush();
315   }
316   GRPC_COMBINER_UNREF(combiner1, "finished");
317   GRPC_COMBINER_UNREF(combiner2, "finished");
318
319   track_counters.Finish(state);
320 }
321 BENCHMARK(BM_ClosureSched2OnTwoCombiners);
322
323 static void BM_ClosureSched4OnTwoCombiners(benchmark::State& state) {
324   TrackCounters track_counters;
325   grpc_combiner* combiner1 = grpc_combiner_create();
326   grpc_combiner* combiner2 = grpc_combiner_create();
327   grpc_closure c1;
328   grpc_closure c2;
329   grpc_closure c3;
330   grpc_closure c4;
331   GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr,
332                     grpc_combiner_scheduler(combiner1));
333   GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr,
334                     grpc_combiner_scheduler(combiner2));
335   GRPC_CLOSURE_INIT(&c3, DoNothing, nullptr,
336                     grpc_combiner_scheduler(combiner1));
337   GRPC_CLOSURE_INIT(&c4, DoNothing, nullptr,
338                     grpc_combiner_scheduler(combiner2));
339   grpc_core::ExecCtx exec_ctx;
340   while (state.KeepRunning()) {
341     GRPC_CLOSURE_SCHED(&c1, GRPC_ERROR_NONE);
342     GRPC_CLOSURE_SCHED(&c2, GRPC_ERROR_NONE);
343     GRPC_CLOSURE_SCHED(&c3, GRPC_ERROR_NONE);
344     GRPC_CLOSURE_SCHED(&c4, GRPC_ERROR_NONE);
345     grpc_core::ExecCtx::Get()->Flush();
346   }
347   GRPC_COMBINER_UNREF(combiner1, "finished");
348   GRPC_COMBINER_UNREF(combiner2, "finished");
349
350   track_counters.Finish(state);
351 }
352 BENCHMARK(BM_ClosureSched4OnTwoCombiners);
353
354 // Helper that continuously reschedules the same closure against something until
355 // the benchmark is complete
356 class Rescheduler {
357  public:
358   Rescheduler(benchmark::State& state, grpc_closure_scheduler* scheduler)
359       : state_(state) {
360     GRPC_CLOSURE_INIT(&closure_, Step, this, scheduler);
361   }
362
363   void ScheduleFirst() { GRPC_CLOSURE_SCHED(&closure_, GRPC_ERROR_NONE); }
364
365   void ScheduleFirstAgainstDifferentScheduler(
366       grpc_closure_scheduler* scheduler) {
367     GRPC_CLOSURE_SCHED(GRPC_CLOSURE_CREATE(Step, this, scheduler),
368                        GRPC_ERROR_NONE);
369   }
370
371  private:
372   benchmark::State& state_;
373   grpc_closure closure_;
374
375   static void Step(void* arg, grpc_error* error) {
376     Rescheduler* self = static_cast<Rescheduler*>(arg);
377     if (self->state_.KeepRunning()) {
378       GRPC_CLOSURE_SCHED(&self->closure_, GRPC_ERROR_NONE);
379     }
380   }
381 };
382
383 static void BM_ClosureReschedOnExecCtx(benchmark::State& state) {
384   TrackCounters track_counters;
385   grpc_core::ExecCtx exec_ctx;
386   Rescheduler r(state, grpc_schedule_on_exec_ctx);
387   r.ScheduleFirst();
388   grpc_core::ExecCtx::Get()->Flush();
389   track_counters.Finish(state);
390 }
391 BENCHMARK(BM_ClosureReschedOnExecCtx);
392
393 static void BM_ClosureReschedOnCombiner(benchmark::State& state) {
394   TrackCounters track_counters;
395   grpc_core::ExecCtx exec_ctx;
396   grpc_combiner* combiner = grpc_combiner_create();
397   Rescheduler r(state, grpc_combiner_scheduler(combiner));
398   r.ScheduleFirst();
399   grpc_core::ExecCtx::Get()->Flush();
400   GRPC_COMBINER_UNREF(combiner, "finished");
401
402   track_counters.Finish(state);
403 }
404 BENCHMARK(BM_ClosureReschedOnCombiner);
405
406 static void BM_ClosureReschedOnCombinerFinally(benchmark::State& state) {
407   TrackCounters track_counters;
408   grpc_core::ExecCtx exec_ctx;
409   grpc_combiner* combiner = grpc_combiner_create();
410   Rescheduler r(state, grpc_combiner_finally_scheduler(combiner));
411   r.ScheduleFirstAgainstDifferentScheduler(grpc_combiner_scheduler(combiner));
412   grpc_core::ExecCtx::Get()->Flush();
413   GRPC_COMBINER_UNREF(combiner, "finished");
414
415   track_counters.Finish(state);
416 }
417 BENCHMARK(BM_ClosureReschedOnCombinerFinally);
418
419 // Some distros have RunSpecifiedBenchmarks under the benchmark namespace,
420 // and others do not. This allows us to support both modes.
421 namespace benchmark {
422 void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
423 }  // namespace benchmark
424
425 int main(int argc, char** argv) {
426   LibraryInitializer libInit;
427   ::benchmark::Initialize(&argc, argv);
428   ::grpc::testing::InitTest(&argc, &argv, false);
429   benchmark::RunTheBenchmarksNamespaced();
430   return 0;
431 }