client.cc 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  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. #include <stdio.h>
  19. #include <string.h>
  20. #include "absl/flags/flag.h"
  21. #include "absl/flags/parse.h"
  22. #include <grpc/byte_buffer.h>
  23. #include <grpc/byte_buffer_reader.h>
  24. #include <grpc/grpc.h>
  25. #include <grpc/grpc_security.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. #include "test/core/memory_usage/memstats.h"
  33. #include "test/core/util/test_config.h"
  34. static grpc_channel* channel;
  35. static grpc_completion_queue* cq;
  36. static grpc_op metadata_ops[2];
  37. static grpc_op status_ops[2];
  38. static grpc_op snapshot_ops[6];
  39. static grpc_op* op;
  40. typedef struct {
  41. grpc_call* call;
  42. grpc_metadata_array initial_metadata_recv;
  43. grpc_status_code status;
  44. grpc_slice details;
  45. grpc_metadata_array trailing_metadata_recv;
  46. } fling_call;
  47. // Statically allocate call data structs. Enough to accommodate 100000 ping-pong
  48. // calls and 1 extra for the snapshot calls.
  49. static fling_call calls[100001];
  50. static void* tag(intptr_t t) { return reinterpret_cast<void*>(t); }
  51. // A call is intentionally divided into two steps. First step is to initiate a
  52. // call (i.e send and recv metadata). A call is outstanding after we initated,
  53. // so we can measure the call memory usage.
  54. static void init_ping_pong_request(int call_idx) {
  55. grpc_metadata_array_init(&calls[call_idx].initial_metadata_recv);
  56. memset(metadata_ops, 0, sizeof(metadata_ops));
  57. op = metadata_ops;
  58. op->op = GRPC_OP_SEND_INITIAL_METADATA;
  59. op->data.send_initial_metadata.count = 0;
  60. op->flags = GRPC_INITIAL_METADATA_WAIT_FOR_READY;
  61. op++;
  62. op->op = GRPC_OP_RECV_INITIAL_METADATA;
  63. op->data.recv_initial_metadata.recv_initial_metadata =
  64. &calls[call_idx].initial_metadata_recv;
  65. op++;
  66. grpc_slice hostname = grpc_slice_from_static_string("localhost");
  67. calls[call_idx].call = grpc_channel_create_call(
  68. channel, nullptr, GRPC_PROPAGATE_DEFAULTS, cq,
  69. grpc_slice_from_static_string("/Reflector/reflectUnary"), &hostname,
  70. gpr_inf_future(GPR_CLOCK_REALTIME), nullptr);
  71. GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(calls[call_idx].call,
  72. metadata_ops,
  73. (size_t)(op - metadata_ops),
  74. tag(call_idx), nullptr));
  75. grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), nullptr);
  76. }
  77. // Second step is to finish the call (i.e recv status) and destroy the call.
  78. static void finish_ping_pong_request(int call_idx) {
  79. grpc_metadata_array_init(&calls[call_idx].trailing_metadata_recv);
  80. memset(status_ops, 0, sizeof(status_ops));
  81. op = status_ops;
  82. op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
  83. op->data.recv_status_on_client.trailing_metadata =
  84. &calls[call_idx].trailing_metadata_recv;
  85. op->data.recv_status_on_client.status = &calls[call_idx].status;
  86. op->data.recv_status_on_client.status_details = &calls[call_idx].details;
  87. op++;
  88. GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(calls[call_idx].call,
  89. status_ops,
  90. (size_t)(op - status_ops),
  91. tag(call_idx), nullptr));
  92. grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), nullptr);
  93. grpc_metadata_array_destroy(&calls[call_idx].initial_metadata_recv);
  94. grpc_metadata_array_destroy(&calls[call_idx].trailing_metadata_recv);
  95. grpc_slice_unref(calls[call_idx].details);
  96. grpc_call_unref(calls[call_idx].call);
  97. calls[call_idx].call = nullptr;
  98. }
  99. static MemStats send_snapshot_request(int call_idx, grpc_slice call_type) {
  100. grpc_metadata_array_init(&calls[call_idx].initial_metadata_recv);
  101. grpc_metadata_array_init(&calls[call_idx].trailing_metadata_recv);
  102. grpc_byte_buffer* response_payload_recv = nullptr;
  103. memset(snapshot_ops, 0, sizeof(snapshot_ops));
  104. op = snapshot_ops;
  105. op->op = GRPC_OP_SEND_INITIAL_METADATA;
  106. op->data.send_initial_metadata.count = 0;
  107. op->flags = GRPC_INITIAL_METADATA_WAIT_FOR_READY;
  108. op++;
  109. op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
  110. op++;
  111. op->op = GRPC_OP_RECV_INITIAL_METADATA;
  112. op->data.recv_initial_metadata.recv_initial_metadata =
  113. &calls[call_idx].initial_metadata_recv;
  114. op++;
  115. op->op = GRPC_OP_RECV_MESSAGE;
  116. op->data.recv_message.recv_message = &response_payload_recv;
  117. op++;
  118. op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
  119. op->data.recv_status_on_client.trailing_metadata =
  120. &calls[call_idx].trailing_metadata_recv;
  121. op->data.recv_status_on_client.status = &calls[call_idx].status;
  122. op->data.recv_status_on_client.status_details = &calls[call_idx].details;
  123. op++;
  124. grpc_slice hostname = grpc_slice_from_static_string("localhost");
  125. calls[call_idx].call = grpc_channel_create_call(
  126. channel, nullptr, GRPC_PROPAGATE_DEFAULTS, cq, call_type, &hostname,
  127. gpr_inf_future(GPR_CLOCK_REALTIME), nullptr);
  128. GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(calls[call_idx].call,
  129. snapshot_ops,
  130. (size_t)(op - snapshot_ops),
  131. (void*)nullptr, nullptr));
  132. grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), nullptr);
  133. grpc_byte_buffer_reader reader;
  134. grpc_byte_buffer_reader_init(&reader, response_payload_recv);
  135. grpc_slice response = grpc_byte_buffer_reader_readall(&reader);
  136. MemStats snapshot =
  137. *reinterpret_cast<MemStats*>(GRPC_SLICE_START_PTR(response));
  138. grpc_metadata_array_destroy(&calls[call_idx].initial_metadata_recv);
  139. grpc_metadata_array_destroy(&calls[call_idx].trailing_metadata_recv);
  140. grpc_slice_unref(response);
  141. grpc_byte_buffer_reader_destroy(&reader);
  142. grpc_byte_buffer_destroy(response_payload_recv);
  143. grpc_slice_unref(calls[call_idx].details);
  144. calls[call_idx].details = grpc_empty_slice();
  145. grpc_call_unref(calls[call_idx].call);
  146. calls[call_idx].call = nullptr;
  147. return snapshot;
  148. }
  149. // Create iterations calls, return MemStats when all outstanding
  150. std::pair<MemStats, MemStats> run_test_loop(int iterations, int* call_idx) {
  151. grpc_event event;
  152. // benchmark period
  153. for (int i = 0; i < iterations; ++i) {
  154. init_ping_pong_request(*call_idx + i + 1);
  155. }
  156. auto peak = std::make_pair(
  157. // client
  158. MemStats::Snapshot(),
  159. // server
  160. send_snapshot_request(
  161. 0, grpc_slice_from_static_string("Reflector/DestroyCalls")));
  162. do {
  163. event = grpc_completion_queue_next(
  164. cq,
  165. gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
  166. gpr_time_from_micros(10000, GPR_TIMESPAN)),
  167. nullptr);
  168. } while (event.type != GRPC_QUEUE_TIMEOUT);
  169. // second step - recv status and destroy call
  170. for (int i = 0; i < iterations; ++i) {
  171. finish_ping_pong_request(*call_idx + i + 1);
  172. }
  173. do {
  174. event = grpc_completion_queue_next(
  175. cq,
  176. gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
  177. gpr_time_from_micros(10000, GPR_TIMESPAN)),
  178. nullptr);
  179. } while (event.type != GRPC_QUEUE_TIMEOUT);
  180. *call_idx += iterations;
  181. return peak;
  182. }
  183. ABSL_FLAG(std::string, target, "localhost:443", "Target host:port");
  184. ABSL_FLAG(int, warmup, 100, "Warmup iterations");
  185. ABSL_FLAG(int, benchmark, 1000, "Benchmark iterations");
  186. int main(int argc, char** argv) {
  187. absl::ParseCommandLine(argc, argv);
  188. grpc_slice slice = grpc_slice_from_copied_string("x");
  189. char* fake_argv[1];
  190. GPR_ASSERT(argc >= 1);
  191. fake_argv[0] = argv[0];
  192. grpc::testing::TestEnvironment env(1, fake_argv);
  193. grpc_init();
  194. for (size_t k = 0; k < GPR_ARRAY_SIZE(calls); k++) {
  195. calls[k].details = grpc_empty_slice();
  196. }
  197. cq = grpc_completion_queue_create_for_next(nullptr);
  198. channel = grpc_channel_create(absl::GetFlag(FLAGS_target).c_str(),
  199. grpc_insecure_credentials_create(), nullptr);
  200. int call_idx = 0;
  201. const int warmup_iterations = absl::GetFlag(FLAGS_warmup);
  202. const int benchmark_iterations = absl::GetFlag(FLAGS_benchmark);
  203. // warmup period
  204. MemStats server_benchmark_calls_start = send_snapshot_request(
  205. 0, grpc_slice_from_static_string("Reflector/SimpleSnapshot"));
  206. MemStats client_benchmark_calls_start = MemStats::Snapshot();
  207. run_test_loop(warmup_iterations, &call_idx);
  208. std::pair<MemStats, MemStats> peak =
  209. run_test_loop(benchmark_iterations, &call_idx);
  210. MemStats client_calls_inflight = peak.first;
  211. MemStats server_calls_inflight = peak.second;
  212. grpc_channel_destroy(channel);
  213. grpc_completion_queue_shutdown(cq);
  214. grpc_event event;
  215. do {
  216. event = grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME),
  217. nullptr);
  218. } while (event.type != GRPC_QUEUE_SHUTDOWN);
  219. grpc_slice_unref(slice);
  220. grpc_completion_queue_destroy(cq);
  221. grpc_shutdown_blocking();
  222. printf("---------client stats--------\n");
  223. printf("client call memory usage: %f bytes per call\n",
  224. static_cast<double>(client_calls_inflight.rss -
  225. client_benchmark_calls_start.rss) /
  226. benchmark_iterations * 1024);
  227. printf("---------server stats--------\n");
  228. printf("server call memory usage: %f bytes per call\n",
  229. static_cast<double>(server_calls_inflight.rss -
  230. server_benchmark_calls_start.rss) /
  231. benchmark_iterations * 1024);
  232. return 0;
  233. }