• 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 
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