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