1 /*
2 *
3 * Copyright 2015 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 /* This benchmark exists to ensure that the benchmark integration is
20 * working */
21
22 #include <benchmark/benchmark.h>
23 #include <grpc/grpc.h>
24 #include <grpc/support/log.h>
25 #include <grpcpp/completion_queue.h>
26 #include <grpcpp/impl/grpc_library.h>
27
28 #include "src/core/lib/surface/completion_queue.h"
29 #include "test/core/util/test_config.h"
30 #include "test/cpp/microbenchmarks/helpers.h"
31 #include "test/cpp/util/test_config.h"
32
33 namespace grpc {
34 namespace testing {
35
BM_CreateDestroyCpp(benchmark::State & state)36 static void BM_CreateDestroyCpp(benchmark::State& state) {
37 TrackCounters track_counters;
38 for (auto _ : state) {
39 CompletionQueue cq;
40 }
41 track_counters.Finish(state);
42 }
43 BENCHMARK(BM_CreateDestroyCpp);
44
45 /* Create cq using a different constructor */
BM_CreateDestroyCpp2(benchmark::State & state)46 static void BM_CreateDestroyCpp2(benchmark::State& state) {
47 TrackCounters track_counters;
48 for (auto _ : state) {
49 grpc_completion_queue* core_cq =
50 grpc_completion_queue_create_for_next(nullptr);
51 CompletionQueue cq(core_cq);
52 }
53 track_counters.Finish(state);
54 }
55 BENCHMARK(BM_CreateDestroyCpp2);
56
BM_CreateDestroyCore(benchmark::State & state)57 static void BM_CreateDestroyCore(benchmark::State& state) {
58 TrackCounters track_counters;
59 for (auto _ : state) {
60 // TODO: sreek Templatize this benchmark and pass completion type and
61 // polling type as parameters
62 grpc_completion_queue_destroy(
63 grpc_completion_queue_create_for_next(nullptr));
64 }
65 track_counters.Finish(state);
66 }
67 BENCHMARK(BM_CreateDestroyCore);
68
DoneWithCompletionOnStack(void *,grpc_cq_completion *)69 static void DoneWithCompletionOnStack(void* /*arg*/,
70 grpc_cq_completion* /*completion*/) {}
71
DoneWithCompletionOnHeap(void *,grpc_cq_completion * completion)72 static void DoneWithCompletionOnHeap(void* /*arg*/,
73 grpc_cq_completion* completion) {
74 delete completion;
75 }
76
77 class DummyTag final : public internal::CompletionQueueTag {
78 public:
FinalizeResult(void **,bool *)79 bool FinalizeResult(void** /*tag*/, bool* /*status*/) override {
80 return true;
81 }
82 };
83
BM_Pass1Cpp(benchmark::State & state)84 static void BM_Pass1Cpp(benchmark::State& state) {
85 TrackCounters track_counters;
86 CompletionQueue cq;
87 grpc_completion_queue* c_cq = cq.cq();
88 for (auto _ : state) {
89 grpc_cq_completion completion;
90 DummyTag dummy_tag;
91 grpc_core::ExecCtx exec_ctx;
92 GPR_ASSERT(grpc_cq_begin_op(c_cq, &dummy_tag));
93 grpc_cq_end_op(c_cq, &dummy_tag, GRPC_ERROR_NONE, DoneWithCompletionOnStack,
94 nullptr, &completion);
95
96 void* tag;
97 bool ok;
98 cq.Next(&tag, &ok);
99 }
100 track_counters.Finish(state);
101 }
102 BENCHMARK(BM_Pass1Cpp);
103
BM_Pass1Core(benchmark::State & state)104 static void BM_Pass1Core(benchmark::State& state) {
105 TrackCounters track_counters;
106 // TODO: sreek Templatize this benchmark and pass polling_type as a param
107 grpc_completion_queue* cq = grpc_completion_queue_create_for_next(nullptr);
108 gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
109 for (auto _ : state) {
110 grpc_cq_completion completion;
111 grpc_core::ExecCtx exec_ctx;
112 GPR_ASSERT(grpc_cq_begin_op(cq, nullptr));
113 grpc_cq_end_op(cq, nullptr, GRPC_ERROR_NONE, DoneWithCompletionOnStack,
114 nullptr, &completion);
115
116 grpc_completion_queue_next(cq, deadline, nullptr);
117 }
118 grpc_completion_queue_destroy(cq);
119 track_counters.Finish(state);
120 }
121 BENCHMARK(BM_Pass1Core);
122
BM_Pluck1Core(benchmark::State & state)123 static void BM_Pluck1Core(benchmark::State& state) {
124 TrackCounters track_counters;
125 // TODO: sreek Templatize this benchmark and pass polling_type as a param
126 grpc_completion_queue* cq = grpc_completion_queue_create_for_pluck(nullptr);
127 gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
128 for (auto _ : state) {
129 grpc_cq_completion completion;
130 grpc_core::ExecCtx exec_ctx;
131 GPR_ASSERT(grpc_cq_begin_op(cq, nullptr));
132 grpc_cq_end_op(cq, nullptr, GRPC_ERROR_NONE, DoneWithCompletionOnStack,
133 nullptr, &completion);
134
135 grpc_completion_queue_pluck(cq, nullptr, deadline, nullptr);
136 }
137 grpc_completion_queue_destroy(cq);
138 track_counters.Finish(state);
139 }
140 BENCHMARK(BM_Pluck1Core);
141
BM_EmptyCore(benchmark::State & state)142 static void BM_EmptyCore(benchmark::State& state) {
143 TrackCounters track_counters;
144 // TODO: sreek Templatize this benchmark and pass polling_type as a param
145 grpc_completion_queue* cq = grpc_completion_queue_create_for_next(nullptr);
146 gpr_timespec deadline = gpr_inf_past(GPR_CLOCK_MONOTONIC);
147 for (auto _ : state) {
148 grpc_completion_queue_next(cq, deadline, nullptr);
149 }
150 grpc_completion_queue_destroy(cq);
151 track_counters.Finish(state);
152 }
153 BENCHMARK(BM_EmptyCore);
154
155 // Helper for tests to shutdown correctly and tersely
shutdown_and_destroy(grpc_completion_queue * cc)156 static void shutdown_and_destroy(grpc_completion_queue* cc) {
157 grpc_completion_queue_shutdown(cc);
158 grpc_completion_queue_destroy(cc);
159 }
160
161 static gpr_mu shutdown_mu, mu;
162 static gpr_cv shutdown_cv, cv;
163
164 // Tag completion queue iterate times
165 class TagCallback : public grpc_experimental_completion_queue_functor {
166 public:
TagCallback(int * iter)167 explicit TagCallback(int* iter) : iter_(iter) {
168 functor_run = &TagCallback::Run;
169 inlineable = false;
170 }
~TagCallback()171 ~TagCallback() {}
Run(grpc_experimental_completion_queue_functor * cb,int ok)172 static void Run(grpc_experimental_completion_queue_functor* cb, int ok) {
173 gpr_mu_lock(&mu);
174 GPR_ASSERT(static_cast<bool>(ok));
175 *static_cast<TagCallback*>(cb)->iter_ += 1;
176 gpr_cv_signal(&cv);
177 gpr_mu_unlock(&mu);
178 };
179
180 private:
181 int* iter_;
182 };
183
184 // Check if completion queue is shut down
185 class ShutdownCallback : public grpc_experimental_completion_queue_functor {
186 public:
ShutdownCallback(bool * done)187 explicit ShutdownCallback(bool* done) : done_(done) {
188 functor_run = &ShutdownCallback::Run;
189 inlineable = false;
190 }
~ShutdownCallback()191 ~ShutdownCallback() {}
Run(grpc_experimental_completion_queue_functor * cb,int ok)192 static void Run(grpc_experimental_completion_queue_functor* cb, int ok) {
193 gpr_mu_lock(&shutdown_mu);
194 *static_cast<ShutdownCallback*>(cb)->done_ = static_cast<bool>(ok);
195 gpr_cv_signal(&shutdown_cv);
196 gpr_mu_unlock(&shutdown_mu);
197 }
198
199 private:
200 bool* done_;
201 };
202
BM_Callback_CQ_Pass1Core(benchmark::State & state)203 static void BM_Callback_CQ_Pass1Core(benchmark::State& state) {
204 TrackCounters track_counters;
205 int iteration = 0, current_iterations = 0;
206 TagCallback tag_cb(&iteration);
207 gpr_mu_init(&mu);
208 gpr_cv_init(&cv);
209 gpr_mu_init(&shutdown_mu);
210 gpr_cv_init(&shutdown_cv);
211 bool got_shutdown = false;
212 ShutdownCallback shutdown_cb(&got_shutdown);
213 // This test with stack-allocated completions only works for non-polling or
214 // EM-polling callback core CQs. For generality, test with non-polling.
215 grpc_completion_queue_attributes attr;
216 attr.version = 2;
217 attr.cq_completion_type = GRPC_CQ_CALLBACK;
218 attr.cq_polling_type = GRPC_CQ_NON_POLLING;
219 attr.cq_shutdown_cb = &shutdown_cb;
220 grpc_completion_queue* cc = grpc_completion_queue_create(
221 grpc_completion_queue_factory_lookup(&attr), &attr, nullptr);
222 for (auto _ : state) {
223 grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
224 grpc_core::ExecCtx exec_ctx;
225 grpc_cq_completion completion;
226 GPR_ASSERT(grpc_cq_begin_op(cc, &tag_cb));
227 grpc_cq_end_op(cc, &tag_cb, GRPC_ERROR_NONE, DoneWithCompletionOnStack,
228 nullptr, &completion);
229 }
230 shutdown_and_destroy(cc);
231
232 gpr_mu_lock(&mu);
233 current_iterations = static_cast<int>(state.iterations());
234 while (current_iterations != iteration) {
235 // Wait for all the callbacks to complete.
236 gpr_cv_wait(&cv, &mu, gpr_inf_future(GPR_CLOCK_REALTIME));
237 }
238 gpr_mu_unlock(&mu);
239
240 gpr_mu_lock(&shutdown_mu);
241 while (!got_shutdown) {
242 // Wait for the shutdown callback to complete.
243 gpr_cv_wait(&shutdown_cv, &shutdown_mu, gpr_inf_future(GPR_CLOCK_REALTIME));
244 }
245 gpr_mu_unlock(&shutdown_mu);
246
247 GPR_ASSERT(got_shutdown);
248 GPR_ASSERT(iteration == static_cast<int>(state.iterations()));
249 track_counters.Finish(state);
250 gpr_cv_destroy(&cv);
251 gpr_mu_destroy(&mu);
252 gpr_cv_destroy(&shutdown_cv);
253 gpr_mu_destroy(&shutdown_mu);
254 }
BM_Callback_CQ_Pass1CoreHeapCompletion(benchmark::State & state)255 static void BM_Callback_CQ_Pass1CoreHeapCompletion(benchmark::State& state) {
256 TrackCounters track_counters;
257 int iteration = 0, current_iterations = 0;
258 TagCallback tag_cb(&iteration);
259 gpr_mu_init(&mu);
260 gpr_cv_init(&cv);
261 gpr_mu_init(&shutdown_mu);
262 gpr_cv_init(&shutdown_cv);
263 bool got_shutdown = false;
264 ShutdownCallback shutdown_cb(&got_shutdown);
265 grpc_completion_queue* cc =
266 grpc_completion_queue_create_for_callback(&shutdown_cb, nullptr);
267 for (auto _ : state) {
268 grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
269 grpc_core::ExecCtx exec_ctx;
270 grpc_cq_completion* completion = new grpc_cq_completion;
271 GPR_ASSERT(grpc_cq_begin_op(cc, &tag_cb));
272 grpc_cq_end_op(cc, &tag_cb, GRPC_ERROR_NONE, DoneWithCompletionOnHeap,
273 nullptr, completion);
274 }
275 shutdown_and_destroy(cc);
276
277 gpr_mu_lock(&mu);
278 current_iterations = static_cast<int>(state.iterations());
279 while (current_iterations != iteration) {
280 // Wait for all the callbacks to complete.
281 gpr_cv_wait(&cv, &mu, gpr_inf_future(GPR_CLOCK_REALTIME));
282 }
283 gpr_mu_unlock(&mu);
284
285 gpr_mu_lock(&shutdown_mu);
286 while (!got_shutdown) {
287 // Wait for the shutdown callback to complete.
288 gpr_cv_wait(&shutdown_cv, &shutdown_mu, gpr_inf_future(GPR_CLOCK_REALTIME));
289 }
290 gpr_mu_unlock(&shutdown_mu);
291
292 GPR_ASSERT(got_shutdown);
293 GPR_ASSERT(iteration == static_cast<int>(state.iterations()));
294 track_counters.Finish(state);
295 gpr_cv_destroy(&cv);
296 gpr_mu_destroy(&mu);
297 gpr_cv_destroy(&shutdown_cv);
298 gpr_mu_destroy(&shutdown_mu);
299 }
300 BENCHMARK(BM_Callback_CQ_Pass1Core);
301 BENCHMARK(BM_Callback_CQ_Pass1CoreHeapCompletion);
302
303 } // namespace testing
304 } // namespace grpc
305
306 // Some distros have RunSpecifiedBenchmarks under the benchmark namespace,
307 // and others do not. This allows us to support both modes.
308 namespace benchmark {
RunTheBenchmarksNamespaced()309 void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
310 } // namespace benchmark
311
main(int argc,char ** argv)312 int main(int argc, char** argv) {
313 grpc::testing::TestEnvironment env(argc, argv);
314 LibraryInitializer libInit;
315 ::benchmark::Initialize(&argc, argv);
316 ::grpc::testing::InitTest(&argc, &argv, false);
317 benchmark::RunTheBenchmarksNamespaced();
318 return 0;
319 }
320