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