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