3 * Copyright 2017 gRPC authors.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 /* Test various closure related operations */
21 #include <benchmark/benchmark.h>
22 #include <grpc/grpc.h>
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"
30 #include "test/cpp/microbenchmarks/helpers.h"
31 #include "test/cpp/util/test_config.h"
33 static void BM_NoOpExecCtx(benchmark::State& state) {
34 TrackCounters track_counters;
35 while (state.KeepRunning()) {
36 grpc_core::ExecCtx exec_ctx;
38 track_counters.Finish(state);
40 BENCHMARK(BM_NoOpExecCtx);
42 static void BM_WellFlushed(benchmark::State& state) {
43 TrackCounters track_counters;
44 grpc_core::ExecCtx exec_ctx;
45 while (state.KeepRunning()) {
46 grpc_core::ExecCtx::Get()->Flush();
49 track_counters.Finish(state);
51 BENCHMARK(BM_WellFlushed);
53 static void DoNothing(void* arg, grpc_error* error) {}
55 static void BM_ClosureInitAgainstExecCtx(benchmark::State& state) {
56 TrackCounters track_counters;
58 while (state.KeepRunning()) {
59 benchmark::DoNotOptimize(
60 GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx));
62 track_counters.Finish(state);
64 BENCHMARK(BM_ClosureInitAgainstExecCtx);
66 static void BM_ClosureInitAgainstCombiner(benchmark::State& state) {
67 TrackCounters track_counters;
68 grpc_combiner* combiner = grpc_combiner_create();
70 grpc_core::ExecCtx exec_ctx;
71 while (state.KeepRunning()) {
72 benchmark::DoNotOptimize(GRPC_CLOSURE_INIT(
73 &c, DoNothing, nullptr, grpc_combiner_scheduler(combiner)));
75 GRPC_COMBINER_UNREF(combiner, "finished");
77 track_counters.Finish(state);
79 BENCHMARK(BM_ClosureInitAgainstCombiner);
81 static void BM_ClosureRunOnExecCtx(benchmark::State& state) {
82 TrackCounters track_counters;
84 GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
85 grpc_core::ExecCtx exec_ctx;
86 while (state.KeepRunning()) {
87 GRPC_CLOSURE_RUN(&c, GRPC_ERROR_NONE);
88 grpc_core::ExecCtx::Get()->Flush();
91 track_counters.Finish(state);
93 BENCHMARK(BM_ClosureRunOnExecCtx);
95 static void BM_ClosureCreateAndRun(benchmark::State& state) {
96 TrackCounters track_counters;
97 grpc_core::ExecCtx exec_ctx;
98 while (state.KeepRunning()) {
100 GRPC_CLOSURE_CREATE(DoNothing, nullptr, grpc_schedule_on_exec_ctx),
104 track_counters.Finish(state);
106 BENCHMARK(BM_ClosureCreateAndRun);
108 static void BM_ClosureInitAndRun(benchmark::State& state) {
109 TrackCounters track_counters;
110 grpc_core::ExecCtx exec_ctx;
112 while (state.KeepRunning()) {
114 GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx),
118 track_counters.Finish(state);
120 BENCHMARK(BM_ClosureInitAndRun);
122 static void BM_ClosureSchedOnExecCtx(benchmark::State& state) {
123 TrackCounters track_counters;
125 GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
126 grpc_core::ExecCtx exec_ctx;
127 while (state.KeepRunning()) {
128 GRPC_CLOSURE_SCHED(&c, GRPC_ERROR_NONE);
129 grpc_core::ExecCtx::Get()->Flush();
132 track_counters.Finish(state);
134 BENCHMARK(BM_ClosureSchedOnExecCtx);
136 static void BM_ClosureSched2OnExecCtx(benchmark::State& state) {
137 TrackCounters track_counters;
140 GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
141 GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
142 grpc_core::ExecCtx exec_ctx;
143 while (state.KeepRunning()) {
144 GRPC_CLOSURE_SCHED(&c1, GRPC_ERROR_NONE);
145 GRPC_CLOSURE_SCHED(&c2, GRPC_ERROR_NONE);
146 grpc_core::ExecCtx::Get()->Flush();
149 track_counters.Finish(state);
151 BENCHMARK(BM_ClosureSched2OnExecCtx);
153 static void BM_ClosureSched3OnExecCtx(benchmark::State& state) {
154 TrackCounters track_counters;
158 GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
159 GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
160 GRPC_CLOSURE_INIT(&c3, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
161 grpc_core::ExecCtx exec_ctx;
162 while (state.KeepRunning()) {
163 GRPC_CLOSURE_SCHED(&c1, GRPC_ERROR_NONE);
164 GRPC_CLOSURE_SCHED(&c2, GRPC_ERROR_NONE);
165 GRPC_CLOSURE_SCHED(&c3, GRPC_ERROR_NONE);
166 grpc_core::ExecCtx::Get()->Flush();
169 track_counters.Finish(state);
171 BENCHMARK(BM_ClosureSched3OnExecCtx);
173 static void BM_AcquireMutex(benchmark::State& state) {
174 TrackCounters track_counters;
175 // for comparison with the combiner stuff below
178 grpc_core::ExecCtx exec_ctx;
179 while (state.KeepRunning()) {
181 DoNothing(nullptr, GRPC_ERROR_NONE);
186 track_counters.Finish(state);
188 BENCHMARK(BM_AcquireMutex);
190 static void BM_TryAcquireMutex(benchmark::State& state) {
191 TrackCounters track_counters;
192 // for comparison with the combiner stuff below
195 grpc_core::ExecCtx exec_ctx;
196 while (state.KeepRunning()) {
197 if (gpr_mu_trylock(&mu)) {
198 DoNothing(nullptr, GRPC_ERROR_NONE);
206 track_counters.Finish(state);
208 BENCHMARK(BM_TryAcquireMutex);
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);
221 track_counters.Finish(state);
223 BENCHMARK(BM_AcquireSpinlock);
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);
239 track_counters.Finish(state);
241 BENCHMARK(BM_TryAcquireSpinlock);
243 static void BM_ClosureSchedOnCombiner(benchmark::State& state) {
244 TrackCounters track_counters;
245 grpc_combiner* combiner = grpc_combiner_create();
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();
253 GRPC_COMBINER_UNREF(combiner, "finished");
255 track_counters.Finish(state);
257 BENCHMARK(BM_ClosureSchedOnCombiner);
259 static void BM_ClosureSched2OnCombiner(benchmark::State& state) {
260 TrackCounters track_counters;
261 grpc_combiner* combiner = grpc_combiner_create();
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();
272 GRPC_COMBINER_UNREF(combiner, "finished");
274 track_counters.Finish(state);
276 BENCHMARK(BM_ClosureSched2OnCombiner);
278 static void BM_ClosureSched3OnCombiner(benchmark::State& state) {
279 TrackCounters track_counters;
280 grpc_combiner* combiner = grpc_combiner_create();
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();
294 GRPC_COMBINER_UNREF(combiner, "finished");
296 track_counters.Finish(state);
298 BENCHMARK(BM_ClosureSched3OnCombiner);
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();
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();
316 GRPC_COMBINER_UNREF(combiner1, "finished");
317 GRPC_COMBINER_UNREF(combiner2, "finished");
319 track_counters.Finish(state);
321 BENCHMARK(BM_ClosureSched2OnTwoCombiners);
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();
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();
347 GRPC_COMBINER_UNREF(combiner1, "finished");
348 GRPC_COMBINER_UNREF(combiner2, "finished");
350 track_counters.Finish(state);
352 BENCHMARK(BM_ClosureSched4OnTwoCombiners);
354 // Helper that continuously reschedules the same closure against something until
355 // the benchmark is complete
358 Rescheduler(benchmark::State& state, grpc_closure_scheduler* scheduler)
360 GRPC_CLOSURE_INIT(&closure_, Step, this, scheduler);
363 void ScheduleFirst() { GRPC_CLOSURE_SCHED(&closure_, GRPC_ERROR_NONE); }
365 void ScheduleFirstAgainstDifferentScheduler(
366 grpc_closure_scheduler* scheduler) {
367 GRPC_CLOSURE_SCHED(GRPC_CLOSURE_CREATE(Step, this, scheduler),
372 benchmark::State& state_;
373 grpc_closure closure_;
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);
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);
388 grpc_core::ExecCtx::Get()->Flush();
389 track_counters.Finish(state);
391 BENCHMARK(BM_ClosureReschedOnExecCtx);
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));
399 grpc_core::ExecCtx::Get()->Flush();
400 GRPC_COMBINER_UNREF(combiner, "finished");
402 track_counters.Finish(state);
404 BENCHMARK(BM_ClosureReschedOnCombiner);
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");
415 track_counters.Finish(state);
417 BENCHMARK(BM_ClosureReschedOnCombinerFinally);
419 // Some distros have RunSpecifiedBenchmarks under the benchmark namespace,
420 // and others do not. This allows us to support both modes.
421 namespace benchmark {
422 void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
423 } // namespace benchmark
425 int main(int argc, char** argv) {
426 LibraryInitializer libInit;
427 ::benchmark::Initialize(&argc, argv);
428 ::grpc::testing::InitTest(&argc, &argv, false);
429 benchmark::RunTheBenchmarksNamespaced();