• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 auto& force_library_initialization = Library::get();
34 
BM_NoOpExecCtx(benchmark::State & state)35 static void BM_NoOpExecCtx(benchmark::State& state) {
36   TrackCounters track_counters;
37   while (state.KeepRunning()) {
38     grpc_core::ExecCtx exec_ctx;
39   }
40   track_counters.Finish(state);
41 }
42 BENCHMARK(BM_NoOpExecCtx);
43 
BM_WellFlushed(benchmark::State & state)44 static void BM_WellFlushed(benchmark::State& state) {
45   TrackCounters track_counters;
46   grpc_core::ExecCtx exec_ctx;
47   while (state.KeepRunning()) {
48     grpc_core::ExecCtx::Get()->Flush();
49   }
50 
51   track_counters.Finish(state);
52 }
53 BENCHMARK(BM_WellFlushed);
54 
DoNothing(void * arg,grpc_error * error)55 static void DoNothing(void* arg, grpc_error* error) {}
56 
BM_ClosureInitAgainstExecCtx(benchmark::State & state)57 static void BM_ClosureInitAgainstExecCtx(benchmark::State& state) {
58   TrackCounters track_counters;
59   grpc_closure c;
60   while (state.KeepRunning()) {
61     benchmark::DoNotOptimize(
62         GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx));
63   }
64   track_counters.Finish(state);
65 }
66 BENCHMARK(BM_ClosureInitAgainstExecCtx);
67 
BM_ClosureInitAgainstCombiner(benchmark::State & state)68 static void BM_ClosureInitAgainstCombiner(benchmark::State& state) {
69   TrackCounters track_counters;
70   grpc_combiner* combiner = grpc_combiner_create();
71   grpc_closure c;
72   grpc_core::ExecCtx exec_ctx;
73   while (state.KeepRunning()) {
74     benchmark::DoNotOptimize(GRPC_CLOSURE_INIT(
75         &c, DoNothing, nullptr, grpc_combiner_scheduler(combiner)));
76   }
77   GRPC_COMBINER_UNREF(combiner, "finished");
78 
79   track_counters.Finish(state);
80 }
81 BENCHMARK(BM_ClosureInitAgainstCombiner);
82 
BM_ClosureRunOnExecCtx(benchmark::State & state)83 static void BM_ClosureRunOnExecCtx(benchmark::State& state) {
84   TrackCounters track_counters;
85   grpc_closure c;
86   GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
87   grpc_core::ExecCtx exec_ctx;
88   while (state.KeepRunning()) {
89     GRPC_CLOSURE_RUN(&c, GRPC_ERROR_NONE);
90     grpc_core::ExecCtx::Get()->Flush();
91   }
92 
93   track_counters.Finish(state);
94 }
95 BENCHMARK(BM_ClosureRunOnExecCtx);
96 
BM_ClosureCreateAndRun(benchmark::State & state)97 static void BM_ClosureCreateAndRun(benchmark::State& state) {
98   TrackCounters track_counters;
99   grpc_core::ExecCtx exec_ctx;
100   while (state.KeepRunning()) {
101     GRPC_CLOSURE_RUN(
102         GRPC_CLOSURE_CREATE(DoNothing, nullptr, grpc_schedule_on_exec_ctx),
103         GRPC_ERROR_NONE);
104   }
105 
106   track_counters.Finish(state);
107 }
108 BENCHMARK(BM_ClosureCreateAndRun);
109 
BM_ClosureInitAndRun(benchmark::State & state)110 static void BM_ClosureInitAndRun(benchmark::State& state) {
111   TrackCounters track_counters;
112   grpc_core::ExecCtx exec_ctx;
113   grpc_closure c;
114   while (state.KeepRunning()) {
115     GRPC_CLOSURE_RUN(
116         GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx),
117         GRPC_ERROR_NONE);
118   }
119 
120   track_counters.Finish(state);
121 }
122 BENCHMARK(BM_ClosureInitAndRun);
123 
BM_ClosureSchedOnExecCtx(benchmark::State & state)124 static void BM_ClosureSchedOnExecCtx(benchmark::State& state) {
125   TrackCounters track_counters;
126   grpc_closure c;
127   GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
128   grpc_core::ExecCtx exec_ctx;
129   while (state.KeepRunning()) {
130     GRPC_CLOSURE_SCHED(&c, GRPC_ERROR_NONE);
131     grpc_core::ExecCtx::Get()->Flush();
132   }
133 
134   track_counters.Finish(state);
135 }
136 BENCHMARK(BM_ClosureSchedOnExecCtx);
137 
BM_ClosureSched2OnExecCtx(benchmark::State & state)138 static void BM_ClosureSched2OnExecCtx(benchmark::State& state) {
139   TrackCounters track_counters;
140   grpc_closure c1;
141   grpc_closure c2;
142   GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
143   GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
144   grpc_core::ExecCtx exec_ctx;
145   while (state.KeepRunning()) {
146     GRPC_CLOSURE_SCHED(&c1, GRPC_ERROR_NONE);
147     GRPC_CLOSURE_SCHED(&c2, GRPC_ERROR_NONE);
148     grpc_core::ExecCtx::Get()->Flush();
149   }
150 
151   track_counters.Finish(state);
152 }
153 BENCHMARK(BM_ClosureSched2OnExecCtx);
154 
BM_ClosureSched3OnExecCtx(benchmark::State & state)155 static void BM_ClosureSched3OnExecCtx(benchmark::State& state) {
156   TrackCounters track_counters;
157   grpc_closure c1;
158   grpc_closure c2;
159   grpc_closure c3;
160   GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
161   GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
162   GRPC_CLOSURE_INIT(&c3, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
163   grpc_core::ExecCtx exec_ctx;
164   while (state.KeepRunning()) {
165     GRPC_CLOSURE_SCHED(&c1, GRPC_ERROR_NONE);
166     GRPC_CLOSURE_SCHED(&c2, GRPC_ERROR_NONE);
167     GRPC_CLOSURE_SCHED(&c3, GRPC_ERROR_NONE);
168     grpc_core::ExecCtx::Get()->Flush();
169   }
170 
171   track_counters.Finish(state);
172 }
173 BENCHMARK(BM_ClosureSched3OnExecCtx);
174 
BM_AcquireMutex(benchmark::State & state)175 static void BM_AcquireMutex(benchmark::State& state) {
176   TrackCounters track_counters;
177   // for comparison with the combiner stuff below
178   gpr_mu mu;
179   gpr_mu_init(&mu);
180   grpc_core::ExecCtx exec_ctx;
181   while (state.KeepRunning()) {
182     gpr_mu_lock(&mu);
183     DoNothing(nullptr, GRPC_ERROR_NONE);
184     gpr_mu_unlock(&mu);
185   }
186 
187   track_counters.Finish(state);
188 }
189 BENCHMARK(BM_AcquireMutex);
190 
BM_TryAcquireMutex(benchmark::State & state)191 static void BM_TryAcquireMutex(benchmark::State& state) {
192   TrackCounters track_counters;
193   // for comparison with the combiner stuff below
194   gpr_mu mu;
195   gpr_mu_init(&mu);
196   grpc_core::ExecCtx exec_ctx;
197   while (state.KeepRunning()) {
198     if (gpr_mu_trylock(&mu)) {
199       DoNothing(nullptr, GRPC_ERROR_NONE);
200       gpr_mu_unlock(&mu);
201     } else {
202       abort();
203     }
204   }
205 
206   track_counters.Finish(state);
207 }
208 BENCHMARK(BM_TryAcquireMutex);
209 
BM_AcquireSpinlock(benchmark::State & state)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 
BM_TryAcquireSpinlock(benchmark::State & state)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 
BM_ClosureSchedOnCombiner(benchmark::State & state)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 
BM_ClosureSched2OnCombiner(benchmark::State & state)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 
BM_ClosureSched3OnCombiner(benchmark::State & state)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 
BM_ClosureSched2OnTwoCombiners(benchmark::State & state)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 
BM_ClosureSched4OnTwoCombiners(benchmark::State & state)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:
Rescheduler(benchmark::State & state,grpc_closure_scheduler * scheduler)358   Rescheduler(benchmark::State& state, grpc_closure_scheduler* scheduler)
359       : state_(state) {
360     GRPC_CLOSURE_INIT(&closure_, Step, this, scheduler);
361   }
362 
ScheduleFirst()363   void ScheduleFirst() { GRPC_CLOSURE_SCHED(&closure_, GRPC_ERROR_NONE); }
364 
ScheduleFirstAgainstDifferentScheduler(grpc_closure_scheduler * scheduler)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 
Step(void * arg,grpc_error * error)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 
BM_ClosureReschedOnExecCtx(benchmark::State & state)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 
BM_ClosureReschedOnCombiner(benchmark::State & state)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 
BM_ClosureReschedOnCombinerFinally(benchmark::State & state)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 {
RunTheBenchmarksNamespaced()422 void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
423 }  // namespace benchmark
424 
main(int argc,char ** argv)425 int main(int argc, char** argv) {
426   ::benchmark::Initialize(&argc, argv);
427   ::grpc::testing::InitTest(&argc, &argv, false);
428   benchmark::RunTheBenchmarksNamespaced();
429   return 0;
430 }
431