• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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