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/core/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 TrackCounters track_counters;
36 for (auto _ : state) {
37 grpc_core::ExecCtx exec_ctx;
38 }
39 track_counters.Finish(state);
40 }
41 BENCHMARK(BM_NoOpExecCtx);
42
BM_WellFlushed(benchmark::State & state)43 static void BM_WellFlushed(benchmark::State& state) {
44 TrackCounters track_counters;
45 grpc_core::ExecCtx exec_ctx;
46 for (auto _ : state) {
47 grpc_core::ExecCtx::Get()->Flush();
48 }
49
50 track_counters.Finish(state);
51 }
52 BENCHMARK(BM_WellFlushed);
53
DoNothing(void *,grpc_error *)54 static void DoNothing(void* /*arg*/, grpc_error* /*error*/) {}
55
BM_ClosureInitAgainstExecCtx(benchmark::State & state)56 static void BM_ClosureInitAgainstExecCtx(benchmark::State& state) {
57 TrackCounters track_counters;
58 grpc_closure c;
59 for (auto _ : state) {
60 benchmark::DoNotOptimize(
61 GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx));
62 }
63 track_counters.Finish(state);
64 }
65 BENCHMARK(BM_ClosureInitAgainstExecCtx);
66
BM_ClosureInitAgainstCombiner(benchmark::State & state)67 static void BM_ClosureInitAgainstCombiner(benchmark::State& state) {
68 TrackCounters track_counters;
69 grpc_core::Combiner* combiner = grpc_combiner_create();
70 grpc_closure c;
71 grpc_core::ExecCtx exec_ctx;
72 for (auto _ : state) {
73 benchmark::DoNotOptimize(
74 GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, nullptr));
75 }
76 GRPC_COMBINER_UNREF(combiner, "finished");
77
78 track_counters.Finish(state);
79 }
80 BENCHMARK(BM_ClosureInitAgainstCombiner);
81
BM_ClosureRun(benchmark::State & state)82 static void BM_ClosureRun(benchmark::State& state) {
83 TrackCounters track_counters;
84 grpc_closure c;
85 GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, grpc_schedule_on_exec_ctx);
86 grpc_core::ExecCtx exec_ctx;
87 for (auto _ : state) {
88 grpc_core::Closure::Run(DEBUG_LOCATION, &c, GRPC_ERROR_NONE);
89 }
90
91 track_counters.Finish(state);
92 }
93 BENCHMARK(BM_ClosureRun);
94
BM_ClosureCreateAndRun(benchmark::State & state)95 static void BM_ClosureCreateAndRun(benchmark::State& state) {
96 TrackCounters track_counters;
97 grpc_core::ExecCtx exec_ctx;
98 for (auto _ : state) {
99 grpc_core::Closure::Run(
100 DEBUG_LOCATION,
101 GRPC_CLOSURE_CREATE(DoNothing, nullptr, grpc_schedule_on_exec_ctx),
102 GRPC_ERROR_NONE);
103 }
104
105 track_counters.Finish(state);
106 }
107 BENCHMARK(BM_ClosureCreateAndRun);
108
BM_ClosureInitAndRun(benchmark::State & state)109 static void BM_ClosureInitAndRun(benchmark::State& state) {
110 TrackCounters track_counters;
111 grpc_core::ExecCtx exec_ctx;
112 grpc_closure c;
113 for (auto _ : state) {
114 grpc_core::Closure::Run(
115 DEBUG_LOCATION,
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 for (auto _ : state) {
130 grpc_core::ExecCtx::Run(DEBUG_LOCATION, &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 for (auto _ : state) {
146 grpc_core::ExecCtx::Run(DEBUG_LOCATION, &c1, GRPC_ERROR_NONE);
147 grpc_core::ExecCtx::Run(DEBUG_LOCATION, &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 for (auto _ : state) {
165 grpc_core::ExecCtx::Run(DEBUG_LOCATION, &c1, GRPC_ERROR_NONE);
166 grpc_core::ExecCtx::Run(DEBUG_LOCATION, &c2, GRPC_ERROR_NONE);
167 grpc_core::ExecCtx::Run(DEBUG_LOCATION, &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 for (auto _ : state) {
182 gpr_mu_lock(&mu);
183 DoNothing(nullptr, GRPC_ERROR_NONE);
184 gpr_mu_unlock(&mu);
185 }
186 gpr_mu_destroy(&mu);
187
188 track_counters.Finish(state);
189 }
190 BENCHMARK(BM_AcquireMutex);
191
BM_TryAcquireMutex(benchmark::State & state)192 static void BM_TryAcquireMutex(benchmark::State& state) {
193 TrackCounters track_counters;
194 // for comparison with the combiner stuff below
195 gpr_mu mu;
196 gpr_mu_init(&mu);
197 grpc_core::ExecCtx exec_ctx;
198 for (auto _ : state) {
199 if (gpr_mu_trylock(&mu)) {
200 DoNothing(nullptr, GRPC_ERROR_NONE);
201 gpr_mu_unlock(&mu);
202 } else {
203 abort();
204 }
205 }
206 gpr_mu_destroy(&mu);
207
208 track_counters.Finish(state);
209 }
210 BENCHMARK(BM_TryAcquireMutex);
211
BM_AcquireSpinlock(benchmark::State & state)212 static void BM_AcquireSpinlock(benchmark::State& state) {
213 TrackCounters track_counters;
214 // for comparison with the combiner stuff below
215 gpr_spinlock mu = GPR_SPINLOCK_INITIALIZER;
216 grpc_core::ExecCtx exec_ctx;
217 for (auto _ : state) {
218 gpr_spinlock_lock(&mu);
219 DoNothing(nullptr, GRPC_ERROR_NONE);
220 gpr_spinlock_unlock(&mu);
221 }
222
223 track_counters.Finish(state);
224 }
225 BENCHMARK(BM_AcquireSpinlock);
226
BM_TryAcquireSpinlock(benchmark::State & state)227 static void BM_TryAcquireSpinlock(benchmark::State& state) {
228 TrackCounters track_counters;
229 // for comparison with the combiner stuff below
230 gpr_spinlock mu = GPR_SPINLOCK_INITIALIZER;
231 grpc_core::ExecCtx exec_ctx;
232 for (auto _ : state) {
233 if (gpr_spinlock_trylock(&mu)) {
234 DoNothing(nullptr, GRPC_ERROR_NONE);
235 gpr_spinlock_unlock(&mu);
236 } else {
237 abort();
238 }
239 }
240
241 track_counters.Finish(state);
242 }
243 BENCHMARK(BM_TryAcquireSpinlock);
244
BM_ClosureSchedOnCombiner(benchmark::State & state)245 static void BM_ClosureSchedOnCombiner(benchmark::State& state) {
246 TrackCounters track_counters;
247 grpc_core::Combiner* combiner = grpc_combiner_create();
248 grpc_closure c;
249 GRPC_CLOSURE_INIT(&c, DoNothing, nullptr, nullptr);
250 grpc_core::ExecCtx exec_ctx;
251 for (auto _ : state) {
252 combiner->Run(&c, GRPC_ERROR_NONE);
253 grpc_core::ExecCtx::Get()->Flush();
254 }
255 GRPC_COMBINER_UNREF(combiner, "finished");
256
257 track_counters.Finish(state);
258 }
259 BENCHMARK(BM_ClosureSchedOnCombiner);
260
BM_ClosureSched2OnCombiner(benchmark::State & state)261 static void BM_ClosureSched2OnCombiner(benchmark::State& state) {
262 TrackCounters track_counters;
263 grpc_core::Combiner* combiner = grpc_combiner_create();
264 grpc_closure c1;
265 grpc_closure c2;
266 GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, nullptr);
267 GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, nullptr);
268 grpc_core::ExecCtx exec_ctx;
269 for (auto _ : state) {
270 combiner->Run(&c1, GRPC_ERROR_NONE);
271 combiner->Run(&c2, GRPC_ERROR_NONE);
272 grpc_core::ExecCtx::Get()->Flush();
273 }
274 GRPC_COMBINER_UNREF(combiner, "finished");
275
276 track_counters.Finish(state);
277 }
278 BENCHMARK(BM_ClosureSched2OnCombiner);
279
BM_ClosureSched3OnCombiner(benchmark::State & state)280 static void BM_ClosureSched3OnCombiner(benchmark::State& state) {
281 TrackCounters track_counters;
282 grpc_core::Combiner* combiner = grpc_combiner_create();
283 grpc_closure c1;
284 grpc_closure c2;
285 grpc_closure c3;
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_core::ExecCtx exec_ctx;
290 for (auto _ : state) {
291 combiner->Run(&c1, GRPC_ERROR_NONE);
292 combiner->Run(&c2, GRPC_ERROR_NONE);
293 combiner->Run(&c3, GRPC_ERROR_NONE);
294 grpc_core::ExecCtx::Get()->Flush();
295 }
296 GRPC_COMBINER_UNREF(combiner, "finished");
297
298 track_counters.Finish(state);
299 }
300 BENCHMARK(BM_ClosureSched3OnCombiner);
301
BM_ClosureSched2OnTwoCombiners(benchmark::State & state)302 static void BM_ClosureSched2OnTwoCombiners(benchmark::State& state) {
303 TrackCounters track_counters;
304 grpc_core::Combiner* combiner1 = grpc_combiner_create();
305 grpc_core::Combiner* combiner2 = grpc_combiner_create();
306 grpc_closure c1;
307 grpc_closure c2;
308 GRPC_CLOSURE_INIT(&c1, DoNothing, nullptr, nullptr);
309 GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, nullptr);
310 grpc_core::ExecCtx exec_ctx;
311 for (auto _ : state) {
312 combiner1->Run(&c1, GRPC_ERROR_NONE);
313 combiner2->Run(&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_core::Combiner* combiner1 = grpc_combiner_create();
326 grpc_core::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, nullptr);
332 GRPC_CLOSURE_INIT(&c2, DoNothing, nullptr, nullptr);
333 GRPC_CLOSURE_INIT(&c3, DoNothing, nullptr, nullptr);
334 GRPC_CLOSURE_INIT(&c4, DoNothing, nullptr, nullptr);
335 grpc_core::ExecCtx exec_ctx;
336 for (auto _ : state) {
337 combiner1->Run(&c1, GRPC_ERROR_NONE);
338 combiner2->Run(&c2, GRPC_ERROR_NONE);
339 combiner1->Run(&c3, GRPC_ERROR_NONE);
340 combiner2->Run(&c4, GRPC_ERROR_NONE);
341 grpc_core::ExecCtx::Get()->Flush();
342 }
343 GRPC_COMBINER_UNREF(combiner1, "finished");
344 GRPC_COMBINER_UNREF(combiner2, "finished");
345
346 track_counters.Finish(state);
347 }
348 BENCHMARK(BM_ClosureSched4OnTwoCombiners);
349
350 // Helper that continuously reschedules the same closure against something until
351 // the benchmark is complete
352 class Rescheduler {
353 public:
Rescheduler(benchmark::State & state)354 explicit Rescheduler(benchmark::State& state) : state_(state) {
355 GRPC_CLOSURE_INIT(&closure_, Step, this, nullptr);
356 }
357
ScheduleFirst()358 void ScheduleFirst() {
359 grpc_core::ExecCtx::Run(DEBUG_LOCATION, &closure_, GRPC_ERROR_NONE);
360 }
361
ScheduleFirstAgainstDifferentScheduler()362 void ScheduleFirstAgainstDifferentScheduler() {
363 grpc_core::ExecCtx::Run(DEBUG_LOCATION,
364 GRPC_CLOSURE_CREATE(Step, this, nullptr),
365 GRPC_ERROR_NONE);
366 }
367
368 private:
369 benchmark::State& state_;
370 grpc_closure closure_;
371
Step(void * arg,grpc_error *)372 static void Step(void* arg, grpc_error* /*error*/) {
373 Rescheduler* self = static_cast<Rescheduler*>(arg);
374 if (self->state_.KeepRunning()) {
375 grpc_core::ExecCtx::Run(DEBUG_LOCATION, &self->closure_, GRPC_ERROR_NONE);
376 }
377 }
378 };
379
BM_ClosureReschedOnExecCtx(benchmark::State & state)380 static void BM_ClosureReschedOnExecCtx(benchmark::State& state) {
381 TrackCounters track_counters;
382 grpc_core::ExecCtx exec_ctx;
383 Rescheduler r(state);
384 r.ScheduleFirst();
385 grpc_core::ExecCtx::Get()->Flush();
386 track_counters.Finish(state);
387 }
388 BENCHMARK(BM_ClosureReschedOnExecCtx);
389
390 // Some distros have RunSpecifiedBenchmarks under the benchmark namespace,
391 // and others do not. This allows us to support both modes.
392 namespace benchmark {
RunTheBenchmarksNamespaced()393 void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
394 } // namespace benchmark
395
main(int argc,char ** argv)396 int main(int argc, char** argv) {
397 grpc::testing::TestEnvironment env(argc, argv);
398 LibraryInitializer libInit;
399 ::benchmark::Initialize(&argc, argv);
400 ::grpc::testing::InitTest(&argc, &argv, false);
401 benchmark::RunTheBenchmarksNamespaced();
402 return 0;
403 }
404