1 // Copyright 2022 The gRPC Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <atomic>
16 #include <memory>
17
18 #include <benchmark/benchmark.h>
19
20 #include <grpcpp/impl/grpc_library.h>
21
22 #include "src/core/lib/gprpp/notification.h"
23 #include "src/core/lib/iomgr/exec_ctx.h"
24 #include "test/core/util/test_config.h"
25 #include "test/cpp/microbenchmarks/helpers.h"
26 #include "test/cpp/util/test_config.h"
27
28 namespace {
NoOpCb(void *,grpc_error_handle)29 void NoOpCb(void* /* arg */, grpc_error_handle /* error */) {}
30
BM_ExecCtx_Run(benchmark::State & state)31 void BM_ExecCtx_Run(benchmark::State& state) {
32 int cb_count = state.range(0);
33 grpc_closure cb;
34 GRPC_CLOSURE_INIT(&cb, NoOpCb, nullptr, nullptr);
35 grpc_core::ExecCtx exec_ctx;
36 for (auto _ : state) {
37 for (int i = 0; i < cb_count; i++) {
38 exec_ctx.Run(DEBUG_LOCATION, &cb, absl::OkStatus());
39 exec_ctx.Flush();
40 }
41 }
42 state.SetItemsProcessed(cb_count * state.iterations());
43 }
44 BENCHMARK(BM_ExecCtx_Run)
45 ->Range(100, 10000)
46 ->MeasureProcessCPUTime()
47 ->UseRealTime();
48
49 struct CountingCbData {
50 std::atomic_int cnt{0};
51 grpc_core::Notification* signal;
52 int limit;
53 };
54
CountingCb(void * arg,grpc_error_handle)55 void CountingCb(void* arg, grpc_error_handle) {
56 auto* data = static_cast<CountingCbData*>(arg);
57 if (++(data->cnt) == data->limit) data->signal->Notify();
58 }
59
BM_ExecCtx_RunCounted(benchmark::State & state)60 void BM_ExecCtx_RunCounted(benchmark::State& state) {
61 // A more fair comparison with EventEngine::Run, which must wait for all
62 // executions to finish
63 int cb_count = state.range(0);
64 CountingCbData data;
65 data.limit = cb_count;
66 data.signal = new grpc_core::Notification();
67 grpc_closure cb;
68 GRPC_CLOSURE_INIT(&cb, CountingCb, &data, nullptr);
69 grpc_core::ExecCtx exec_ctx;
70 for (auto _ : state) {
71 for (int i = 0; i < cb_count; i++) {
72 exec_ctx.Run(DEBUG_LOCATION, &cb, absl::OkStatus());
73 exec_ctx.Flush();
74 }
75 data.signal->WaitForNotification();
76 state.PauseTiming();
77 delete data.signal;
78 data.signal = new grpc_core::Notification();
79 data.cnt = 0;
80 state.ResumeTiming();
81 }
82 delete data.signal;
83 state.SetItemsProcessed(cb_count * state.iterations());
84 }
85 BENCHMARK(BM_ExecCtx_RunCounted)
86 ->Range(100, 10000)
87 ->MeasureProcessCPUTime()
88 ->UseRealTime();
89 } // namespace
90
91 // Some distros have RunSpecifiedBenchmarks under the benchmark namespace,
92 // and others do not. This allows us to support both modes.
93 namespace benchmark {
RunTheBenchmarksNamespaced()94 void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
95 } // namespace benchmark
96
main(int argc,char ** argv)97 int main(int argc, char** argv) {
98 grpc::testing::TestEnvironment env(&argc, argv);
99 LibraryInitializer libInit;
100 benchmark::Initialize(&argc, argv);
101 grpc::testing::InitTest(&argc, &argv, false);
102
103 benchmark::RunTheBenchmarksNamespaced();
104 return 0;
105 }
106