• 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/core/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   TrackCounters track_counters;
36   for (auto _ : state) {
37     grpc_core::ExecCtx exec_ctx;
38   }
39   track_counters.Finish(state);
40 }
41 BENCHMARK(BM_NoOpExecCtx);
42 
BM_WellFlushed(benchmark::State & state)43 static void BM_WellFlushed(benchmark::State& state) {
44   TrackCounters track_counters;
45   grpc_core::ExecCtx exec_ctx;
46   for (auto _ : state) {
47     grpc_core::ExecCtx::Get()->Flush();
48   }
49 
50   track_counters.Finish(state);
51 }
52 BENCHMARK(BM_WellFlushed);
53 
DoNothing(void *,grpc_error *)54 static void DoNothing(void* /*arg*/, grpc_error* /*error*/) {}
55 
BM_ClosureInitAgainstExecCtx(benchmark::State & state)56 static void BM_ClosureInitAgainstExecCtx(benchmark::State& state) {
57   TrackCounters track_counters;
58   grpc_closure c;
59   for (auto _ : state) {
60     benchmark::DoNotOptimize(
61         GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx));
62   }
63   track_counters.Finish(state);
64 }
65 BENCHMARK(BM_ClosureInitAgainstExecCtx);
66 
BM_ClosureInitAgainstCombiner(benchmark::State & state)67 static void BM_ClosureInitAgainstCombiner(benchmark::State& state) {
68   TrackCounters track_counters;
69   grpc_core::Combiner* combiner = grpc_combiner_create();
70   grpc_closure c;
71   grpc_core::ExecCtx exec_ctx;
72   for (auto _ : state) {
73     benchmark::DoNotOptimize(
74         GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, nullptr));
75   }
76   GRPC_COMBINER_UNREF(combiner, "finished");
77 
78   track_counters.Finish(state);
79 }
80 BENCHMARK(BM_ClosureInitAgainstCombiner);
81 
BM_ClosureRun(benchmark::State & state)82 static void BM_ClosureRun(benchmark::State& state) {
83   TrackCounters track_counters;
84   grpc_closure c;
85   GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
86   grpc_core::ExecCtx exec_ctx;
87   for (auto _ : state) {
88     grpc_core::Closure::Run(DEBUG_LOCATION, &c, GRPC_ERROR_NONE);
89   }
90 
91   track_counters.Finish(state);
92 }
93 BENCHMARK(BM_ClosureRun);
94 
BM_ClosureCreateAndRun(benchmark::State & state)95 static void BM_ClosureCreateAndRun(benchmark::State& state) {
96   TrackCounters track_counters;
97   grpc_core::ExecCtx exec_ctx;
98   for (auto _ : state) {
99     grpc_core::Closure::Run(
100         DEBUG_LOCATION,
101         GRPC_CLOSURE_CREATE(DoNothing, nullptr, grpc_schedule_on_exec_ctx),
102         GRPC_ERROR_NONE);
103   }
104 
105   track_counters.Finish(state);
106 }
107 BENCHMARK(BM_ClosureCreateAndRun);
108 
BM_ClosureInitAndRun(benchmark::State & state)109 static void BM_ClosureInitAndRun(benchmark::State& state) {
110   TrackCounters track_counters;
111   grpc_core::ExecCtx exec_ctx;
112   grpc_closure c;
113   for (auto _ : state) {
114     grpc_core::Closure::Run(
115         DEBUG_LOCATION,
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   for (auto _ : state) {
130     grpc_core::ExecCtx::Run(DEBUG_LOCATION, &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   for (auto _ : state) {
146     grpc_core::ExecCtx::Run(DEBUG_LOCATION, &c1, GRPC_ERROR_NONE);
147     grpc_core::ExecCtx::Run(DEBUG_LOCATION, &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   for (auto _ : state) {
165     grpc_core::ExecCtx::Run(DEBUG_LOCATION, &c1, GRPC_ERROR_NONE);
166     grpc_core::ExecCtx::Run(DEBUG_LOCATION, &c2, GRPC_ERROR_NONE);
167     grpc_core::ExecCtx::Run(DEBUG_LOCATION, &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   for (auto _ : state) {
182     gpr_mu_lock(&mu);
183     DoNothing(nullptr, GRPC_ERROR_NONE);
184     gpr_mu_unlock(&mu);
185   }
186   gpr_mu_destroy(&mu);
187 
188   track_counters.Finish(state);
189 }
190 BENCHMARK(BM_AcquireMutex);
191 
BM_TryAcquireMutex(benchmark::State & state)192 static void BM_TryAcquireMutex(benchmark::State& state) {
193   TrackCounters track_counters;
194   // for comparison with the combiner stuff below
195   gpr_mu mu;
196   gpr_mu_init(&mu);
197   grpc_core::ExecCtx exec_ctx;
198   for (auto _ : state) {
199     if (gpr_mu_trylock(&mu)) {
200       DoNothing(nullptr, GRPC_ERROR_NONE);
201       gpr_mu_unlock(&mu);
202     } else {
203       abort();
204     }
205   }
206   gpr_mu_destroy(&mu);
207 
208   track_counters.Finish(state);
209 }
210 BENCHMARK(BM_TryAcquireMutex);
211 
BM_AcquireSpinlock(benchmark::State & state)212 static void BM_AcquireSpinlock(benchmark::State& state) {
213   TrackCounters track_counters;
214   // for comparison with the combiner stuff below
215   gpr_spinlock mu = GPR_SPINLOCK_INITIALIZER;
216   grpc_core::ExecCtx exec_ctx;
217   for (auto _ : state) {
218     gpr_spinlock_lock(&mu);
219     DoNothing(nullptr, GRPC_ERROR_NONE);
220     gpr_spinlock_unlock(&mu);
221   }
222 
223   track_counters.Finish(state);
224 }
225 BENCHMARK(BM_AcquireSpinlock);
226 
BM_TryAcquireSpinlock(benchmark::State & state)227 static void BM_TryAcquireSpinlock(benchmark::State& state) {
228   TrackCounters track_counters;
229   // for comparison with the combiner stuff below
230   gpr_spinlock mu = GPR_SPINLOCK_INITIALIZER;
231   grpc_core::ExecCtx exec_ctx;
232   for (auto _ : state) {
233     if (gpr_spinlock_trylock(&mu)) {
234       DoNothing(nullptr, GRPC_ERROR_NONE);
235       gpr_spinlock_unlock(&mu);
236     } else {
237       abort();
238     }
239   }
240 
241   track_counters.Finish(state);
242 }
243 BENCHMARK(BM_TryAcquireSpinlock);
244 
BM_ClosureSchedOnCombiner(benchmark::State & state)245 static void BM_ClosureSchedOnCombiner(benchmark::State& state) {
246   TrackCounters track_counters;
247   grpc_core::Combiner* combiner = grpc_combiner_create();
248   grpc_closure c;
249   GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, nullptr);
250   grpc_core::ExecCtx exec_ctx;
251   for (auto _ : state) {
252     combiner->Run(&c, GRPC_ERROR_NONE);
253     grpc_core::ExecCtx::Get()->Flush();
254   }
255   GRPC_COMBINER_UNREF(combiner, "finished");
256 
257   track_counters.Finish(state);
258 }
259 BENCHMARK(BM_ClosureSchedOnCombiner);
260 
BM_ClosureSched2OnCombiner(benchmark::State & state)261 static void BM_ClosureSched2OnCombiner(benchmark::State& state) {
262   TrackCounters track_counters;
263   grpc_core::Combiner* combiner = grpc_combiner_create();
264   grpc_closure c1;
265   grpc_closure c2;
266   GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, nullptr);
267   GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, nullptr);
268   grpc_core::ExecCtx exec_ctx;
269   for (auto _ : state) {
270     combiner->Run(&c1, GRPC_ERROR_NONE);
271     combiner->Run(&c2, GRPC_ERROR_NONE);
272     grpc_core::ExecCtx::Get()->Flush();
273   }
274   GRPC_COMBINER_UNREF(combiner, "finished");
275 
276   track_counters.Finish(state);
277 }
278 BENCHMARK(BM_ClosureSched2OnCombiner);
279 
BM_ClosureSched3OnCombiner(benchmark::State & state)280 static void BM_ClosureSched3OnCombiner(benchmark::State& state) {
281   TrackCounters track_counters;
282   grpc_core::Combiner* combiner = grpc_combiner_create();
283   grpc_closure c1;
284   grpc_closure c2;
285   grpc_closure c3;
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_core::ExecCtx exec_ctx;
290   for (auto _ : state) {
291     combiner->Run(&c1, GRPC_ERROR_NONE);
292     combiner->Run(&c2, GRPC_ERROR_NONE);
293     combiner->Run(&c3, GRPC_ERROR_NONE);
294     grpc_core::ExecCtx::Get()->Flush();
295   }
296   GRPC_COMBINER_UNREF(combiner, "finished");
297 
298   track_counters.Finish(state);
299 }
300 BENCHMARK(BM_ClosureSched3OnCombiner);
301 
BM_ClosureSched2OnTwoCombiners(benchmark::State & state)302 static void BM_ClosureSched2OnTwoCombiners(benchmark::State& state) {
303   TrackCounters track_counters;
304   grpc_core::Combiner* combiner1 = grpc_combiner_create();
305   grpc_core::Combiner* combiner2 = grpc_combiner_create();
306   grpc_closure c1;
307   grpc_closure c2;
308   GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, nullptr);
309   GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, nullptr);
310   grpc_core::ExecCtx exec_ctx;
311   for (auto _ : state) {
312     combiner1->Run(&c1, GRPC_ERROR_NONE);
313     combiner2->Run(&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_core::Combiner* combiner1 = grpc_combiner_create();
326   grpc_core::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, nullptr);
332   GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, nullptr);
333   GRPC_CLOSURE_INIT(&c3, DoNothing, nullptr, nullptr);
334   GRPC_CLOSURE_INIT(&c4, DoNothing, nullptr, nullptr);
335   grpc_core::ExecCtx exec_ctx;
336   for (auto _ : state) {
337     combiner1->Run(&c1, GRPC_ERROR_NONE);
338     combiner2->Run(&c2, GRPC_ERROR_NONE);
339     combiner1->Run(&c3, GRPC_ERROR_NONE);
340     combiner2->Run(&c4, GRPC_ERROR_NONE);
341     grpc_core::ExecCtx::Get()->Flush();
342   }
343   GRPC_COMBINER_UNREF(combiner1, "finished");
344   GRPC_COMBINER_UNREF(combiner2, "finished");
345 
346   track_counters.Finish(state);
347 }
348 BENCHMARK(BM_ClosureSched4OnTwoCombiners);
349 
350 // Helper that continuously reschedules the same closure against something until
351 // the benchmark is complete
352 class Rescheduler {
353  public:
Rescheduler(benchmark::State & state)354   explicit Rescheduler(benchmark::State& state) : state_(state) {
355     GRPC_CLOSURE_INIT(&closure_, Step, this, nullptr);
356   }
357 
ScheduleFirst()358   void ScheduleFirst() {
359     grpc_core::ExecCtx::Run(DEBUG_LOCATION, &closure_, GRPC_ERROR_NONE);
360   }
361 
ScheduleFirstAgainstDifferentScheduler()362   void ScheduleFirstAgainstDifferentScheduler() {
363     grpc_core::ExecCtx::Run(DEBUG_LOCATION,
364                             GRPC_CLOSURE_CREATE(Step, this, nullptr),
365                             GRPC_ERROR_NONE);
366   }
367 
368  private:
369   benchmark::State& state_;
370   grpc_closure closure_;
371 
Step(void * arg,grpc_error *)372   static void Step(void* arg, grpc_error* /*error*/) {
373     Rescheduler* self = static_cast<Rescheduler*>(arg);
374     if (self->state_.KeepRunning()) {
375       grpc_core::ExecCtx::Run(DEBUG_LOCATION, &self->closure_, GRPC_ERROR_NONE);
376     }
377   }
378 };
379 
BM_ClosureReschedOnExecCtx(benchmark::State & state)380 static void BM_ClosureReschedOnExecCtx(benchmark::State& state) {
381   TrackCounters track_counters;
382   grpc_core::ExecCtx exec_ctx;
383   Rescheduler r(state);
384   r.ScheduleFirst();
385   grpc_core::ExecCtx::Get()->Flush();
386   track_counters.Finish(state);
387 }
388 BENCHMARK(BM_ClosureReschedOnExecCtx);
389 
390 // Some distros have RunSpecifiedBenchmarks under the benchmark namespace,
391 // and others do not. This allows us to support both modes.
392 namespace benchmark {
RunTheBenchmarksNamespaced()393 void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
394 }  // namespace benchmark
395 
main(int argc,char ** argv)396 int main(int argc, char** argv) {
397   grpc::testing::TestEnvironment env(argc, argv);
398   LibraryInitializer libInit;
399   ::benchmark::Initialize(&argc, argv);
400   ::grpc::testing::InitTest(&argc, &argv, false);
401   benchmark::RunTheBenchmarksNamespaced();
402   return 0;
403 }
404