• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 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 <benchmark/benchmark.h>
16 #include <grpc/grpc.h>
17 
18 #include "src/core/lib/event_engine/default_event_engine.h"
19 #include "src/core/lib/event_engine/event_engine_context.h"
20 #include "src/core/lib/promise/all_ok.h"
21 #include "src/core/lib/promise/map.h"
22 #include "src/core/lib/resource_quota/arena.h"
23 #include "src/core/lib/resource_quota/resource_quota.h"
24 #include "src/core/lib/slice/slice.h"
25 #include "src/core/lib/surface/client_call.h"
26 #include "src/core/lib/transport/call_arena_allocator.h"
27 
28 namespace grpc_core {
29 namespace {
30 
31 class TestCallDestination : public UnstartedCallDestination {
32  public:
StartCall(UnstartedCallHandler handler)33   void StartCall(UnstartedCallHandler handler) override {
34     handler_ = std::move(handler);
35   }
36 
TakeHandler()37   UnstartedCallHandler TakeHandler() {
38     CHECK(handler_.has_value());
39     auto handler = std::move(*handler_);
40     handler_.reset();
41     return handler;
42   }
43 
Orphaned()44   void Orphaned() override { handler_.reset(); }
45 
46  private:
47   absl::optional<UnstartedCallHandler> handler_;
48 };
49 
50 class Helper {
51  public:
~Helper()52   ~Helper() {
53     grpc_completion_queue_shutdown(cq_);
54     auto ev = grpc_completion_queue_next(
55         cq_, gpr_inf_future(GPR_CLOCK_REALTIME), nullptr);
56     CHECK_EQ(ev.type, GRPC_QUEUE_SHUTDOWN);
57     grpc_completion_queue_destroy(cq_);
58   }
59 
MakeCall()60   auto MakeCall() {
61     auto arena = arena_allocator_->MakeArena();
62     arena->SetContext<grpc_event_engine::experimental::EventEngine>(
63         event_engine_.get());
64     return std::unique_ptr<grpc_call, void (*)(grpc_call*)>(
65         MakeClientCall(nullptr, 0, cq_, path_.Copy(), absl::nullopt, true,
66                        Timestamp::InfFuture(), compression_options_,
67                        std::move(arena), destination_),
68         grpc_call_unref);
69   }
70 
TakeHandler()71   UnstartedCallHandler TakeHandler() { return destination_->TakeHandler(); }
72 
cq()73   grpc_completion_queue* cq() { return cq_; }
74 
75  private:
76   grpc_completion_queue* cq_ = grpc_completion_queue_create_for_next(nullptr);
77   Slice path_ = Slice::FromStaticString("/foo/bar");
78   const grpc_compression_options compression_options_ = {
79       1,
80       {0, GRPC_COMPRESS_LEVEL_NONE},
81       {0, GRPC_COMPRESS_NONE},
82   };
83   std::shared_ptr<grpc_event_engine::experimental::EventEngine> event_engine_ =
84       grpc_event_engine::experimental::GetDefaultEventEngine();
85   RefCountedPtr<CallArenaAllocator> arena_allocator_ =
86       MakeRefCounted<CallArenaAllocator>(
87           ResourceQuota::Default()->memory_quota()->CreateMemoryAllocator(
88               "test-allocator"),
89           1024);
90   RefCountedPtr<TestCallDestination> destination_ =
91       MakeRefCounted<TestCallDestination>();
92 };
93 
BM_CreateDestroy(benchmark::State & state)94 void BM_CreateDestroy(benchmark::State& state) {
95   Helper helper;
96   for (auto _ : state) {
97     helper.MakeCall();
98   }
99 }
100 BENCHMARK(BM_CreateDestroy);
101 
BM_Unary(benchmark::State & state)102 void BM_Unary(benchmark::State& state) {
103   Helper helper;
104   grpc_slice request_payload_slice = grpc_slice_from_static_string("hello");
105   grpc_byte_buffer* request_payload =
106       grpc_raw_byte_buffer_create(&request_payload_slice, 1);
107   grpc_status_code status;
108   grpc_slice status_details;
109   grpc_metadata_array initial_metadata_recv;
110   grpc_metadata_array trailing_metadata_recv;
111   Slice response_payload = Slice::FromStaticString("world");
112   grpc_byte_buffer* recv_response_payload = nullptr;
113   for (auto _ : state) {
114     auto call = helper.MakeCall();
115     // Create ops the old school way to avoid any overheads
116     grpc_op ops[6];
117     memset(ops, 0, sizeof(ops));
118     grpc_metadata_array_init(&initial_metadata_recv);
119     grpc_metadata_array_init(&trailing_metadata_recv);
120     ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
121     ops[0].data.send_initial_metadata.count = 0;
122     ops[1].op = GRPC_OP_SEND_MESSAGE;
123     ops[1].data.send_message.send_message = request_payload;
124     ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
125     ops[3].op = GRPC_OP_RECV_INITIAL_METADATA;
126     ops[3].data.recv_initial_metadata.recv_initial_metadata =
127         &initial_metadata_recv;
128     ops[4].op = GRPC_OP_RECV_MESSAGE;
129     ops[4].data.recv_message.recv_message = &recv_response_payload;
130     ops[5].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
131     ops[5].data.recv_status_on_client.status = &status;
132     ops[5].data.recv_status_on_client.status_details = &status_details;
133     ops[5].data.recv_status_on_client.trailing_metadata =
134         &trailing_metadata_recv;
135     grpc_call_start_batch(call.get(), ops, 6, reinterpret_cast<void*>(1),
136                           nullptr);
137     // Now fetch the handler at the other side, retrieve the request, and poke
138     // back a response.
139     auto unstarted_handler = helper.TakeHandler();
140     unstarted_handler.SpawnInfallible("run_handler", [&]() mutable {
141       auto handler = unstarted_handler.StartCall();
142       handler.PushServerInitialMetadata(
143           Arena::MakePooledForOverwrite<ServerMetadata>());
144       auto response =
145           Arena::MakePooled<Message>(SliceBuffer(response_payload.Copy()), 0);
146       return Map(
147           AllOk<StatusFlag>(
148               Map(handler.PullClientInitialMetadata(),
149                   [](ValueOrFailure<ClientMetadataHandle> status) {
150                     return status.status();
151                   }),
152               Map(handler.PullMessage(),
153                   [](ClientToServerNextMessage message) {
154                     return message.status();
155                   }),
156               handler.PushMessage(std::move(response))),
157           [handler](StatusFlag status) mutable {
158             CHECK(status.ok());
159             auto trailing_metadata =
160                 Arena::MakePooledForOverwrite<ServerMetadata>();
161             trailing_metadata->Set(GrpcStatusMetadata(), GRPC_STATUS_OK);
162             handler.PushServerTrailingMetadata(std::move(trailing_metadata));
163           });
164     });
165     auto ev = grpc_completion_queue_next(
166         helper.cq(), gpr_inf_future(GPR_CLOCK_REALTIME), nullptr);
167     CHECK_EQ(ev.type, GRPC_OP_COMPLETE);
168     call.reset();
169     grpc_byte_buffer_destroy(recv_response_payload);
170     grpc_metadata_array_destroy(&initial_metadata_recv);
171     grpc_metadata_array_destroy(&trailing_metadata_recv);
172   }
173   grpc_byte_buffer_destroy(request_payload);
174 }
175 BENCHMARK(BM_Unary);
176 
177 }  // namespace
178 }  // namespace grpc_core
179 
180 // Some distros have RunSpecifiedBenchmarks under the benchmark namespace,
181 // and others do not. This allows us to support both modes.
182 namespace benchmark {
RunTheBenchmarksNamespaced()183 void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
184 }  // namespace benchmark
185 
main(int argc,char ** argv)186 int main(int argc, char** argv) {
187   ::benchmark::Initialize(&argc, argv);
188   grpc_init();
189   {
190     auto ee = grpc_event_engine::experimental::GetDefaultEventEngine();
191     benchmark::RunTheBenchmarksNamespaced();
192   }
193   grpc_shutdown();
194   return 0;
195 }
196