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