• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  * Copyright 2016 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 #include <grpc/grpc.h>
20 
21 #include <stdio.h>
22 #include <string.h>
23 
24 #include <grpc/byte_buffer.h>
25 #include <grpc/byte_buffer_reader.h>
26 #include <grpc/support/alloc.h>
27 #include <grpc/support/log.h>
28 #include <grpc/support/time.h>
29 #include "src/core/lib/gpr/env.h"
30 #include "src/core/lib/gpr/string.h"
31 #include "src/core/lib/gpr/useful.h"
32 
33 #include "test/core/util/cmdline.h"
34 #include "test/core/util/memory_counters.h"
35 #include "test/core/util/test_config.h"
36 
37 static grpc_channel* channel;
38 static grpc_completion_queue* cq;
39 static grpc_op metadata_ops[2];
40 static grpc_op status_ops[2];
41 static grpc_op snapshot_ops[6];
42 static grpc_op* op;
43 
44 typedef struct {
45   grpc_call* call;
46   grpc_metadata_array initial_metadata_recv;
47   grpc_status_code status;
48   grpc_slice details;
49   grpc_metadata_array trailing_metadata_recv;
50 } fling_call;
51 
52 // Statically allocate call data structs. Enough to accomodate 10000 ping-pong
53 // calls and 1 extra for the snapshot calls.
54 static fling_call calls[10001];
55 
tag(intptr_t t)56 static void* tag(intptr_t t) { return (void*)t; }
57 
58 // A call is intentionally divided into two steps. First step is to initiate a
59 // call (i.e send and recv metadata). A call is outstanding after we initated,
60 // so we can measure the call memory usage.
init_ping_pong_request(int call_idx)61 static void init_ping_pong_request(int call_idx) {
62   grpc_metadata_array_init(&calls[call_idx].initial_metadata_recv);
63 
64   memset(metadata_ops, 0, sizeof(metadata_ops));
65   op = metadata_ops;
66   op->op = GRPC_OP_SEND_INITIAL_METADATA;
67   op->data.send_initial_metadata.count = 0;
68   op->flags = GRPC_INITIAL_METADATA_WAIT_FOR_READY;
69   op++;
70   op->op = GRPC_OP_RECV_INITIAL_METADATA;
71   op->data.recv_initial_metadata.recv_initial_metadata =
72       &calls[call_idx].initial_metadata_recv;
73   op++;
74 
75   grpc_slice hostname = grpc_slice_from_static_string("localhost");
76   calls[call_idx].call = grpc_channel_create_call(
77       channel, nullptr, GRPC_PROPAGATE_DEFAULTS, cq,
78       grpc_slice_from_static_string("/Reflector/reflectUnary"), &hostname,
79       gpr_inf_future(GPR_CLOCK_REALTIME), nullptr);
80 
81   GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(calls[call_idx].call,
82                                                    metadata_ops,
83                                                    (size_t)(op - metadata_ops),
84                                                    tag(call_idx), nullptr));
85   grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), nullptr);
86 }
87 
88 // Second step is to finish the call (i.e recv status) and destroy the call.
finish_ping_pong_request(int call_idx)89 static void finish_ping_pong_request(int call_idx) {
90   grpc_metadata_array_init(&calls[call_idx].trailing_metadata_recv);
91 
92   memset(status_ops, 0, sizeof(status_ops));
93   op = status_ops;
94   op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
95   op->data.recv_status_on_client.trailing_metadata =
96       &calls[call_idx].trailing_metadata_recv;
97   op->data.recv_status_on_client.status = &calls[call_idx].status;
98   op->data.recv_status_on_client.status_details = &calls[call_idx].details;
99   op++;
100 
101   GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(calls[call_idx].call,
102                                                    status_ops,
103                                                    (size_t)(op - status_ops),
104                                                    tag(call_idx), nullptr));
105   grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), nullptr);
106   grpc_metadata_array_destroy(&calls[call_idx].initial_metadata_recv);
107   grpc_metadata_array_destroy(&calls[call_idx].trailing_metadata_recv);
108   grpc_slice_unref(calls[call_idx].details);
109   grpc_call_unref(calls[call_idx].call);
110   calls[call_idx].call = nullptr;
111 }
112 
send_snapshot_request(int call_idx,grpc_slice call_type)113 static struct grpc_memory_counters send_snapshot_request(int call_idx,
114                                                          grpc_slice call_type) {
115   grpc_metadata_array_init(&calls[call_idx].initial_metadata_recv);
116   grpc_metadata_array_init(&calls[call_idx].trailing_metadata_recv);
117 
118   grpc_byte_buffer* response_payload_recv = nullptr;
119   memset(snapshot_ops, 0, sizeof(snapshot_ops));
120   op = snapshot_ops;
121 
122   op->op = GRPC_OP_SEND_INITIAL_METADATA;
123   op->data.send_initial_metadata.count = 0;
124   op->flags = GRPC_INITIAL_METADATA_WAIT_FOR_READY;
125   op++;
126   op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
127   op++;
128   op->op = GRPC_OP_RECV_INITIAL_METADATA;
129   op->data.recv_initial_metadata.recv_initial_metadata =
130       &calls[call_idx].initial_metadata_recv;
131   op++;
132   op->op = GRPC_OP_RECV_MESSAGE;
133   op->data.recv_message.recv_message = &response_payload_recv;
134   op++;
135   op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
136   op->data.recv_status_on_client.trailing_metadata =
137       &calls[call_idx].trailing_metadata_recv;
138   op->data.recv_status_on_client.status = &calls[call_idx].status;
139   op->data.recv_status_on_client.status_details = &calls[call_idx].details;
140   op++;
141 
142   grpc_slice hostname = grpc_slice_from_static_string("localhost");
143   calls[call_idx].call = grpc_channel_create_call(
144       channel, nullptr, GRPC_PROPAGATE_DEFAULTS, cq, call_type, &hostname,
145       gpr_inf_future(GPR_CLOCK_REALTIME), nullptr);
146   GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(calls[call_idx].call,
147                                                    snapshot_ops,
148                                                    (size_t)(op - snapshot_ops),
149                                                    (void*)nullptr, nullptr));
150   grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), nullptr);
151 
152   grpc_byte_buffer_reader reader;
153   grpc_byte_buffer_reader_init(&reader, response_payload_recv);
154   grpc_slice response = grpc_byte_buffer_reader_readall(&reader);
155 
156   struct grpc_memory_counters snapshot;
157   snapshot.total_size_absolute =
158       (reinterpret_cast<struct grpc_memory_counters*> GRPC_SLICE_START_PTR(
159            response))
160           ->total_size_absolute;
161   snapshot.total_allocs_absolute =
162       (reinterpret_cast<struct grpc_memory_counters*> GRPC_SLICE_START_PTR(
163            response))
164           ->total_allocs_absolute;
165   snapshot.total_size_relative =
166       (reinterpret_cast<struct grpc_memory_counters*> GRPC_SLICE_START_PTR(
167            response))
168           ->total_size_relative;
169   snapshot.total_allocs_relative =
170       (reinterpret_cast<struct grpc_memory_counters*> GRPC_SLICE_START_PTR(
171            response))
172           ->total_allocs_relative;
173 
174   grpc_metadata_array_destroy(&calls[call_idx].initial_metadata_recv);
175   grpc_metadata_array_destroy(&calls[call_idx].trailing_metadata_recv);
176   grpc_slice_unref(response);
177   grpc_byte_buffer_reader_destroy(&reader);
178   grpc_byte_buffer_destroy(response_payload_recv);
179   grpc_slice_unref(calls[call_idx].details);
180   calls[call_idx].details = grpc_empty_slice();
181   grpc_call_unref(calls[call_idx].call);
182   calls[call_idx].call = nullptr;
183 
184   return snapshot;
185 }
186 
main(int argc,char ** argv)187 int main(int argc, char** argv) {
188   grpc_memory_counters_init();
189   grpc_slice slice = grpc_slice_from_copied_string("x");
190   char* fake_argv[1];
191 
192   const char* target = "localhost:443";
193   gpr_cmdline* cl;
194   grpc_event event;
195 
196   grpc_init();
197 
198   GPR_ASSERT(argc >= 1);
199   fake_argv[0] = argv[0];
200   grpc_test_init(1, fake_argv);
201 
202   int warmup_iterations = 100;
203   int benchmark_iterations = 1000;
204 
205   cl = gpr_cmdline_create("memory profiling client");
206   gpr_cmdline_add_string(cl, "target", "Target host:port", &target);
207   gpr_cmdline_add_int(cl, "warmup", "Warmup iterations", &warmup_iterations);
208   gpr_cmdline_add_int(cl, "benchmark", "Benchmark iterations",
209                       &benchmark_iterations);
210   gpr_cmdline_parse(cl, argc, argv);
211   gpr_cmdline_destroy(cl);
212 
213   for (size_t k = 0; k < GPR_ARRAY_SIZE(calls); k++) {
214     calls[k].details = grpc_empty_slice();
215   }
216 
217   cq = grpc_completion_queue_create_for_next(nullptr);
218 
219   struct grpc_memory_counters client_channel_start =
220       grpc_memory_counters_snapshot();
221   channel = grpc_insecure_channel_create(target, nullptr, nullptr);
222 
223   int call_idx = 0;
224 
225   struct grpc_memory_counters before_server_create = send_snapshot_request(
226       0, grpc_slice_from_static_string("Reflector/GetBeforeSvrCreation"));
227   struct grpc_memory_counters after_server_create = send_snapshot_request(
228       0, grpc_slice_from_static_string("Reflector/GetAfterSvrCreation"));
229 
230   // warmup period
231   for (int i = 0; i < warmup_iterations; i++) {
232     send_snapshot_request(
233         0, grpc_slice_from_static_string("Reflector/SimpleSnapshot"));
234   }
235 
236   for (call_idx = 0; call_idx < warmup_iterations; ++call_idx) {
237     init_ping_pong_request(call_idx + 1);
238   }
239 
240   struct grpc_memory_counters server_benchmark_calls_start =
241       send_snapshot_request(
242           0, grpc_slice_from_static_string("Reflector/SimpleSnapshot"));
243 
244   struct grpc_memory_counters client_benchmark_calls_start =
245       grpc_memory_counters_snapshot();
246 
247   // benchmark period
248   for (; call_idx < warmup_iterations + benchmark_iterations; ++call_idx) {
249     init_ping_pong_request(call_idx + 1);
250   }
251 
252   struct grpc_memory_counters client_calls_inflight =
253       grpc_memory_counters_snapshot();
254 
255   struct grpc_memory_counters server_calls_inflight = send_snapshot_request(
256       0, grpc_slice_from_static_string("Reflector/DestroyCalls"));
257 
258   do {
259     event = grpc_completion_queue_next(
260         cq,
261         gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
262                      gpr_time_from_micros(10000, GPR_TIMESPAN)),
263         nullptr);
264   } while (event.type != GRPC_QUEUE_TIMEOUT);
265 
266   // second step - recv status and destroy call
267   for (call_idx = 0; call_idx < warmup_iterations + benchmark_iterations;
268        ++call_idx) {
269     finish_ping_pong_request(call_idx + 1);
270   }
271 
272   struct grpc_memory_counters server_calls_end = send_snapshot_request(
273       0, grpc_slice_from_static_string("Reflector/SimpleSnapshot"));
274 
275   struct grpc_memory_counters client_channel_end =
276       grpc_memory_counters_snapshot();
277 
278   grpc_channel_destroy(channel);
279   grpc_completion_queue_shutdown(cq);
280 
281   do {
282     event = grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME),
283                                        nullptr);
284   } while (event.type != GRPC_QUEUE_SHUTDOWN);
285   grpc_slice_unref(slice);
286 
287   grpc_completion_queue_destroy(cq);
288   grpc_shutdown();
289 
290   gpr_log(GPR_INFO, "---------client stats--------");
291   gpr_log(
292       GPR_INFO, "client call memory usage: %f bytes per call",
293       static_cast<double>(client_calls_inflight.total_size_relative -
294                           client_benchmark_calls_start.total_size_relative) /
295           benchmark_iterations);
296   gpr_log(GPR_INFO, "client channel memory usage %zi bytes",
297           client_channel_end.total_size_relative -
298               client_channel_start.total_size_relative);
299 
300   gpr_log(GPR_INFO, "---------server stats--------");
301   gpr_log(GPR_INFO, "server create: %zi bytes",
302           after_server_create.total_size_relative -
303               before_server_create.total_size_relative);
304   gpr_log(
305       GPR_INFO, "server call memory usage: %f bytes per call",
306       static_cast<double>(server_calls_inflight.total_size_relative -
307                           server_benchmark_calls_start.total_size_relative) /
308           benchmark_iterations);
309   gpr_log(GPR_INFO, "server channel memory usage %zi bytes",
310           server_calls_end.total_size_relative -
311               after_server_create.total_size_relative);
312 
313   const char* csv_file = "memory_usage.csv";
314   FILE* csv = fopen(csv_file, "w");
315   if (csv) {
316     char* env_build = gpr_getenv("BUILD_NUMBER");
317     char* env_job = gpr_getenv("JOB_NAME");
318     fprintf(
319         csv, "%f,%zi,%zi,%f,%zi,%s,%s\n",
320         static_cast<double>(client_calls_inflight.total_size_relative -
321                             client_benchmark_calls_start.total_size_relative) /
322             benchmark_iterations,
323         client_channel_end.total_size_relative -
324             client_channel_start.total_size_relative,
325         after_server_create.total_size_relative -
326             before_server_create.total_size_relative,
327         static_cast<double>(server_calls_inflight.total_size_relative -
328                             server_benchmark_calls_start.total_size_relative) /
329             benchmark_iterations,
330         server_calls_end.total_size_relative -
331             after_server_create.total_size_relative,
332         env_build == nullptr ? "" : env_build,
333         env_job == nullptr ? "" : env_job);
334     fclose(csv);
335     gpr_log(GPR_INFO, "Summary written to %s", csv_file);
336   }
337 
338   grpc_memory_counters_destroy();
339   return 0;
340 }
341