diff options
Diffstat (limited to 'test/cpp/microbenchmarks')
-rw-r--r-- | test/cpp/microbenchmarks/BUILD | 108 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/bm_arena.cc | 76 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/bm_call_create.cc | 435 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/bm_chttp2_hpack.cc | 227 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/bm_chttp2_transport.cc | 625 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/bm_closure.cc | 3 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/bm_cq.cc | 28 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/bm_cq_multiple_threads.cc | 160 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/bm_error.cc | 74 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/bm_fullstack_streaming_ping_pong.cc | 430 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/bm_fullstack_streaming_pump.cc | 11 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/bm_fullstack_trickle.cc | 5 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/bm_fullstack_unary_ping_pong.cc | 12 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/bm_metadata.cc | 17 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/fullstack_fixtures.h | 81 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/helpers.h | 2 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/noop-benchmark.cc | 2 |
17 files changed, 2116 insertions, 180 deletions
diff --git a/test/cpp/microbenchmarks/BUILD b/test/cpp/microbenchmarks/BUILD new file mode 100644 index 0000000000..cae3fa1a14 --- /dev/null +++ b/test/cpp/microbenchmarks/BUILD @@ -0,0 +1,108 @@ +# Copyright 2017, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +licenses(["notice"]) # 3-clause BSD + +cc_test( + name = "noop-benchmark", + srcs = ["noop-benchmark.cc"], + linkopts = ["-pthread"], + deps = ["//external:benchmark"], +) + +cc_library( + name = "helpers", + srcs = ["helpers.cc"], + hdrs = [ + "fullstack_context_mutators.h", + "fullstack_fixtures.h", + "helpers.h", + ], + linkopts = ["-pthread"], + deps = [ + "//:grpc++", + "//external:benchmark", + "//src/proto/grpc/testing:echo_proto", + "//test/core/util:grpc_test_util", + ], +) + +cc_test( + name = "bm_closure", + srcs = ["bm_closure.cc"], + deps = [":helpers"], +) + +cc_test( + name = "bm_cq", + srcs = ["bm_cq.cc"], + deps = [":helpers"], +) + +cc_test( + name = "bm_cq_multiple_threads", + srcs = ["bm_cq_multiple_threads.cc"], + deps = [":helpers"], +) + +cc_test( + name = "bm_error", + srcs = ["bm_error.cc"], + deps = [":helpers"], +) + +cc_test( + name = "bm_fullstack_streaming_ping_pong", + srcs = ["bm_fullstack_streaming_ping_pong.cc"], + deps = [":helpers"], +) + +cc_test( + name = "bm_fullstack_streaming_pump", + srcs = ["bm_fullstack_streaming_pump.cc"], + deps = [":helpers"], +) + +cc_test( + name = "bm_fullstack_trickle", + srcs = ["bm_fullstack_trickle.cc"], + deps = [":helpers"], +) + +cc_test( + name = "bm_fullstack_unary_ping_pong", + srcs = ["bm_fullstack_unary_ping_pong.cc"], + deps = [":helpers"], +) + +cc_test( + name = "bm_metadata", + srcs = ["bm_metadata.cc"], + deps = [":helpers"], +) diff --git a/test/cpp/microbenchmarks/bm_arena.cc b/test/cpp/microbenchmarks/bm_arena.cc new file mode 100644 index 0000000000..770c0b6d47 --- /dev/null +++ b/test/cpp/microbenchmarks/bm_arena.cc @@ -0,0 +1,76 @@ +/* + * + * Copyright 2017, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* Benchmark arenas */ + +extern "C" { +#include "src/core/lib/support/arena.h" +} +#include "test/cpp/microbenchmarks/helpers.h" +#include "third_party/benchmark/include/benchmark/benchmark.h" + +static void BM_Arena_NoOp(benchmark::State& state) { + while (state.KeepRunning()) { + gpr_arena_destroy(gpr_arena_create(state.range(0))); + } +} +BENCHMARK(BM_Arena_NoOp)->Range(1, 1024 * 1024); + +static void BM_Arena_ManyAlloc(benchmark::State& state) { + gpr_arena* a = gpr_arena_create(state.range(0)); + const size_t realloc_after = + 1024 * 1024 * 1024 / ((state.range(1) + 15) & 0xffffff0u); + while (state.KeepRunning()) { + gpr_arena_alloc(a, state.range(1)); + // periodically recreate arena to avoid OOM + if (state.iterations() % realloc_after == 0) { + gpr_arena_destroy(a); + a = gpr_arena_create(state.range(0)); + } + } + gpr_arena_destroy(a); +} +BENCHMARK(BM_Arena_ManyAlloc)->Ranges({{1, 1024 * 1024}, {1, 32 * 1024}}); + +static void BM_Arena_Batch(benchmark::State& state) { + while (state.KeepRunning()) { + gpr_arena* a = gpr_arena_create(state.range(0)); + for (int i = 0; i < state.range(1); i++) { + gpr_arena_alloc(a, state.range(2)); + } + gpr_arena_destroy(a); + } +} +BENCHMARK(BM_Arena_Batch)->Ranges({{1, 64 * 1024}, {1, 64}, {1, 1024}}); + +BENCHMARK_MAIN(); diff --git a/test/cpp/microbenchmarks/bm_call_create.cc b/test/cpp/microbenchmarks/bm_call_create.cc index 146f8e6bad..c91219e98c 100644 --- a/test/cpp/microbenchmarks/bm_call_create.cc +++ b/test/cpp/microbenchmarks/bm_call_create.cc @@ -34,6 +34,7 @@ /* This benchmark exists to ensure that the benchmark integration is * working */ +#include <benchmark/benchmark.h> #include <string.h> #include <sstream> @@ -44,22 +45,23 @@ #include <grpc/support/string_util.h> extern "C" { -#include "src/core/ext/client_channel/client_channel.h" -#include "src/core/ext/load_reporting/load_reporting_filter.h" +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/deadline/deadline_filter.h" +#include "src/core/ext/filters/http/client/http_client_filter.h" +#include "src/core/ext/filters/http/message_compress/message_compress_filter.h" +#include "src/core/ext/filters/http/server/http_server_filter.h" +#include "src/core/ext/filters/load_reporting/load_reporting_filter.h" +#include "src/core/ext/filters/message_size/message_size_filter.h" #include "src/core/lib/channel/channel_stack.h" -#include "src/core/lib/channel/compress_filter.h" #include "src/core/lib/channel/connected_channel.h" -#include "src/core/lib/channel/deadline_filter.h" -#include "src/core/lib/channel/http_client_filter.h" -#include "src/core/lib/channel/http_server_filter.h" -#include "src/core/lib/channel/message_size_filter.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/surface/channel.h" #include "src/core/lib/transport/transport_impl.h" } #include "src/cpp/client/create_channel_internal.h" #include "src/proto/grpc/testing/echo.grpc.pb.h" #include "test/cpp/microbenchmarks/helpers.h" -#include "third_party/benchmark/include/benchmark/benchmark.h" auto &force_library_initialization = Library::get(); @@ -87,6 +89,9 @@ BENCHMARK(BM_Zalloc) ->Arg(6144) ->Arg(7168); +//////////////////////////////////////////////////////////////////////////////// +// Benchmarks creating full stacks + class BaseChannelFixture { public: BaseChannelFixture(grpc_channel *channel) : channel_(channel) {} @@ -116,12 +121,12 @@ template <class Fixture> static void BM_CallCreateDestroy(benchmark::State &state) { TrackCounters track_counters; Fixture fixture; - grpc_completion_queue *cq = grpc_completion_queue_create(NULL); + grpc_completion_queue *cq = grpc_completion_queue_create_for_next(NULL); gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); void *method_hdl = grpc_channel_register_call(fixture.channel(), "/foo/bar", NULL, NULL); while (state.KeepRunning()) { - grpc_call_destroy(grpc_channel_create_registered_call( + grpc_call_unref(grpc_channel_create_registered_call( fixture.channel(), NULL, GRPC_PROPAGATE_DEFAULTS, cq, method_hdl, deadline, NULL)); } @@ -132,6 +137,9 @@ static void BM_CallCreateDestroy(benchmark::State &state) { BENCHMARK_TEMPLATE(BM_CallCreateDestroy, InsecureChannel); BENCHMARK_TEMPLATE(BM_CallCreateDestroy, LameChannel); +//////////////////////////////////////////////////////////////////////////////// +// Benchmarks isolating individual filters + static void *tag(int i) { return reinterpret_cast<void *>(static_cast<intptr_t>(i)); } @@ -147,6 +155,7 @@ static void BM_LameChannelCallCreateCpp(benchmark::State &state) { grpc::testing::EchoResponse recv_response; grpc::Status recv_status; while (state.KeepRunning()) { + GPR_TIMER_SCOPE("BenchmarkCycle", 0); grpc::ClientContext cli_ctx; auto reader = stub->AsyncEcho(&cli_ctx, send_request, &cq); reader->Finish(&recv_response, &recv_status, tag(0)); @@ -159,6 +168,165 @@ static void BM_LameChannelCallCreateCpp(benchmark::State &state) { } BENCHMARK(BM_LameChannelCallCreateCpp); +static void do_nothing(void *ignored) {} + +static void BM_LameChannelCallCreateCore(benchmark::State &state) { + TrackCounters track_counters; + + grpc_channel *channel; + grpc_completion_queue *cq; + grpc_metadata_array initial_metadata_recv; + grpc_metadata_array trailing_metadata_recv; + grpc_byte_buffer *response_payload_recv = NULL; + grpc_status_code status; + grpc_slice details; + grpc::testing::EchoRequest send_request; + grpc_slice send_request_slice = + grpc_slice_new(&send_request, sizeof(send_request), do_nothing); + + channel = grpc_lame_client_channel_create( + "localhost:1234", GRPC_STATUS_UNAUTHENTICATED, "blah"); + cq = grpc_completion_queue_create_for_next(NULL); + void *rc = grpc_channel_register_call( + channel, "/grpc.testing.EchoTestService/Echo", NULL, NULL); + while (state.KeepRunning()) { + GPR_TIMER_SCOPE("BenchmarkCycle", 0); + grpc_call *call = grpc_channel_create_registered_call( + channel, NULL, GRPC_PROPAGATE_DEFAULTS, cq, rc, + gpr_inf_future(GPR_CLOCK_REALTIME), NULL); + grpc_metadata_array_init(&initial_metadata_recv); + grpc_metadata_array_init(&trailing_metadata_recv); + grpc_byte_buffer *request_payload_send = + grpc_raw_byte_buffer_create(&send_request_slice, 1); + + // Fill in call ops + grpc_op ops[6]; + memset(ops, 0, sizeof(ops)); + grpc_op *op = ops; + op->op = GRPC_OP_SEND_INITIAL_METADATA; + op->data.send_initial_metadata.count = 0; + op++; + op->op = GRPC_OP_SEND_MESSAGE; + op->data.send_message.send_message = request_payload_send; + op++; + op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; + op++; + op->op = GRPC_OP_RECV_INITIAL_METADATA; + op->data.recv_initial_metadata.recv_initial_metadata = + &initial_metadata_recv; + op++; + op->op = GRPC_OP_RECV_MESSAGE; + op->data.recv_message.recv_message = &response_payload_recv; + op++; + op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; + op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; + op->data.recv_status_on_client.status = &status; + op->data.recv_status_on_client.status_details = &details; + op++; + + GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(call, ops, + (size_t)(op - ops), + (void *)1, NULL)); + grpc_event ev = grpc_completion_queue_next( + cq, gpr_inf_future(GPR_CLOCK_REALTIME), NULL); + GPR_ASSERT(ev.type != GRPC_QUEUE_SHUTDOWN); + GPR_ASSERT(ev.success != 0); + grpc_call_unref(call); + grpc_byte_buffer_destroy(request_payload_send); + grpc_byte_buffer_destroy(response_payload_recv); + grpc_metadata_array_destroy(&initial_metadata_recv); + grpc_metadata_array_destroy(&trailing_metadata_recv); + } + grpc_channel_destroy(channel); + grpc_completion_queue_destroy(cq); + grpc_slice_unref(send_request_slice); + track_counters.Finish(state); +} +BENCHMARK(BM_LameChannelCallCreateCore); + +static void BM_LameChannelCallCreateCoreSeparateBatch(benchmark::State &state) { + TrackCounters track_counters; + + grpc_channel *channel; + grpc_completion_queue *cq; + grpc_metadata_array initial_metadata_recv; + grpc_metadata_array trailing_metadata_recv; + grpc_byte_buffer *response_payload_recv = NULL; + grpc_status_code status; + grpc_slice details; + grpc::testing::EchoRequest send_request; + grpc_slice send_request_slice = + grpc_slice_new(&send_request, sizeof(send_request), do_nothing); + + channel = grpc_lame_client_channel_create( + "localhost:1234", GRPC_STATUS_UNAUTHENTICATED, "blah"); + cq = grpc_completion_queue_create_for_next(NULL); + void *rc = grpc_channel_register_call( + channel, "/grpc.testing.EchoTestService/Echo", NULL, NULL); + while (state.KeepRunning()) { + GPR_TIMER_SCOPE("BenchmarkCycle", 0); + grpc_call *call = grpc_channel_create_registered_call( + channel, NULL, GRPC_PROPAGATE_DEFAULTS, cq, rc, + gpr_inf_future(GPR_CLOCK_REALTIME), NULL); + grpc_metadata_array_init(&initial_metadata_recv); + grpc_metadata_array_init(&trailing_metadata_recv); + grpc_byte_buffer *request_payload_send = + grpc_raw_byte_buffer_create(&send_request_slice, 1); + + // Fill in call ops + grpc_op ops[3]; + memset(ops, 0, sizeof(ops)); + grpc_op *op = ops; + op->op = GRPC_OP_SEND_INITIAL_METADATA; + op->data.send_initial_metadata.count = 0; + op++; + op->op = GRPC_OP_SEND_MESSAGE; + op->data.send_message.send_message = request_payload_send; + op++; + op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; + op++; + GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(call, ops, + (size_t)(op - ops), + (void *)0, NULL)); + memset(ops, 0, sizeof(ops)); + op = ops; + op->op = GRPC_OP_RECV_INITIAL_METADATA; + op->data.recv_initial_metadata.recv_initial_metadata = + &initial_metadata_recv; + op++; + op->op = GRPC_OP_RECV_MESSAGE; + op->data.recv_message.recv_message = &response_payload_recv; + op++; + op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; + op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; + op->data.recv_status_on_client.status = &status; + op->data.recv_status_on_client.status_details = &details; + op++; + + GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(call, ops, + (size_t)(op - ops), + (void *)1, NULL)); + grpc_event ev = grpc_completion_queue_next( + cq, gpr_inf_future(GPR_CLOCK_REALTIME), NULL); + GPR_ASSERT(ev.type != GRPC_QUEUE_SHUTDOWN); + GPR_ASSERT(ev.success == 0); + ev = grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), + NULL); + GPR_ASSERT(ev.type != GRPC_QUEUE_SHUTDOWN); + GPR_ASSERT(ev.success != 0); + grpc_call_unref(call); + grpc_byte_buffer_destroy(request_payload_send); + grpc_byte_buffer_destroy(response_payload_recv); + grpc_metadata_array_destroy(&initial_metadata_recv); + grpc_metadata_array_destroy(&trailing_metadata_recv); + } + grpc_channel_destroy(channel); + grpc_completion_queue_destroy(cq); + grpc_slice_unref(send_request_slice); + track_counters.Finish(state); +} +BENCHMARK(BM_LameChannelCallCreateCoreSeparateBatch); + static void FilterDestroy(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { gpr_free(arg); @@ -216,7 +384,7 @@ namespace dummy_filter { static void StartTransportStreamOp(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_transport_stream_op *op) {} + grpc_transport_stream_op_batch *op) {} static void StartTransportOp(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, @@ -234,7 +402,7 @@ static void SetPollsetOrPollsetSet(grpc_exec_ctx *exec_ctx, static void DestroyCallElem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, const grpc_call_final_info *final_info, - void *and_free_memory) {} + grpc_closure *then_sched_closure) {} grpc_error *InitChannelElem(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, grpc_channel_element_args *args) { @@ -277,7 +445,7 @@ const char *name; /* implementation of grpc_transport_init_stream */ int InitStream(grpc_exec_ctx *exec_ctx, grpc_transport *self, grpc_stream *stream, grpc_stream_refcount *refcount, - const void *server_data) { + const void *server_data, gpr_arena *arena) { return 0; } @@ -291,7 +459,7 @@ void SetPollsetSet(grpc_exec_ctx *exec_ctx, grpc_transport *self, /* implementation of grpc_transport_perform_stream_op */ void PerformStreamOp(grpc_exec_ctx *exec_ctx, grpc_transport *self, - grpc_stream *stream, grpc_transport_stream_op *op) { + grpc_stream *stream, grpc_transport_stream_op_batch *op) { grpc_closure_sched(exec_ctx, op->on_complete, GRPC_ERROR_NONE); } @@ -301,7 +469,7 @@ void PerformOp(grpc_exec_ctx *exec_ctx, grpc_transport *self, /* implementation of grpc_transport_destroy_stream */ void DestroyStream(grpc_exec_ctx *exec_ctx, grpc_transport *self, - grpc_stream *stream, void *and_free_memory) {} + grpc_stream *stream, grpc_closure *then_sched_closure) {} /* implementation of grpc_transport_destroy */ void Destroy(grpc_exec_ctx *exec_ctx, grpc_transport *self) {} @@ -341,13 +509,15 @@ class SendEmptyMetadata { memset(&op_, 0, sizeof(op_)); op_.on_complete = grpc_closure_init(&closure_, DoNothing, nullptr, grpc_schedule_on_exec_ctx); + op_.send_initial_metadata = true; + op_.payload = &op_payload_; } class Op { public: Op(grpc_exec_ctx *exec_ctx, SendEmptyMetadata *p, grpc_call_stack *s) { grpc_metadata_batch_init(&batch_); - p->op_.send_initial_metadata = &batch_; + p->op_payload_.send_initial_metadata.send_initial_metadata = &batch_; } void Finish(grpc_exec_ctx *exec_ctx) { grpc_metadata_batch_destroy(exec_ctx, &batch_); @@ -361,7 +531,8 @@ class SendEmptyMetadata { const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC); const gpr_timespec start_time_ = gpr_now(GPR_CLOCK_MONOTONIC); const grpc_slice method_ = grpc_slice_from_static_string("/foo/bar"); - grpc_transport_stream_op op_; + grpc_transport_stream_op_batch op_; + grpc_transport_stream_op_batch_payload op_payload_; grpc_closure closure_; }; @@ -396,7 +567,7 @@ static void BM_IsolatedFilter(benchmark::State &state) { grpc_channel_stack *channel_stack = static_cast<grpc_channel_stack *>(gpr_zalloc(channel_size)); GPR_ASSERT(GRPC_LOG_IF_ERROR( - "call_stack_init", + "channel_stack_init", grpc_channel_stack_init(&exec_ctx, 1, FilterDestroy, channel_stack, &filters[0], filters.size(), &channel_args, fixture.flags & REQUIRES_TRANSPORT @@ -411,15 +582,30 @@ static void BM_IsolatedFilter(benchmark::State &state) { grpc_slice method = grpc_slice_from_static_string("/foo/bar"); grpc_call_final_info final_info; TestOp test_op_data; + grpc_call_element_args call_args; + call_args.call_stack = call_stack; + call_args.server_transport_data = NULL; + call_args.context = NULL; + call_args.path = method; + call_args.start_time = start_time; + call_args.deadline = deadline; + const int kArenaSize = 4096; + call_args.arena = gpr_arena_create(kArenaSize); while (state.KeepRunning()) { + GPR_TIMER_SCOPE("BenchmarkCycle", 0); GRPC_ERROR_UNREF(grpc_call_stack_init(&exec_ctx, channel_stack, 1, - DoNothing, NULL, NULL, NULL, method, - start_time, deadline, call_stack)); + DoNothing, NULL, &call_args)); typename TestOp::Op op(&exec_ctx, &test_op_data, call_stack); grpc_call_stack_destroy(&exec_ctx, call_stack, &final_info, NULL); op.Finish(&exec_ctx); grpc_exec_ctx_flush(&exec_ctx); + // recreate arena every 64k iterations to avoid oom + if (0 == (state.iterations() & 0xffff)) { + gpr_arena_destroy(call_args.arena); + call_args.arena = gpr_arena_create(kArenaSize); + } } + gpr_arena_destroy(call_args.arena); grpc_channel_stack_destroy(&exec_ctx, channel_stack); grpc_exec_ctx_finish(&exec_ctx); gpr_free(channel_stack); @@ -436,7 +622,7 @@ BENCHMARK_TEMPLATE(BM_IsolatedFilter, DummyFilter, NoOp); BENCHMARK_TEMPLATE(BM_IsolatedFilter, DummyFilter, SendEmptyMetadata); typedef Fixture<&grpc_client_channel_filter, 0> ClientChannelFilter; BENCHMARK_TEMPLATE(BM_IsolatedFilter, ClientChannelFilter, NoOp); -typedef Fixture<&grpc_compress_filter, CHECKS_NOT_LAST> CompressFilter; +typedef Fixture<&grpc_message_compress_filter, CHECKS_NOT_LAST> CompressFilter; BENCHMARK_TEMPLATE(BM_IsolatedFilter, CompressFilter, NoOp); BENCHMARK_TEMPLATE(BM_IsolatedFilter, CompressFilter, SendEmptyMetadata); typedef Fixture<&grpc_client_deadline_filter, CHECKS_NOT_LAST> @@ -462,4 +648,211 @@ typedef Fixture<&grpc_load_reporting_filter, CHECKS_NOT_LAST> BENCHMARK_TEMPLATE(BM_IsolatedFilter, LoadReportingFilter, NoOp); BENCHMARK_TEMPLATE(BM_IsolatedFilter, LoadReportingFilter, SendEmptyMetadata); +//////////////////////////////////////////////////////////////////////////////// +// Benchmarks isolating grpc_call + +namespace isolated_call_filter { + +static void StartTransportStreamOp(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_transport_stream_op_batch *op) { + if (op->recv_initial_metadata) { + grpc_closure_sched( + exec_ctx, + op->payload->recv_initial_metadata.recv_initial_metadata_ready, + GRPC_ERROR_NONE); + } + if (op->recv_message) { + grpc_closure_sched(exec_ctx, op->payload->recv_message.recv_message_ready, + GRPC_ERROR_NONE); + } + grpc_closure_sched(exec_ctx, op->on_complete, GRPC_ERROR_NONE); +} + +static void StartTransportOp(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + grpc_transport_op *op) { + if (op->disconnect_with_error != GRPC_ERROR_NONE) { + GRPC_ERROR_UNREF(op->disconnect_with_error); + } + grpc_closure_sched(exec_ctx, op->on_consumed, GRPC_ERROR_NONE); +} + +static grpc_error *InitCallElem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + const grpc_call_element_args *args) { + return GRPC_ERROR_NONE; +} + +static void SetPollsetOrPollsetSet(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_polling_entity *pollent) {} + +static void DestroyCallElem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + const grpc_call_final_info *final_info, + grpc_closure *then_sched_closure) { + grpc_closure_sched(exec_ctx, then_sched_closure, GRPC_ERROR_NONE); +} + +grpc_error *InitChannelElem(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, + grpc_channel_element_args *args) { + return GRPC_ERROR_NONE; +} + +void DestroyChannelElem(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem) {} + +char *GetPeer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) { + return gpr_strdup("peer"); +} + +void GetChannelInfo(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, + const grpc_channel_info *channel_info) {} + +static const grpc_channel_filter isolated_call_filter = { + StartTransportStreamOp, + StartTransportOp, + 0, + InitCallElem, + SetPollsetOrPollsetSet, + DestroyCallElem, + 0, + InitChannelElem, + DestroyChannelElem, + GetPeer, + GetChannelInfo, + "isolated_call_filter"}; +} // namespace isolated_call_filter + +class IsolatedCallFixture : public TrackCounters { + public: + IsolatedCallFixture() { + grpc_channel_stack_builder *builder = grpc_channel_stack_builder_create(); + grpc_channel_stack_builder_set_name(builder, "dummy"); + grpc_channel_stack_builder_set_target(builder, "dummy_target"); + GPR_ASSERT(grpc_channel_stack_builder_append_filter( + builder, &isolated_call_filter::isolated_call_filter, NULL, NULL)); + { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + channel_ = grpc_channel_create_with_builder(&exec_ctx, builder, + GRPC_CLIENT_CHANNEL); + grpc_exec_ctx_finish(&exec_ctx); + } + cq_ = grpc_completion_queue_create_for_next(NULL); + } + + void Finish(benchmark::State &state) { + grpc_completion_queue_destroy(cq_); + grpc_channel_destroy(channel_); + TrackCounters::Finish(state); + } + + grpc_channel *channel() const { return channel_; } + grpc_completion_queue *cq() const { return cq_; } + + private: + grpc_completion_queue *cq_; + grpc_channel *channel_; +}; + +static void BM_IsolatedCall_NoOp(benchmark::State &state) { + IsolatedCallFixture fixture; + gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + void *method_hdl = + grpc_channel_register_call(fixture.channel(), "/foo/bar", NULL, NULL); + while (state.KeepRunning()) { + GPR_TIMER_SCOPE("BenchmarkCycle", 0); + grpc_call_unref(grpc_channel_create_registered_call( + fixture.channel(), nullptr, GRPC_PROPAGATE_DEFAULTS, fixture.cq(), + method_hdl, deadline, NULL)); + } + fixture.Finish(state); +} +BENCHMARK(BM_IsolatedCall_NoOp); + +static void BM_IsolatedCall_Unary(benchmark::State &state) { + IsolatedCallFixture fixture; + gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + void *method_hdl = + grpc_channel_register_call(fixture.channel(), "/foo/bar", NULL, NULL); + grpc_slice slice = grpc_slice_from_static_string("hello world"); + grpc_byte_buffer *send_message = grpc_raw_byte_buffer_create(&slice, 1); + grpc_byte_buffer *recv_message = NULL; + grpc_status_code status_code; + grpc_slice status_details = grpc_empty_slice(); + grpc_metadata_array recv_initial_metadata; + grpc_metadata_array_init(&recv_initial_metadata); + grpc_metadata_array recv_trailing_metadata; + grpc_metadata_array_init(&recv_trailing_metadata); + grpc_op ops[6]; + memset(ops, 0, sizeof(ops)); + ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; + ops[1].op = GRPC_OP_SEND_MESSAGE; + ops[1].data.send_message.send_message = send_message; + ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; + ops[3].op = GRPC_OP_RECV_INITIAL_METADATA; + ops[3].data.recv_initial_metadata.recv_initial_metadata = + &recv_initial_metadata; + ops[4].op = GRPC_OP_RECV_MESSAGE; + ops[4].data.recv_message.recv_message = &recv_message; + ops[5].op = GRPC_OP_RECV_STATUS_ON_CLIENT; + ops[5].data.recv_status_on_client.status = &status_code; + ops[5].data.recv_status_on_client.status_details = &status_details; + ops[5].data.recv_status_on_client.trailing_metadata = &recv_trailing_metadata; + while (state.KeepRunning()) { + GPR_TIMER_SCOPE("BenchmarkCycle", 0); + grpc_call *call = grpc_channel_create_registered_call( + fixture.channel(), nullptr, GRPC_PROPAGATE_DEFAULTS, fixture.cq(), + method_hdl, deadline, NULL); + grpc_call_start_batch(call, ops, 6, tag(1), NULL); + grpc_completion_queue_next(fixture.cq(), + gpr_inf_future(GPR_CLOCK_MONOTONIC), NULL); + grpc_call_unref(call); + } + fixture.Finish(state); + grpc_metadata_array_destroy(&recv_initial_metadata); + grpc_metadata_array_destroy(&recv_trailing_metadata); + grpc_byte_buffer_destroy(send_message); +} +BENCHMARK(BM_IsolatedCall_Unary); + +static void BM_IsolatedCall_StreamingSend(benchmark::State &state) { + IsolatedCallFixture fixture; + gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + void *method_hdl = + grpc_channel_register_call(fixture.channel(), "/foo/bar", NULL, NULL); + grpc_slice slice = grpc_slice_from_static_string("hello world"); + grpc_byte_buffer *send_message = grpc_raw_byte_buffer_create(&slice, 1); + grpc_metadata_array recv_initial_metadata; + grpc_metadata_array_init(&recv_initial_metadata); + grpc_metadata_array recv_trailing_metadata; + grpc_metadata_array_init(&recv_trailing_metadata); + grpc_op ops[2]; + memset(ops, 0, sizeof(ops)); + ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; + ops[1].op = GRPC_OP_RECV_INITIAL_METADATA; + ops[1].data.recv_initial_metadata.recv_initial_metadata = + &recv_initial_metadata; + grpc_call *call = grpc_channel_create_registered_call( + fixture.channel(), nullptr, GRPC_PROPAGATE_DEFAULTS, fixture.cq(), + method_hdl, deadline, NULL); + grpc_call_start_batch(call, ops, 2, tag(1), NULL); + grpc_completion_queue_next(fixture.cq(), gpr_inf_future(GPR_CLOCK_MONOTONIC), + NULL); + memset(ops, 0, sizeof(ops)); + ops[0].op = GRPC_OP_SEND_MESSAGE; + ops[0].data.send_message.send_message = send_message; + while (state.KeepRunning()) { + GPR_TIMER_SCOPE("BenchmarkCycle", 0); + grpc_call_start_batch(call, ops, 1, tag(2), NULL); + grpc_completion_queue_next(fixture.cq(), + gpr_inf_future(GPR_CLOCK_MONOTONIC), NULL); + } + grpc_call_unref(call); + fixture.Finish(state); + grpc_metadata_array_destroy(&recv_initial_metadata); + grpc_metadata_array_destroy(&recv_trailing_metadata); + grpc_byte_buffer_destroy(send_message); +} +BENCHMARK(BM_IsolatedCall_StreamingSend); + BENCHMARK_MAIN(); diff --git a/test/cpp/microbenchmarks/bm_chttp2_hpack.cc b/test/cpp/microbenchmarks/bm_chttp2_hpack.cc index 563db758f7..581440682a 100644 --- a/test/cpp/microbenchmarks/bm_chttp2_hpack.cc +++ b/test/cpp/microbenchmarks/bm_chttp2_hpack.cc @@ -33,6 +33,7 @@ /* Microbenchmarks around CHTTP2 HPACK operations */ +#include <grpc/support/alloc.h> #include <grpc/support/log.h> #include <string.h> #include <sstream> @@ -40,6 +41,7 @@ extern "C" { #include "src/core/ext/transport/chttp2/transport/hpack_encoder.h" #include "src/core/ext/transport/chttp2/transport/hpack_parser.h" #include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" #include "src/core/lib/transport/static_metadata.h" } #include "test/cpp/microbenchmarks/helpers.h" @@ -69,6 +71,7 @@ template <class Fixture> static void BM_HpackEncoderEncodeHeader(benchmark::State &state) { TrackCounters track_counters; grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + static bool logged_representative_output = false; grpc_metadata_batch b; grpc_metadata_batch_init(&b); @@ -87,8 +90,22 @@ static void BM_HpackEncoderEncodeHeader(benchmark::State &state) { grpc_slice_buffer outbuf; grpc_slice_buffer_init(&outbuf); while (state.KeepRunning()) { - grpc_chttp2_encode_header(&exec_ctx, &c, (uint32_t)state.iterations(), &b, - state.range(0), state.range(1), &stats, &outbuf); + grpc_encode_header_options hopt = { + static_cast<uint32_t>(state.iterations()), + state.range(0) != 0, + Fixture::kEnableTrueBinary, + (size_t)state.range(1), + &stats, + }; + grpc_chttp2_encode_header(&exec_ctx, &c, &b, &hopt, &outbuf); + if (!logged_representative_output && state.iterations() > 3) { + logged_representative_output = true; + for (size_t i = 0; i < outbuf.count; i++) { + char *s = grpc_dump_slice(outbuf.slices[i], GPR_DUMP_HEX); + gpr_log(GPR_DEBUG, "%" PRIdPTR ": %s", i, s); + gpr_free(s); + } + } grpc_slice_buffer_reset_and_unref_internal(&exec_ctx, &outbuf); grpc_exec_ctx_flush(&exec_ctx); } @@ -110,6 +127,7 @@ namespace hpack_encoder_fixtures { class EmptyBatch { public: + static constexpr bool kEnableTrueBinary = false; static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) { return {}; } @@ -117,6 +135,7 @@ class EmptyBatch { class SingleStaticElem { public: + static constexpr bool kEnableTrueBinary = false; static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) { return {GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE}; } @@ -124,6 +143,7 @@ class SingleStaticElem { class SingleInternedElem { public: + static constexpr bool kEnableTrueBinary = false; static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) { return {grpc_mdelem_from_slices( exec_ctx, grpc_slice_intern(grpc_slice_from_static_string("abc")), @@ -131,8 +151,32 @@ class SingleInternedElem { } }; +template <int kLength, bool kTrueBinary> +class SingleInternedBinaryElem { + public: + static constexpr bool kEnableTrueBinary = kTrueBinary; + static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) { + grpc_slice bytes = MakeBytes(); + std::vector<grpc_mdelem> out = {grpc_mdelem_from_slices( + exec_ctx, grpc_slice_intern(grpc_slice_from_static_string("abc-bin")), + grpc_slice_intern(bytes))}; + grpc_slice_unref(bytes); + return out; + } + + private: + static grpc_slice MakeBytes() { + std::vector<char> v; + for (int i = 0; i < kLength; i++) { + v.push_back(static_cast<char>(rand())); + } + return grpc_slice_from_copied_buffer(v.data(), v.size()); + } +}; + class SingleInternedKeyElem { public: + static constexpr bool kEnableTrueBinary = false; static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) { return {grpc_mdelem_from_slices( exec_ctx, grpc_slice_intern(grpc_slice_from_static_string("abc")), @@ -142,6 +186,7 @@ class SingleInternedKeyElem { class SingleNonInternedElem { public: + static constexpr bool kEnableTrueBinary = false; static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) { return {grpc_mdelem_from_slices(exec_ctx, grpc_slice_from_static_string("abc"), @@ -149,8 +194,28 @@ class SingleNonInternedElem { } }; +template <int kLength, bool kTrueBinary> +class SingleNonInternedBinaryElem { + public: + static constexpr bool kEnableTrueBinary = kTrueBinary; + static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) { + return {grpc_mdelem_from_slices( + exec_ctx, grpc_slice_from_static_string("abc-bin"), MakeBytes())}; + } + + private: + static grpc_slice MakeBytes() { + std::vector<char> v; + for (int i = 0; i < kLength; i++) { + v.push_back(static_cast<char>(rand())); + } + return grpc_slice_from_copied_buffer(v.data(), v.size()); + } +}; + class RepresentativeClientInitialMetadata { public: + static constexpr bool kEnableTrueBinary = true; static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) { return { GRPC_MDELEM_SCHEME_HTTP, GRPC_MDELEM_METHOD_POST, @@ -172,6 +237,7 @@ class RepresentativeClientInitialMetadata { class RepresentativeServerInitialMetadata { public: + static constexpr bool kEnableTrueBinary = true; static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) { return {GRPC_MDELEM_STATUS_200, GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC, @@ -181,6 +247,7 @@ class RepresentativeServerInitialMetadata { class RepresentativeServerTrailingMetadata { public: + static constexpr bool kEnableTrueBinary = true; static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) { return {GRPC_MDELEM_GRPC_STATUS_0}; } @@ -195,8 +262,68 @@ BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleInternedKeyElem) ->Args({0, 16384}); BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleInternedElem) ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + SingleInternedBinaryElem<1, false>) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + SingleInternedBinaryElem<3, false>) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + SingleInternedBinaryElem<10, false>) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + SingleInternedBinaryElem<31, false>) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + SingleInternedBinaryElem<100, false>) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + SingleInternedBinaryElem<1, true>) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + SingleInternedBinaryElem<3, true>) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + SingleInternedBinaryElem<10, true>) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + SingleInternedBinaryElem<31, true>) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + SingleInternedBinaryElem<100, true>) + ->Args({0, 16384}); BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleNonInternedElem) ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + SingleNonInternedBinaryElem<1, false>) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + SingleNonInternedBinaryElem<3, false>) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + SingleNonInternedBinaryElem<10, false>) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + SingleNonInternedBinaryElem<31, false>) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + SingleNonInternedBinaryElem<100, false>) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + SingleNonInternedBinaryElem<1, true>) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + SingleNonInternedBinaryElem<3, true>) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + SingleNonInternedBinaryElem<10, true>) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + SingleNonInternedBinaryElem<31, true>) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + SingleNonInternedBinaryElem<100, true>) + ->Args({0, 16384}); // test with a tiny frame size, to highlight continuation costs BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleNonInternedElem) ->Args({0, 1}); @@ -255,6 +382,8 @@ static void BM_HpackParserParseHeader(benchmark::State &state) { } grpc_exec_ctx_flush(&exec_ctx); } + for (auto slice : init_slices) grpc_slice_unref(slice); + for (auto slice : benchmark_slices) grpc_slice_unref(slice); grpc_chttp2_hpack_parser_destroy(&exec_ctx, &p); grpc_exec_ctx_finish(&exec_ctx); track_counters.Finish(state); @@ -262,7 +391,7 @@ static void BM_HpackParserParseHeader(benchmark::State &state) { namespace hpack_parser_fixtures { -static grpc_slice MakeSlice(std::initializer_list<uint8_t> bytes) { +static grpc_slice MakeSlice(std::vector<uint8_t> bytes) { grpc_slice s = grpc_slice_malloc(bytes.size()); uint8_t *p = GRPC_SLICE_START_PTR(s); for (auto b : bytes) { @@ -346,6 +475,88 @@ class NonIndexedElem { } }; +template <int kLength, bool kTrueBinary> +class NonIndexedBinaryElem; + +template <int kLength> +class NonIndexedBinaryElem<kLength, true> { + public: + static std::vector<grpc_slice> GetInitSlices() { return {}; } + static std::vector<grpc_slice> GetBenchmarkSlices() { + std::vector<uint8_t> v = { + 0x00, 0x07, 'a', 'b', 'c', + '-', 'b', 'i', 'n', static_cast<uint8_t>(kLength + 1), + 0}; + for (int i = 0; i < kLength; i++) { + v.push_back(static_cast<uint8_t>(i)); + } + return {MakeSlice(v)}; + } +}; + +template <> +class NonIndexedBinaryElem<1, false> { + public: + static std::vector<grpc_slice> GetInitSlices() { return {}; } + static std::vector<grpc_slice> GetBenchmarkSlices() { + return {MakeSlice( + {0x00, 0x07, 'a', 'b', 'c', '-', 'b', 'i', 'n', 0x82, 0xf7, 0xb3})}; + } +}; + +template <> +class NonIndexedBinaryElem<3, false> { + public: + static std::vector<grpc_slice> GetInitSlices() { return {}; } + static std::vector<grpc_slice> GetBenchmarkSlices() { + return {MakeSlice({0x00, 0x07, 'a', 'b', 'c', '-', 'b', 'i', 'n', 0x84, + 0x7f, 0x4e, 0x29, 0x3f})}; + } +}; + +template <> +class NonIndexedBinaryElem<10, false> { + public: + static std::vector<grpc_slice> GetInitSlices() { return {}; } + static std::vector<grpc_slice> GetBenchmarkSlices() { + return {MakeSlice({0x00, 0x07, 'a', 'b', 'c', '-', 'b', + 'i', 'n', 0x8b, 0x71, 0x0c, 0xa5, 0x81, + 0x73, 0x7b, 0x47, 0x13, 0xe9, 0xf7, 0xe3})}; + } +}; + +template <> +class NonIndexedBinaryElem<31, false> { + public: + static std::vector<grpc_slice> GetInitSlices() { return {}; } + static std::vector<grpc_slice> GetBenchmarkSlices() { + return {MakeSlice({0x00, 0x07, 'a', 'b', 'c', '-', 'b', 'i', 'n', + 0xa3, 0x92, 0x43, 0x7f, 0xbe, 0x7c, 0xea, 0x6f, 0xf3, + 0x3d, 0xa7, 0xa7, 0x67, 0xfb, 0xe2, 0x82, 0xf7, 0xf2, + 0x8f, 0x1f, 0x9d, 0xdf, 0xf1, 0x7e, 0xb3, 0xef, 0xb2, + 0x8f, 0x53, 0x77, 0xce, 0x0c, 0x13, 0xe3, 0xfd, 0x87})}; + } +}; + +template <> +class NonIndexedBinaryElem<100, false> { + public: + static std::vector<grpc_slice> GetInitSlices() { return {}; } + static std::vector<grpc_slice> GetBenchmarkSlices() { + return {MakeSlice( + {0x00, 0x07, 'a', 'b', 'c', '-', 'b', 'i', 'n', 0xeb, 0x1d, 0x4d, + 0xe8, 0x96, 0x8c, 0x14, 0x20, 0x06, 0xc1, 0xc3, 0xdf, 0x6e, 0x1f, 0xef, + 0xde, 0x2f, 0xde, 0xb7, 0xf2, 0xfe, 0x6d, 0xd4, 0xe4, 0x7d, 0xf5, 0x55, + 0x46, 0x52, 0x3d, 0x91, 0xf2, 0xd4, 0x6f, 0xca, 0x34, 0xcd, 0xd9, 0x39, + 0xbd, 0x03, 0x27, 0xe3, 0x9c, 0x74, 0xcc, 0x17, 0x34, 0xed, 0xa6, 0x6a, + 0x77, 0x73, 0x10, 0xcd, 0x8e, 0x4e, 0x5c, 0x7c, 0x72, 0x39, 0xd8, 0xe6, + 0x78, 0x6b, 0xdb, 0xa5, 0xb7, 0xab, 0xe7, 0x46, 0xae, 0x21, 0xab, 0x7f, + 0x01, 0x89, 0x13, 0xd7, 0xca, 0x17, 0x6e, 0xcb, 0xd6, 0x79, 0x71, 0x68, + 0xbf, 0x8a, 0x3f, 0x32, 0xe8, 0xba, 0xf5, 0xbe, 0xb3, 0xbc, 0xde, 0x28, + 0xc7, 0xcf, 0x62, 0x7a, 0x58, 0x2c, 0xcf, 0x4d, 0xe3})}; + } +}; + class RepresentativeClientInitialMetadata { public: static std::vector<grpc_slice> GetInitSlices() { @@ -437,6 +648,16 @@ BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, IndexedSingleInternedElem); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, AddIndexedSingleInternedElem); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, KeyIndexedSingleInternedElem); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedElem); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem<1, false>); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem<3, false>); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem<10, false>); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem<31, false>); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem<100, false>); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem<1, true>); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem<3, true>); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem<10, true>); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem<31, true>); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem<100, true>); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, RepresentativeClientInitialMetadata); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, diff --git a/test/cpp/microbenchmarks/bm_chttp2_transport.cc b/test/cpp/microbenchmarks/bm_chttp2_transport.cc new file mode 100644 index 0000000000..8c5413b5fd --- /dev/null +++ b/test/cpp/microbenchmarks/bm_chttp2_transport.cc @@ -0,0 +1,625 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* Microbenchmarks around CHTTP2 transport operations */ + +#include <grpc++/support/channel_arguments.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> +#include <string.h> +#include <memory> +#include <queue> +#include <sstream> +extern "C" { +#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" +#include "src/core/ext/transport/chttp2/transport/internal.h" +#include "src/core/lib/iomgr/resource_quota.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/transport/static_metadata.h" +} +#include "test/cpp/microbenchmarks/helpers.h" +#include "third_party/benchmark/include/benchmark/benchmark.h" + +auto &force_library_initialization = Library::get(); + +//////////////////////////////////////////////////////////////////////////////// +// Helper classes +// + +class DummyEndpoint : public grpc_endpoint { + public: + DummyEndpoint() { + static const grpc_endpoint_vtable my_vtable = {read, + write, + get_workqueue, + add_to_pollset, + add_to_pollset_set, + shutdown, + destroy, + get_resource_user, + get_peer, + get_fd}; + grpc_endpoint::vtable = &my_vtable; + ru_ = grpc_resource_user_create(Library::get().rq(), "dummy_endpoint"); + } + + void PushInput(grpc_exec_ctx *exec_ctx, grpc_slice slice) { + if (read_cb_ == nullptr) { + GPR_ASSERT(!have_slice_); + buffered_slice_ = slice; + have_slice_ = true; + return; + } + grpc_slice_buffer_add(slices_, slice); + grpc_closure_sched(exec_ctx, read_cb_, GRPC_ERROR_NONE); + read_cb_ = nullptr; + } + + private: + grpc_resource_user *ru_; + grpc_closure *read_cb_ = nullptr; + grpc_slice_buffer *slices_ = nullptr; + bool have_slice_ = false; + grpc_slice buffered_slice_; + + void QueueRead(grpc_exec_ctx *exec_ctx, grpc_slice_buffer *slices, + grpc_closure *cb) { + GPR_ASSERT(read_cb_ == nullptr); + if (have_slice_) { + have_slice_ = false; + grpc_slice_buffer_add(slices, buffered_slice_); + grpc_closure_sched(exec_ctx, cb, GRPC_ERROR_NONE); + return; + } + read_cb_ = cb; + slices_ = slices; + } + + static void read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_slice_buffer *slices, grpc_closure *cb) { + static_cast<DummyEndpoint *>(ep)->QueueRead(exec_ctx, slices, cb); + } + + static void write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_slice_buffer *slices, grpc_closure *cb) { + grpc_closure_sched(exec_ctx, cb, GRPC_ERROR_NONE); + } + + static grpc_workqueue *get_workqueue(grpc_endpoint *ep) { return NULL; } + + static void add_to_pollset(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_pollset *pollset) {} + + static void add_to_pollset_set(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_pollset_set *pollset) {} + + static void shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_error *why) { + grpc_resource_user_shutdown(exec_ctx, + static_cast<DummyEndpoint *>(ep)->ru_); + grpc_closure_sched(exec_ctx, static_cast<DummyEndpoint *>(ep)->read_cb_, + why); + } + + static void destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) { + grpc_resource_user_unref(exec_ctx, static_cast<DummyEndpoint *>(ep)->ru_); + delete static_cast<DummyEndpoint *>(ep); + } + + static grpc_resource_user *get_resource_user(grpc_endpoint *ep) { + return static_cast<DummyEndpoint *>(ep)->ru_; + } + static char *get_peer(grpc_endpoint *ep) { return gpr_strdup("test"); } + static int get_fd(grpc_endpoint *ep) { return 0; } +}; + +class Fixture { + public: + Fixture(const grpc::ChannelArguments &args, bool client) { + grpc_channel_args c_args = args.c_channel_args(); + ep_ = new DummyEndpoint; + t_ = grpc_create_chttp2_transport(exec_ctx(), &c_args, ep_, client); + grpc_chttp2_transport_start_reading(exec_ctx(), t_, NULL); + FlushExecCtx(); + } + + void FlushExecCtx() { grpc_exec_ctx_flush(&exec_ctx_); } + + ~Fixture() { + grpc_transport_destroy(&exec_ctx_, t_); + grpc_exec_ctx_finish(&exec_ctx_); + } + + grpc_chttp2_transport *chttp2_transport() { + return reinterpret_cast<grpc_chttp2_transport *>(t_); + } + grpc_transport *transport() { return t_; } + grpc_exec_ctx *exec_ctx() { return &exec_ctx_; } + + void PushInput(grpc_slice slice) { ep_->PushInput(exec_ctx(), slice); } + + private: + DummyEndpoint *ep_; + grpc_exec_ctx exec_ctx_ = GRPC_EXEC_CTX_INIT; + grpc_transport *t_; +}; + +static void DoNothing(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {} + +class Stream { + public: + Stream(Fixture *f) : f_(f) { + GRPC_STREAM_REF_INIT(&refcount_, 1, DoNothing, nullptr, "test_stream"); + stream_size_ = grpc_transport_stream_size(f->transport()); + stream_ = gpr_malloc(stream_size_); + arena_ = gpr_arena_create(4096); + } + + ~Stream() { + gpr_free(stream_); + gpr_arena_destroy(arena_); + } + + void Init(benchmark::State &state) { + memset(stream_, 0, stream_size_); + if ((state.iterations() & 0xffff) == 0) { + gpr_arena_destroy(arena_); + arena_ = gpr_arena_create(4096); + } + grpc_transport_init_stream(f_->exec_ctx(), f_->transport(), + static_cast<grpc_stream *>(stream_), &refcount_, + NULL, arena_); + } + + void DestroyThen(grpc_closure *closure) { + grpc_transport_destroy_stream(f_->exec_ctx(), f_->transport(), + static_cast<grpc_stream *>(stream_), closure); + } + + void Op(grpc_transport_stream_op_batch *op) { + grpc_transport_perform_stream_op(f_->exec_ctx(), f_->transport(), + static_cast<grpc_stream *>(stream_), op); + } + + grpc_chttp2_stream *chttp2_stream() { + return static_cast<grpc_chttp2_stream *>(stream_); + } + + private: + Fixture *f_; + grpc_stream_refcount refcount_; + gpr_arena *arena_; + size_t stream_size_; + void *stream_; +}; + +class Closure : public grpc_closure { + public: + virtual ~Closure() {} +}; + +template <class F> +std::unique_ptr<Closure> MakeClosure( + F f, grpc_closure_scheduler *sched = grpc_schedule_on_exec_ctx) { + struct C : public Closure { + C(const F &f, grpc_closure_scheduler *sched) : f_(f) { + grpc_closure_init(this, Execute, this, sched); + } + F f_; + static void Execute(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { + static_cast<C *>(arg)->f_(exec_ctx, error); + } + }; + return std::unique_ptr<Closure>(new C(f, sched)); +} + +template <class F> +grpc_closure *MakeOnceClosure( + F f, grpc_closure_scheduler *sched = grpc_schedule_on_exec_ctx) { + struct C : public grpc_closure { + C(const F &f) : f_(f) {} + F f_; + static void Execute(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { + static_cast<C *>(arg)->f_(exec_ctx, error); + delete static_cast<C *>(arg); + } + }; + auto *c = new C{f}; + return grpc_closure_init(c, C::Execute, c, sched); +} + +//////////////////////////////////////////////////////////////////////////////// +// Benchmarks +// + +static void BM_StreamCreateDestroy(benchmark::State &state) { + TrackCounters track_counters; + Fixture f(grpc::ChannelArguments(), true); + Stream s(&f); + std::unique_ptr<Closure> next = + MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) { + if (!state.KeepRunning()) return; + s.Init(state); + s.DestroyThen(next.get()); + }); + grpc_closure_run(f.exec_ctx(), next.get(), GRPC_ERROR_NONE); + f.FlushExecCtx(); + track_counters.Finish(state); +} +BENCHMARK(BM_StreamCreateDestroy); + +class RepresentativeClientInitialMetadata { + public: + static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) { + return { + GRPC_MDELEM_SCHEME_HTTP, GRPC_MDELEM_METHOD_POST, + grpc_mdelem_from_slices( + exec_ctx, GRPC_MDSTR_PATH, + grpc_slice_intern(grpc_slice_from_static_string("/foo/bar"))), + grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_AUTHORITY, + grpc_slice_intern(grpc_slice_from_static_string( + "foo.test.google.fr:1234"))), + GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE_COMMA_GZIP, + GRPC_MDELEM_TE_TRAILERS, + GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC, + grpc_mdelem_from_slices( + exec_ctx, GRPC_MDSTR_USER_AGENT, + grpc_slice_intern(grpc_slice_from_static_string( + "grpc-c/3.0.0-dev (linux; chttp2; green)")))}; + } +}; + +template <class Metadata> +static void BM_StreamCreateSendInitialMetadataDestroy(benchmark::State &state) { + TrackCounters track_counters; + Fixture f(grpc::ChannelArguments(), true); + Stream s(&f); + grpc_transport_stream_op_batch op; + grpc_transport_stream_op_batch_payload op_payload; + std::unique_ptr<Closure> start; + std::unique_ptr<Closure> done; + + auto reset_op = [&]() { + memset(&op, 0, sizeof(op)); + op.payload = &op_payload; + }; + + grpc_metadata_batch b; + grpc_metadata_batch_init(&b); + b.deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + std::vector<grpc_mdelem> elems = Metadata::GetElems(f.exec_ctx()); + std::vector<grpc_linked_mdelem> storage(elems.size()); + for (size_t i = 0; i < elems.size(); i++) { + GPR_ASSERT(GRPC_LOG_IF_ERROR( + "addmd", + grpc_metadata_batch_add_tail(f.exec_ctx(), &b, &storage[i], elems[i]))); + } + + f.FlushExecCtx(); + start = MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) { + if (!state.KeepRunning()) return; + s.Init(state); + reset_op(); + op.on_complete = done.get(); + op.send_initial_metadata = true; + op.payload->send_initial_metadata.send_initial_metadata = &b; + s.Op(&op); + }); + done = MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) { + reset_op(); + op.cancel_stream = true; + op.payload->cancel_stream.cancel_error = GRPC_ERROR_CANCELLED; + s.Op(&op); + s.DestroyThen(start.get()); + }); + grpc_closure_sched(f.exec_ctx(), start.get(), GRPC_ERROR_NONE); + f.FlushExecCtx(); + grpc_metadata_batch_destroy(f.exec_ctx(), &b); + track_counters.Finish(state); +} +BENCHMARK_TEMPLATE(BM_StreamCreateSendInitialMetadataDestroy, + RepresentativeClientInitialMetadata); + +static void BM_TransportEmptyOp(benchmark::State &state) { + TrackCounters track_counters; + Fixture f(grpc::ChannelArguments(), true); + Stream s(&f); + s.Init(state); + grpc_transport_stream_op_batch op; + grpc_transport_stream_op_batch_payload op_payload; + auto reset_op = [&]() { + memset(&op, 0, sizeof(op)); + op.payload = &op_payload; + }; + std::unique_ptr<Closure> c = + MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) { + if (!state.KeepRunning()) return; + reset_op(); + op.on_complete = c.get(); + s.Op(&op); + }); + grpc_closure_sched(f.exec_ctx(), c.get(), GRPC_ERROR_NONE); + f.FlushExecCtx(); + s.DestroyThen( + MakeOnceClosure([](grpc_exec_ctx *exec_ctx, grpc_error *error) {})); + f.FlushExecCtx(); + track_counters.Finish(state); +} +BENCHMARK(BM_TransportEmptyOp); + +static void BM_TransportStreamSend(benchmark::State &state) { + TrackCounters track_counters; + Fixture f(grpc::ChannelArguments(), true); + Stream s(&f); + s.Init(state); + grpc_transport_stream_op_batch op; + grpc_transport_stream_op_batch_payload op_payload; + auto reset_op = [&]() { + memset(&op, 0, sizeof(op)); + op.payload = &op_payload; + }; + grpc_slice_buffer_stream send_stream; + grpc_slice_buffer send_buffer; + grpc_slice_buffer_init(&send_buffer); + grpc_slice_buffer_add(&send_buffer, gpr_slice_malloc(state.range(0))); + memset(GRPC_SLICE_START_PTR(send_buffer.slices[0]), 0, + GRPC_SLICE_LENGTH(send_buffer.slices[0])); + + grpc_metadata_batch b; + grpc_metadata_batch_init(&b); + b.deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + std::vector<grpc_mdelem> elems = + RepresentativeClientInitialMetadata::GetElems(f.exec_ctx()); + std::vector<grpc_linked_mdelem> storage(elems.size()); + for (size_t i = 0; i < elems.size(); i++) { + GPR_ASSERT(GRPC_LOG_IF_ERROR( + "addmd", + grpc_metadata_batch_add_tail(f.exec_ctx(), &b, &storage[i], elems[i]))); + } + + std::unique_ptr<Closure> c = + MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) { + if (!state.KeepRunning()) return; + // force outgoing window to be yuge + s.chttp2_stream()->outgoing_window_delta = 1024 * 1024 * 1024; + f.chttp2_transport()->outgoing_window = 1024 * 1024 * 1024; + grpc_slice_buffer_stream_init(&send_stream, &send_buffer, 0); + reset_op(); + op.on_complete = c.get(); + op.send_message = true; + op.payload->send_message.send_message = &send_stream.base; + s.Op(&op); + }); + + reset_op(); + op.send_initial_metadata = true; + op.payload->send_initial_metadata.send_initial_metadata = &b; + op.on_complete = c.get(); + s.Op(&op); + + f.FlushExecCtx(); + reset_op(); + op.cancel_stream = true; + op.payload->cancel_stream.cancel_error = GRPC_ERROR_CANCELLED; + s.Op(&op); + s.DestroyThen( + MakeOnceClosure([](grpc_exec_ctx *exec_ctx, grpc_error *error) {})); + f.FlushExecCtx(); + track_counters.Finish(state); + grpc_metadata_batch_destroy(f.exec_ctx(), &b); + grpc_slice_buffer_destroy(&send_buffer); +} +BENCHMARK(BM_TransportStreamSend)->Range(0, 128 * 1024 * 1024); + +#define SLICE_FROM_BUFFER(s) grpc_slice_from_static_buffer(s, sizeof(s) - 1) + +static grpc_slice CreateIncomingDataSlice(size_t length, size_t frame_size) { + std::queue<char> unframed; + + unframed.push(static_cast<uint8_t>(0)); + unframed.push(static_cast<uint8_t>(length >> 24)); + unframed.push(static_cast<uint8_t>(length >> 16)); + unframed.push(static_cast<uint8_t>(length >> 8)); + unframed.push(static_cast<uint8_t>(length)); + for (size_t i = 0; i < length; i++) { + unframed.push('a'); + } + + std::vector<char> framed; + while (unframed.size() > frame_size) { + // frame size + framed.push_back(static_cast<uint8_t>(frame_size >> 16)); + framed.push_back(static_cast<uint8_t>(frame_size >> 8)); + framed.push_back(static_cast<uint8_t>(frame_size)); + // data frame + framed.push_back(0); + // no flags + framed.push_back(0); + // stream id + framed.push_back(0); + framed.push_back(0); + framed.push_back(0); + framed.push_back(1); + // frame data + for (size_t i = 0; i < frame_size; i++) { + framed.push_back(unframed.front()); + unframed.pop(); + } + } + + // frame size + framed.push_back(static_cast<uint8_t>(unframed.size() >> 16)); + framed.push_back(static_cast<uint8_t>(unframed.size() >> 8)); + framed.push_back(static_cast<uint8_t>(unframed.size())); + // data frame + framed.push_back(0); + // no flags + framed.push_back(0); + // stream id + framed.push_back(0); + framed.push_back(0); + framed.push_back(0); + framed.push_back(1); + while (!unframed.empty()) { + framed.push_back(unframed.front()); + unframed.pop(); + } + + return grpc_slice_from_copied_buffer(framed.data(), framed.size()); +} + +static void BM_TransportStreamRecv(benchmark::State &state) { + TrackCounters track_counters; + Fixture f(grpc::ChannelArguments(), true); + Stream s(&f); + s.Init(state); + grpc_transport_stream_op_batch_payload op_payload; + grpc_transport_stream_op_batch op; + grpc_byte_stream *recv_stream; + grpc_slice incoming_data = CreateIncomingDataSlice(state.range(0), 16384); + + auto reset_op = [&]() { + memset(&op, 0, sizeof(op)); + op.payload = &op_payload; + }; + + grpc_metadata_batch b; + grpc_metadata_batch_init(&b); + grpc_metadata_batch b_recv; + grpc_metadata_batch_init(&b_recv); + b.deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + std::vector<grpc_mdelem> elems = + RepresentativeClientInitialMetadata::GetElems(f.exec_ctx()); + std::vector<grpc_linked_mdelem> storage(elems.size()); + for (size_t i = 0; i < elems.size(); i++) { + GPR_ASSERT(GRPC_LOG_IF_ERROR( + "addmd", + grpc_metadata_batch_add_tail(f.exec_ctx(), &b, &storage[i], elems[i]))); + } + + std::unique_ptr<Closure> do_nothing = + MakeClosure([](grpc_exec_ctx *exec_ctx, grpc_error *error) {}); + + uint32_t received; + + std::unique_ptr<Closure> drain_start; + std::unique_ptr<Closure> drain; + std::unique_ptr<Closure> drain_continue; + grpc_slice recv_slice; + + std::unique_ptr<Closure> c = + MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) { + if (!state.KeepRunning()) return; + // force outgoing window to be yuge + s.chttp2_stream()->incoming_window_delta = 1024 * 1024 * 1024; + f.chttp2_transport()->incoming_window = 1024 * 1024 * 1024; + received = 0; + reset_op(); + op.on_complete = do_nothing.get(); + op.recv_message = true; + op.payload->recv_message.recv_message = &recv_stream; + op.payload->recv_message.recv_message_ready = drain_start.get(); + s.Op(&op); + f.PushInput(grpc_slice_ref(incoming_data)); + }); + + drain_start = MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) { + if (recv_stream == NULL) { + GPR_ASSERT(!state.KeepRunning()); + return; + } + grpc_closure_run(exec_ctx, drain.get(), GRPC_ERROR_NONE); + }); + + drain = MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) { + do { + if (received == recv_stream->length) { + grpc_byte_stream_destroy(exec_ctx, recv_stream); + grpc_closure_sched(exec_ctx, c.get(), GRPC_ERROR_NONE); + return; + } + } while (grpc_byte_stream_next(exec_ctx, recv_stream, + recv_stream->length - received, + drain_continue.get()) && + GRPC_ERROR_NONE == + grpc_byte_stream_pull(exec_ctx, recv_stream, &recv_slice) && + (received += GRPC_SLICE_LENGTH(recv_slice), + grpc_slice_unref_internal(exec_ctx, recv_slice), true)); + }); + + drain_continue = MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) { + grpc_byte_stream_pull(exec_ctx, recv_stream, &recv_slice); + received += GRPC_SLICE_LENGTH(recv_slice); + grpc_slice_unref_internal(exec_ctx, recv_slice); + grpc_closure_run(exec_ctx, drain.get(), GRPC_ERROR_NONE); + }); + + reset_op(); + op.send_initial_metadata = true; + op.payload->send_initial_metadata.send_initial_metadata = &b; + op.recv_initial_metadata = true; + op.payload->recv_initial_metadata.recv_initial_metadata = &b_recv; + op.payload->recv_initial_metadata.recv_initial_metadata_ready = + do_nothing.get(); + op.on_complete = c.get(); + s.Op(&op); + f.PushInput(SLICE_FROM_BUFFER( + "\x00\x00\x00\x04\x00\x00\x00\x00\x00" + // Generated using: + // tools/codegen/core/gen_header_frame.py < + // test/cpp/microbenchmarks/representative_server_initial_metadata.headers + "\x00\x00X\x01\x04\x00\x00\x00\x01" + "\x10\x07:status\x03" + "200" + "\x10\x0c" + "content-type\x10" + "application/grpc" + "\x10\x14grpc-accept-encoding\x15identity,deflate,gzip")); + + f.FlushExecCtx(); + reset_op(); + op.cancel_stream = true; + op.payload->cancel_stream.cancel_error = GRPC_ERROR_CANCELLED; + s.Op(&op); + s.DestroyThen( + MakeOnceClosure([](grpc_exec_ctx *exec_ctx, grpc_error *error) {})); + f.FlushExecCtx(); + track_counters.Finish(state); + grpc_metadata_batch_destroy(f.exec_ctx(), &b); + grpc_metadata_batch_destroy(f.exec_ctx(), &b_recv); + grpc_slice_unref(incoming_data); +} +BENCHMARK(BM_TransportStreamRecv)->Range(0, 128 * 1024 * 1024); + +BENCHMARK_MAIN(); diff --git a/test/cpp/microbenchmarks/bm_closure.cc b/test/cpp/microbenchmarks/bm_closure.cc index 28a385b6c1..d52fe4ee30 100644 --- a/test/cpp/microbenchmarks/bm_closure.cc +++ b/test/cpp/microbenchmarks/bm_closure.cc @@ -33,7 +33,9 @@ /* Test various closure related operations */ +#include <benchmark/benchmark.h> #include <grpc/grpc.h> +#include <sstream> extern "C" { #include "src/core/lib/iomgr/closure.h" @@ -43,7 +45,6 @@ extern "C" { } #include "test/cpp/microbenchmarks/helpers.h" -#include "third_party/benchmark/include/benchmark/benchmark.h" auto& force_library_initialization = Library::get(); diff --git a/test/cpp/microbenchmarks/bm_cq.cc b/test/cpp/microbenchmarks/bm_cq.cc index 91e6a85101..8b26bf977c 100644 --- a/test/cpp/microbenchmarks/bm_cq.cc +++ b/test/cpp/microbenchmarks/bm_cq.cc @@ -34,12 +34,11 @@ /* This benchmark exists to ensure that the benchmark integration is * working */ +#include <benchmark/benchmark.h> #include <grpc++/completion_queue.h> #include <grpc++/impl/grpc_library.h> #include <grpc/grpc.h> - #include "test/cpp/microbenchmarks/helpers.h" -#include "third_party/benchmark/include/benchmark/benchmark.h" extern "C" { #include "src/core/lib/surface/completion_queue.h" @@ -59,10 +58,24 @@ static void BM_CreateDestroyCpp(benchmark::State& state) { } BENCHMARK(BM_CreateDestroyCpp); +/* Create cq using a different constructor */ +static void BM_CreateDestroyCpp2(benchmark::State& state) { + TrackCounters track_counters; + while (state.KeepRunning()) { + grpc_completion_queue* core_cq = + grpc_completion_queue_create_for_next(NULL); + CompletionQueue cq(core_cq); + } + track_counters.Finish(state); +} +BENCHMARK(BM_CreateDestroyCpp2); + static void BM_CreateDestroyCore(benchmark::State& state) { TrackCounters track_counters; while (state.KeepRunning()) { - grpc_completion_queue_destroy(grpc_completion_queue_create(NULL)); + // TODO: sreek Templatize this benchmark and pass completion type and + // polling type as parameters + grpc_completion_queue_destroy(grpc_completion_queue_create_for_next(NULL)); } track_counters.Finish(state); } @@ -98,7 +111,8 @@ BENCHMARK(BM_Pass1Cpp); static void BM_Pass1Core(benchmark::State& state) { TrackCounters track_counters; - grpc_completion_queue* cq = grpc_completion_queue_create(NULL); + // TODO: sreek Templatize this benchmark and pass polling_type as a param + grpc_completion_queue* cq = grpc_completion_queue_create_for_next(NULL); gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); while (state.KeepRunning()) { grpc_cq_completion completion; @@ -116,7 +130,8 @@ BENCHMARK(BM_Pass1Core); static void BM_Pluck1Core(benchmark::State& state) { TrackCounters track_counters; - grpc_completion_queue* cq = grpc_completion_queue_create(NULL); + // TODO: sreek Templatize this benchmark and pass polling_type as a param + grpc_completion_queue* cq = grpc_completion_queue_create_for_pluck(NULL); gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); while (state.KeepRunning()) { grpc_cq_completion completion; @@ -134,7 +149,8 @@ BENCHMARK(BM_Pluck1Core); static void BM_EmptyCore(benchmark::State& state) { TrackCounters track_counters; - grpc_completion_queue* cq = grpc_completion_queue_create(NULL); + // TODO: sreek Templatize this benchmark and pass polling_type as a param + grpc_completion_queue* cq = grpc_completion_queue_create_for_next(NULL); gpr_timespec deadline = gpr_inf_past(GPR_CLOCK_MONOTONIC); while (state.KeepRunning()) { grpc_completion_queue_next(cq, deadline, NULL); diff --git a/test/cpp/microbenchmarks/bm_cq_multiple_threads.cc b/test/cpp/microbenchmarks/bm_cq_multiple_threads.cc new file mode 100644 index 0000000000..9d7f65d292 --- /dev/null +++ b/test/cpp/microbenchmarks/bm_cq_multiple_threads.cc @@ -0,0 +1,160 @@ +/* + * + * Copyright 2017, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <benchmark/benchmark.h> +#include <string.h> +#include <atomic> + +#include <grpc/grpc.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include "test/cpp/microbenchmarks/helpers.h" + +extern "C" { +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/port.h" +#include "src/core/lib/surface/completion_queue.h" +} + +struct grpc_pollset { + gpr_mu mu; +}; + +namespace grpc { +namespace testing { + +static void* g_tag = (void*)(intptr_t)10; // Some random number +static grpc_completion_queue* g_cq; +static grpc_event_engine_vtable g_vtable; + +static void pollset_shutdown(grpc_exec_ctx* exec_ctx, grpc_pollset* ps, + grpc_closure* closure) { + grpc_closure_sched(exec_ctx, closure, GRPC_ERROR_NONE); +} + +static void pollset_init(grpc_pollset* ps, gpr_mu** mu) { + gpr_mu_init(&ps->mu); + *mu = &ps->mu; +} + +static void pollset_destroy(grpc_pollset* ps) { gpr_mu_destroy(&ps->mu); } + +static grpc_error* pollset_kick(grpc_pollset* p, grpc_pollset_worker* worker) { + return GRPC_ERROR_NONE; +} + +/* Callback when the tag is dequeued from the completion queue. Does nothing */ +static void cq_done_cb(grpc_exec_ctx* exec_ctx, void* done_arg, + grpc_cq_completion* cq_completion) { + gpr_free(cq_completion); +} + +/* Queues a completion tag. ZERO polling overhead */ +static grpc_error* pollset_work(grpc_exec_ctx* exec_ctx, grpc_pollset* ps, + grpc_pollset_worker** worker, gpr_timespec now, + gpr_timespec deadline) { + gpr_mu_unlock(&ps->mu); + grpc_cq_begin_op(g_cq, g_tag); + grpc_cq_end_op(exec_ctx, g_cq, g_tag, GRPC_ERROR_NONE, cq_done_cb, NULL, + (grpc_cq_completion*)gpr_malloc(sizeof(grpc_cq_completion))); + grpc_exec_ctx_flush(exec_ctx); + gpr_mu_lock(&ps->mu); + return GRPC_ERROR_NONE; +} + +static void init_engine_vtable() { + memset(&g_vtable, 0, sizeof(g_vtable)); + + g_vtable.pollset_size = sizeof(grpc_pollset); + g_vtable.pollset_init = pollset_init; + g_vtable.pollset_shutdown = pollset_shutdown; + g_vtable.pollset_destroy = pollset_destroy; + g_vtable.pollset_work = pollset_work; + g_vtable.pollset_kick = pollset_kick; +} + +static void setup() { + grpc_init(); + init_engine_vtable(); + grpc_set_event_engine_test_only(&g_vtable); + + g_cq = grpc_completion_queue_create_for_next(NULL); +} + +static void teardown() { + grpc_completion_queue_shutdown(g_cq); + grpc_completion_queue_destroy(g_cq); +} + +/* A few notes about Multi-threaded benchmarks: + + Setup: + The benchmark framework ensures that none of the threads proceed beyond the + state.KeepRunning() call unless all the threads have called state.keepRunning + atleast once. So it is safe to do the initialization in one of the threads + before state.KeepRunning() is called. + + Teardown: + The benchmark framework also ensures that no thread is running the benchmark + code (i.e the code between two successive calls of state.KeepRunning()) if + state.KeepRunning() returns false. So it is safe to do the teardown in one + of the threads after state.keepRunning() returns false. +*/ +static void BM_Cq_Throughput(benchmark::State& state) { + TrackCounters track_counters; + gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + + if (state.thread_index == 0) { + setup(); + } + + while (state.KeepRunning()) { + GPR_ASSERT(grpc_completion_queue_next(g_cq, deadline, NULL).type == + GRPC_OP_COMPLETE); + } + + state.SetItemsProcessed(state.iterations()); + + if (state.thread_index == 0) { + teardown(); + } + + track_counters.Finish(state); +} + +BENCHMARK(BM_Cq_Throughput)->ThreadRange(1, 16)->UseRealTime(); + +} // namespace testing +} // namespace grpc + +BENCHMARK_MAIN(); diff --git a/test/cpp/microbenchmarks/bm_error.cc b/test/cpp/microbenchmarks/bm_error.cc index c4f6aa19d5..ea9777bbe6 100644 --- a/test/cpp/microbenchmarks/bm_error.cc +++ b/test/cpp/microbenchmarks/bm_error.cc @@ -33,6 +33,7 @@ /* Test various operations on grpc_error */ +#include <benchmark/benchmark.h> #include <memory> extern "C" { @@ -41,7 +42,6 @@ extern "C" { } #include "test/cpp/microbenchmarks/helpers.h" -#include "third_party/benchmark/include/benchmark/benchmark.h" auto& force_library_initialization = Library::get(); @@ -51,21 +51,30 @@ class ErrorDeleter { }; typedef std::unique_ptr<grpc_error, ErrorDeleter> ErrorPtr; -static void BM_ErrorCreate(benchmark::State& state) { +static void BM_ErrorCreateFromStatic(benchmark::State& state) { TrackCounters track_counters; while (state.KeepRunning()) { - GRPC_ERROR_UNREF(GRPC_ERROR_CREATE("Error")); + GRPC_ERROR_UNREF(GRPC_ERROR_CREATE_FROM_STATIC_STRING("Error")); } track_counters.Finish(state); } -BENCHMARK(BM_ErrorCreate); +BENCHMARK(BM_ErrorCreateFromStatic); + +static void BM_ErrorCreateFromCopied(benchmark::State& state) { + TrackCounters track_counters; + while (state.KeepRunning()) { + GRPC_ERROR_UNREF(GRPC_ERROR_CREATE_FROM_COPIED_STRING("Error not inline")); + } + track_counters.Finish(state); +} +BENCHMARK(BM_ErrorCreateFromCopied); static void BM_ErrorCreateAndSetStatus(benchmark::State& state) { TrackCounters track_counters; while (state.KeepRunning()) { - GRPC_ERROR_UNREF(grpc_error_set_int(GRPC_ERROR_CREATE("Error"), - GRPC_ERROR_INT_GRPC_STATUS, - GRPC_STATUS_ABORTED)); + GRPC_ERROR_UNREF( + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("Error"), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_ABORTED)); } track_counters.Finish(state); } @@ -75,9 +84,10 @@ static void BM_ErrorCreateAndSetIntAndStr(benchmark::State& state) { TrackCounters track_counters; while (state.KeepRunning()) { GRPC_ERROR_UNREF(grpc_error_set_str( - grpc_error_set_int(GRPC_ERROR_CREATE("GOAWAY received"), - GRPC_ERROR_INT_HTTP2_ERROR, (intptr_t)0), - GRPC_ERROR_STR_RAW_BYTES, "raw bytes")); + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("GOAWAY received"), + GRPC_ERROR_INT_HTTP2_ERROR, (intptr_t)0), + GRPC_ERROR_STR_RAW_BYTES, grpc_slice_from_static_string("raw bytes"))); } track_counters.Finish(state); } @@ -85,7 +95,7 @@ BENCHMARK(BM_ErrorCreateAndSetIntAndStr); static void BM_ErrorCreateAndSetIntLoop(benchmark::State& state) { TrackCounters track_counters; - grpc_error* error = GRPC_ERROR_CREATE("Error"); + grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Error"); int n = 0; while (state.KeepRunning()) { error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, n++); @@ -97,10 +107,11 @@ BENCHMARK(BM_ErrorCreateAndSetIntLoop); static void BM_ErrorCreateAndSetStrLoop(benchmark::State& state) { TrackCounters track_counters; - grpc_error* error = GRPC_ERROR_CREATE("Error"); + grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Error"); const char* str = "hello"; while (state.KeepRunning()) { - error = grpc_error_set_str(error, GRPC_ERROR_STR_GRPC_MESSAGE, str); + error = grpc_error_set_str(error, GRPC_ERROR_STR_GRPC_MESSAGE, + grpc_slice_from_static_string(str)); } GRPC_ERROR_UNREF(error); track_counters.Finish(state); @@ -109,7 +120,7 @@ BENCHMARK(BM_ErrorCreateAndSetStrLoop); static void BM_ErrorRefUnref(benchmark::State& state) { TrackCounters track_counters; - grpc_error* error = GRPC_ERROR_CREATE("Error"); + grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Error"); while (state.KeepRunning()) { GRPC_ERROR_UNREF(GRPC_ERROR_REF(error)); } @@ -138,8 +149,8 @@ BENCHMARK(BM_ErrorGetIntFromNoError); static void BM_ErrorGetMissingInt(benchmark::State& state) { TrackCounters track_counters; - ErrorPtr error( - grpc_error_set_int(GRPC_ERROR_CREATE("Error"), GRPC_ERROR_INT_INDEX, 1)); + ErrorPtr error(grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Error"), GRPC_ERROR_INT_INDEX, 1)); while (state.KeepRunning()) { intptr_t value; grpc_error_get_int(error.get(), GRPC_ERROR_INT_OFFSET, &value); @@ -150,8 +161,8 @@ BENCHMARK(BM_ErrorGetMissingInt); static void BM_ErrorGetPresentInt(benchmark::State& state) { TrackCounters track_counters; - ErrorPtr error( - grpc_error_set_int(GRPC_ERROR_CREATE("Error"), GRPC_ERROR_INT_OFFSET, 1)); + ErrorPtr error(grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Error"), GRPC_ERROR_INT_OFFSET, 1)); while (state.KeepRunning()) { intptr_t value; grpc_error_get_int(error.get(), GRPC_ERROR_INT_OFFSET, &value); @@ -186,7 +197,7 @@ class SimpleError { private: const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC); - ErrorPtr error_{GRPC_ERROR_CREATE("Error")}; + ErrorPtr error_{GRPC_ERROR_CREATE_FROM_STATIC_STRING("Error")}; }; class ErrorWithGrpcStatus { @@ -196,9 +207,9 @@ class ErrorWithGrpcStatus { private: const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC); - ErrorPtr error_{grpc_error_set_int(GRPC_ERROR_CREATE("Error"), - GRPC_ERROR_INT_GRPC_STATUS, - GRPC_STATUS_UNIMPLEMENTED)}; + ErrorPtr error_{grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Error"), GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_UNIMPLEMENTED)}; }; class ErrorWithHttpError { @@ -208,9 +219,9 @@ class ErrorWithHttpError { private: const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC); - ErrorPtr error_{grpc_error_set_int(GRPC_ERROR_CREATE("Error"), - GRPC_ERROR_INT_HTTP2_ERROR, - GRPC_HTTP2_COMPRESSION_ERROR)}; + ErrorPtr error_{grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Error"), GRPC_ERROR_INT_HTTP2_ERROR, + GRPC_HTTP2_COMPRESSION_ERROR)}; }; class ErrorWithNestedGrpcStatus { @@ -220,11 +231,12 @@ class ErrorWithNestedGrpcStatus { private: const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC); - ErrorPtr nested_error_{grpc_error_set_int(GRPC_ERROR_CREATE("Error"), - GRPC_ERROR_INT_GRPC_STATUS, - GRPC_STATUS_UNIMPLEMENTED)}; + ErrorPtr nested_error_{grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Error"), GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_UNIMPLEMENTED)}; grpc_error* nested_errors_[1] = {nested_error_.get()}; - ErrorPtr error_{GRPC_ERROR_CREATE_REFERENCING("Error", nested_errors_, 1)}; + ErrorPtr error_{GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Error", nested_errors_, 1)}; }; template <class Fixture> @@ -253,8 +265,8 @@ static void BM_ErrorGetStatus(benchmark::State& state) { Fixture fixture; while (state.KeepRunning()) { grpc_status_code status; - const char* msg; - grpc_error_get_status(fixture.error(), fixture.deadline(), &status, &msg, + grpc_slice slice; + grpc_error_get_status(fixture.error(), fixture.deadline(), &status, &slice, NULL); } track_counters.Finish(state); diff --git a/test/cpp/microbenchmarks/bm_fullstack_streaming_ping_pong.cc b/test/cpp/microbenchmarks/bm_fullstack_streaming_ping_pong.cc index dc0e7d769a..fd2210c474 100644 --- a/test/cpp/microbenchmarks/bm_fullstack_streaming_ping_pong.cc +++ b/test/cpp/microbenchmarks/bm_fullstack_streaming_ping_pong.cc @@ -33,14 +33,13 @@ /* Benchmark gRPC end2end in various configurations */ +#include <benchmark/benchmark.h> #include <sstream> - #include "src/core/lib/profiling/timers.h" #include "src/cpp/client/create_channel_internal.h" #include "src/proto/grpc/testing/echo.grpc.pb.h" #include "test/cpp/microbenchmarks/fullstack_context_mutators.h" #include "test/cpp/microbenchmarks/fullstack_fixtures.h" -#include "third_party/benchmark/include/benchmark/benchmark.h" namespace grpc { namespace testing { @@ -54,86 +53,141 @@ auto& force_library_initialization = Library::get(); static void* tag(intptr_t x) { return reinterpret_cast<void*>(x); } -template <class Fixture> -static void BM_PumpStreamClientToServer(benchmark::State& state) { +// Repeatedly makes Streaming Bidi calls (exchanging a configurable number of +// messages in each call) in a loop on a single channel +// +// First parmeter (i.e state.range(0)): Message size (in bytes) to use +// Second parameter (i.e state.range(1)): Number of ping pong messages. +// Note: One ping-pong means two messages (one from client to server and +// the other from server to client): +template <class Fixture, class ClientContextMutator, class ServerContextMutator> +static void BM_StreamingPingPong(benchmark::State& state) { + const int msg_size = state.range(0); + const int max_ping_pongs = state.range(1); + EchoTestService::AsyncService service; std::unique_ptr<Fixture> fixture(new Fixture(&service)); { + EchoResponse send_response; + EchoResponse recv_response; EchoRequest send_request; EchoRequest recv_request; - if (state.range(0) > 0) { - send_request.set_message(std::string(state.range(0), 'a')); + + if (msg_size > 0) { + send_request.set_message(std::string(msg_size, 'a')); + send_response.set_message(std::string(msg_size, 'b')); } - Status recv_status; - ServerContext svr_ctx; - ServerAsyncReaderWriter<EchoResponse, EchoRequest> response_rw(&svr_ctx); - service.RequestBidiStream(&svr_ctx, &response_rw, fixture->cq(), - fixture->cq(), tag(0)); + std::unique_ptr<EchoTestService::Stub> stub( EchoTestService::NewStub(fixture->channel())); - ClientContext cli_ctx; - auto request_rw = stub->AsyncBidiStream(&cli_ctx, fixture->cq(), tag(1)); - int need_tags = (1 << 0) | (1 << 1); - void* t; - bool ok; - while (need_tags) { - GPR_ASSERT(fixture->cq()->Next(&t, &ok)); - GPR_ASSERT(ok); - int i = (int)(intptr_t)t; - GPR_ASSERT(need_tags & (1 << i)); - need_tags &= ~(1 << i); - } - response_rw.Read(&recv_request, tag(0)); + while (state.KeepRunning()) { - GPR_TIMER_SCOPE("BenchmarkCycle", 0); - request_rw->Write(send_request, tag(1)); - while (true) { + ServerContext svr_ctx; + ServerContextMutator svr_ctx_mut(&svr_ctx); + ServerAsyncReaderWriter<EchoResponse, EchoRequest> response_rw(&svr_ctx); + service.RequestBidiStream(&svr_ctx, &response_rw, fixture->cq(), + fixture->cq(), tag(0)); + + ClientContext cli_ctx; + ClientContextMutator cli_ctx_mut(&cli_ctx); + auto request_rw = stub->AsyncBidiStream(&cli_ctx, fixture->cq(), tag(1)); + + // Establish async stream between client side and server side + void* t; + bool ok; + int need_tags = (1 << 0) | (1 << 1); + while (need_tags) { GPR_ASSERT(fixture->cq()->Next(&t, &ok)); - if (t == tag(0)) { - response_rw.Read(&recv_request, tag(0)); - } else if (t == tag(1)) { - break; - } else { - GPR_ASSERT(false); + GPR_ASSERT(ok); + int i = (int)(intptr_t)t; + GPR_ASSERT(need_tags & (1 << i)); + need_tags &= ~(1 << i); + } + + // Send 'max_ping_pongs' number of ping pong messages + int ping_pong_cnt = 0; + while (ping_pong_cnt < max_ping_pongs) { + request_rw->Write(send_request, tag(0)); // Start client send + response_rw.Read(&recv_request, tag(1)); // Start server recv + request_rw->Read(&recv_response, tag(2)); // Start client recv + + need_tags = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3); + while (need_tags) { + GPR_ASSERT(fixture->cq()->Next(&t, &ok)); + GPR_ASSERT(ok); + int i = (int)(intptr_t)t; + + // If server recv is complete, start the server send operation + if (i == 1) { + response_rw.Write(send_response, tag(3)); + } + + GPR_ASSERT(need_tags & (1 << i)); + need_tags &= ~(1 << i); } + + ping_pong_cnt++; } - } - request_rw->WritesDone(tag(1)); - need_tags = (1 << 0) | (1 << 1); - while (need_tags) { - GPR_ASSERT(fixture->cq()->Next(&t, &ok)); - int i = (int)(intptr_t)t; - GPR_ASSERT(need_tags & (1 << i)); - need_tags &= ~(1 << i); + + request_rw->WritesDone(tag(0)); + response_rw.Finish(Status::OK, tag(1)); + + Status recv_status; + request_rw->Finish(&recv_status, tag(2)); + + need_tags = (1 << 0) | (1 << 1) | (1 << 2); + while (need_tags) { + GPR_ASSERT(fixture->cq()->Next(&t, &ok)); + int i = (int)(intptr_t)t; + GPR_ASSERT(need_tags & (1 << i)); + need_tags &= ~(1 << i); + } + + GPR_ASSERT(recv_status.ok()); } } + fixture->Finish(state); fixture.reset(); - state.SetBytesProcessed(state.range(0) * state.iterations()); + state.SetBytesProcessed(msg_size * state.iterations() * max_ping_pongs * 2); } -template <class Fixture> -static void BM_PumpStreamServerToClient(benchmark::State& state) { +// Repeatedly sends ping pong messages in a single streaming Bidi call in a loop +// First parmeter (i.e state.range(0)): Message size (in bytes) to use +template <class Fixture, class ClientContextMutator, class ServerContextMutator> +static void BM_StreamingPingPongMsgs(benchmark::State& state) { + const int msg_size = state.range(0); + EchoTestService::AsyncService service; std::unique_ptr<Fixture> fixture(new Fixture(&service)); { EchoResponse send_response; EchoResponse recv_response; - if (state.range(0) > 0) { - send_response.set_message(std::string(state.range(0), 'a')); + EchoRequest send_request; + EchoRequest recv_request; + + if (msg_size > 0) { + send_request.set_message(std::string(msg_size, 'a')); + send_response.set_message(std::string(msg_size, 'b')); } - Status recv_status; + + std::unique_ptr<EchoTestService::Stub> stub( + EchoTestService::NewStub(fixture->channel())); + ServerContext svr_ctx; + ServerContextMutator svr_ctx_mut(&svr_ctx); ServerAsyncReaderWriter<EchoResponse, EchoRequest> response_rw(&svr_ctx); service.RequestBidiStream(&svr_ctx, &response_rw, fixture->cq(), fixture->cq(), tag(0)); - std::unique_ptr<EchoTestService::Stub> stub( - EchoTestService::NewStub(fixture->channel())); + ClientContext cli_ctx; + ClientContextMutator cli_ctx_mut(&cli_ctx); auto request_rw = stub->AsyncBidiStream(&cli_ctx, fixture->cq(), tag(1)); - int need_tags = (1 << 0) | (1 << 1); + + // Establish async stream between client side and server side void* t; bool ok; + int need_tags = (1 << 0) | (1 << 1); while (need_tags) { GPR_ASSERT(fixture->cq()->Next(&t, &ok)); GPR_ASSERT(ok); @@ -141,56 +195,286 @@ static void BM_PumpStreamServerToClient(benchmark::State& state) { GPR_ASSERT(need_tags & (1 << i)); need_tags &= ~(1 << i); } - request_rw->Read(&recv_response, tag(0)); + while (state.KeepRunning()) { GPR_TIMER_SCOPE("BenchmarkCycle", 0); - response_rw.Write(send_response, tag(1)); - while (true) { + request_rw->Write(send_request, tag(0)); // Start client send + response_rw.Read(&recv_request, tag(1)); // Start server recv + request_rw->Read(&recv_response, tag(2)); // Start client recv + + need_tags = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3); + while (need_tags) { GPR_ASSERT(fixture->cq()->Next(&t, &ok)); - if (t == tag(0)) { - request_rw->Read(&recv_response, tag(0)); - } else if (t == tag(1)) { - break; - } else { - GPR_ASSERT(false); + GPR_ASSERT(ok); + int i = (int)(intptr_t)t; + + // If server recv is complete, start the server send operation + if (i == 1) { + response_rw.Write(send_response, tag(3)); } + + GPR_ASSERT(need_tags & (1 << i)); + need_tags &= ~(1 << i); } } + + request_rw->WritesDone(tag(0)); response_rw.Finish(Status::OK, tag(1)); - need_tags = (1 << 0) | (1 << 1); + Status recv_status; + request_rw->Finish(&recv_status, tag(2)); + + need_tags = (1 << 0) | (1 << 1) | (1 << 2); while (need_tags) { GPR_ASSERT(fixture->cq()->Next(&t, &ok)); int i = (int)(intptr_t)t; GPR_ASSERT(need_tags & (1 << i)); need_tags &= ~(1 << i); } + + GPR_ASSERT(recv_status.ok()); } + + fixture->Finish(state); + fixture.reset(); + state.SetBytesProcessed(msg_size * state.iterations() * 2); +} + +// Repeatedly makes Streaming Bidi calls (exchanging a configurable number of +// messages in each call) in a loop on a single channel. Different from +// BM_StreamingPingPong we are using stream coalescing api, e.g. WriteLast, +// WriteAndFinish, set_initial_metadata_corked. These apis aim at saving +// sendmsg syscalls for streaming by coalescing 1. initial metadata with first +// message; 2. final streaming message with trailing metadata. +// +// First parmeter (i.e state.range(0)): Message size (in bytes) to use +// Second parameter (i.e state.range(1)): Number of ping pong messages. +// Note: One ping-pong means two messages (one from client to server and +// the other from server to client): +// Third parameter (i.e state.range(2)): Switch between using WriteAndFinish +// API and WriteLast API for server. +template <class Fixture, class ClientContextMutator, class ServerContextMutator> +static void BM_StreamingPingPongWithCoalescingApi(benchmark::State& state) { + const int msg_size = state.range(0); + const int max_ping_pongs = state.range(1); + // This options is used to test out server API: WriteLast and WriteAndFinish + // respectively, since we can not use both of them on server side at the same + // time. Value 1 means we are testing out the WriteAndFinish API, and + // otherwise we are testing out the WriteLast API. + const int write_and_finish = state.range(2); + + EchoTestService::AsyncService service; + std::unique_ptr<Fixture> fixture(new Fixture(&service)); + { + EchoResponse send_response; + EchoResponse recv_response; + EchoRequest send_request; + EchoRequest recv_request; + + if (msg_size > 0) { + send_request.set_message(std::string(msg_size, 'a')); + send_response.set_message(std::string(msg_size, 'b')); + } + + std::unique_ptr<EchoTestService::Stub> stub( + EchoTestService::NewStub(fixture->channel())); + + while (state.KeepRunning()) { + ServerContext svr_ctx; + ServerContextMutator svr_ctx_mut(&svr_ctx); + ServerAsyncReaderWriter<EchoResponse, EchoRequest> response_rw(&svr_ctx); + service.RequestBidiStream(&svr_ctx, &response_rw, fixture->cq(), + fixture->cq(), tag(0)); + + ClientContext cli_ctx; + ClientContextMutator cli_ctx_mut(&cli_ctx); + cli_ctx.set_initial_metadata_corked(true); + // tag:1 here will never comes up, since we are not performing any op due + // to initial metadata coalescing. + auto request_rw = stub->AsyncBidiStream(&cli_ctx, fixture->cq(), tag(1)); + + void* t; + bool ok; + int need_tags; + + // Send 'max_ping_pongs' number of ping pong messages + int ping_pong_cnt = 0; + while (ping_pong_cnt < max_ping_pongs) { + if (ping_pong_cnt == max_ping_pongs - 1) { + request_rw->WriteLast(send_request, WriteOptions(), tag(2)); + } else { + request_rw->Write(send_request, tag(2)); // Start client send + } + + need_tags = (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5); + + if (ping_pong_cnt == 0) { + // wait for the server call structure (call_hook, etc.) to be + // initialized (async stream between client side and server side + // established). It is necessary when client init metadata is + // coalesced + GPR_ASSERT(fixture->cq()->Next(&t, &ok)); + while ((int)(intptr_t)t != 0) { + // In some cases tag:2 comes before tag:0 (write tag comes out + // first), this while loop is to make sure get tag:0. + int i = (int)(intptr_t)t; + GPR_ASSERT(need_tags & (1 << i)); + need_tags &= ~(1 << i); + GPR_ASSERT(fixture->cq()->Next(&t, &ok)); + } + } + + response_rw.Read(&recv_request, tag(3)); // Start server recv + request_rw->Read(&recv_response, tag(4)); // Start client recv + + while (need_tags) { + GPR_ASSERT(fixture->cq()->Next(&t, &ok)); + GPR_ASSERT(ok); + int i = (int)(intptr_t)t; + + // If server recv is complete, start the server send operation + if (i == 3) { + if (ping_pong_cnt == max_ping_pongs - 1) { + if (write_and_finish == 1) { + response_rw.WriteAndFinish(send_response, WriteOptions(), + Status::OK, tag(5)); + } else { + response_rw.WriteLast(send_response, WriteOptions(), tag(5)); + // WriteLast buffers the write, so neither server write op nor + // client read op will finish inside the while loop. + need_tags &= ~(1 << 4); + need_tags &= ~(1 << 5); + } + } else { + response_rw.Write(send_response, tag(5)); + } + } + + GPR_ASSERT(need_tags & (1 << i)); + need_tags &= ~(1 << i); + } + + ping_pong_cnt++; + } + + if (max_ping_pongs == 0) { + need_tags = (1 << 6) | (1 << 7) | (1 << 8); + } else { + if (write_and_finish == 1) { + need_tags = (1 << 8); + } else { + // server's buffered write and the client's read of the buffered write + // tags should come up. + need_tags = (1 << 4) | (1 << 5) | (1 << 7) | (1 << 8); + } + } + + // No message write or initial metadata write happened yet. + if (max_ping_pongs == 0) { + request_rw->WritesDone(tag(6)); + // wait for server call data structure(call_hook, etc.) to be + // initialized, since initial metadata is corked. + GPR_ASSERT(fixture->cq()->Next(&t, &ok)); + while ((int)(intptr_t)t != 0) { + int i = (int)(intptr_t)t; + GPR_ASSERT(need_tags & (1 << i)); + need_tags &= ~(1 << i); + GPR_ASSERT(fixture->cq()->Next(&t, &ok)); + } + response_rw.Finish(Status::OK, tag(7)); + } else { + if (write_and_finish != 1) { + response_rw.Finish(Status::OK, tag(7)); + } + } + + Status recv_status; + request_rw->Finish(&recv_status, tag(8)); + + while (need_tags) { + GPR_ASSERT(fixture->cq()->Next(&t, &ok)); + int i = (int)(intptr_t)t; + GPR_ASSERT(need_tags & (1 << i)); + need_tags &= ~(1 << i); + } + + GPR_ASSERT(recv_status.ok()); + } + } + fixture->Finish(state); fixture.reset(); - state.SetBytesProcessed(state.range(0) * state.iterations()); + state.SetBytesProcessed(msg_size * state.iterations() * max_ping_pongs * 2); } /******************************************************************************* * CONFIGURATIONS */ -BENCHMARK_TEMPLATE(BM_PumpStreamClientToServer, TCP) - ->Range(0, 128 * 1024 * 1024); -BENCHMARK_TEMPLATE(BM_PumpStreamClientToServer, UDS) - ->Range(0, 128 * 1024 * 1024); -BENCHMARK_TEMPLATE(BM_PumpStreamClientToServer, SockPair) - ->Range(0, 128 * 1024 * 1024); -BENCHMARK_TEMPLATE(BM_PumpStreamClientToServer, InProcessCHTTP2) - ->Range(0, 128 * 1024 * 1024); -BENCHMARK_TEMPLATE(BM_PumpStreamServerToClient, TCP) +// Generate Args for StreamingPingPong benchmarks. Currently generates args for +// only "small streams" (i.e streams with 0, 1 or 2 messages) +static void StreamingPingPongArgs(benchmark::internal::Benchmark* b) { + int msg_size = 0; + + b->Args({0, 0}); // spl case: 0 ping-pong msgs (msg_size doesn't matter here) + + for (msg_size = 0; msg_size <= 128 * 1024 * 1024; + msg_size == 0 ? msg_size++ : msg_size *= 8) { + b->Args({msg_size, 1}); + b->Args({msg_size, 2}); + } +} + +BENCHMARK_TEMPLATE(BM_StreamingPingPong, InProcessCHTTP2, NoOpMutator, + NoOpMutator) + ->Apply(StreamingPingPongArgs); +BENCHMARK_TEMPLATE(BM_StreamingPingPong, TCP, NoOpMutator, NoOpMutator) + ->Apply(StreamingPingPongArgs); + +BENCHMARK_TEMPLATE(BM_StreamingPingPongMsgs, InProcessCHTTP2, NoOpMutator, + NoOpMutator) ->Range(0, 128 * 1024 * 1024); -BENCHMARK_TEMPLATE(BM_PumpStreamServerToClient, UDS) +BENCHMARK_TEMPLATE(BM_StreamingPingPongMsgs, TCP, NoOpMutator, NoOpMutator) ->Range(0, 128 * 1024 * 1024); -BENCHMARK_TEMPLATE(BM_PumpStreamServerToClient, SockPair) + +BENCHMARK_TEMPLATE(BM_StreamingPingPong, MinInProcessCHTTP2, NoOpMutator, + NoOpMutator) + ->Apply(StreamingPingPongArgs); +BENCHMARK_TEMPLATE(BM_StreamingPingPong, MinTCP, NoOpMutator, NoOpMutator) + ->Apply(StreamingPingPongArgs); + +BENCHMARK_TEMPLATE(BM_StreamingPingPongMsgs, MinInProcessCHTTP2, NoOpMutator, + NoOpMutator) ->Range(0, 128 * 1024 * 1024); -BENCHMARK_TEMPLATE(BM_PumpStreamServerToClient, InProcessCHTTP2) +BENCHMARK_TEMPLATE(BM_StreamingPingPongMsgs, MinTCP, NoOpMutator, NoOpMutator) ->Range(0, 128 * 1024 * 1024); +// Generate Args for StreamingPingPongWithCoalescingApi benchmarks. Currently +// generates args for only "small streams" (i.e streams with 0, 1 or 2 messages) +static void StreamingPingPongWithCoalescingApiArgs( + benchmark::internal::Benchmark* b) { + int msg_size = 0; + + b->Args( + {0, 0, 0}); // spl case: 0 ping-pong msgs (msg_size doesn't matter here) + b->Args( + {0, 0, 1}); // spl case: 0 ping-pong msgs (msg_size doesn't matter here) + + for (msg_size = 0; msg_size <= 128 * 1024 * 1024; + msg_size == 0 ? msg_size++ : msg_size *= 8) { + b->Args({msg_size, 1, 0}); + b->Args({msg_size, 2, 0}); + b->Args({msg_size, 1, 1}); + b->Args({msg_size, 2, 1}); + } +} + +BENCHMARK_TEMPLATE(BM_StreamingPingPongWithCoalescingApi, InProcessCHTTP2, + NoOpMutator, NoOpMutator) + ->Apply(StreamingPingPongWithCoalescingApiArgs); +BENCHMARK_TEMPLATE(BM_StreamingPingPongWithCoalescingApi, MinInProcessCHTTP2, + NoOpMutator, NoOpMutator) + ->Apply(StreamingPingPongWithCoalescingApiArgs); + } // namespace testing } // namespace grpc diff --git a/test/cpp/microbenchmarks/bm_fullstack_streaming_pump.cc b/test/cpp/microbenchmarks/bm_fullstack_streaming_pump.cc index dc0e7d769a..47705d3031 100644 --- a/test/cpp/microbenchmarks/bm_fullstack_streaming_pump.cc +++ b/test/cpp/microbenchmarks/bm_fullstack_streaming_pump.cc @@ -33,14 +33,13 @@ /* Benchmark gRPC end2end in various configurations */ +#include <benchmark/benchmark.h> #include <sstream> - #include "src/core/lib/profiling/timers.h" #include "src/cpp/client/create_channel_internal.h" #include "src/proto/grpc/testing/echo.grpc.pb.h" #include "test/cpp/microbenchmarks/fullstack_context_mutators.h" #include "test/cpp/microbenchmarks/fullstack_fixtures.h" -#include "third_party/benchmark/include/benchmark/benchmark.h" namespace grpc { namespace testing { @@ -190,6 +189,14 @@ BENCHMARK_TEMPLATE(BM_PumpStreamServerToClient, SockPair) ->Range(0, 128 * 1024 * 1024); BENCHMARK_TEMPLATE(BM_PumpStreamServerToClient, InProcessCHTTP2) ->Range(0, 128 * 1024 * 1024); +BENCHMARK_TEMPLATE(BM_PumpStreamClientToServer, MinTCP)->Arg(0); +BENCHMARK_TEMPLATE(BM_PumpStreamClientToServer, MinUDS)->Arg(0); +BENCHMARK_TEMPLATE(BM_PumpStreamClientToServer, MinSockPair)->Arg(0); +BENCHMARK_TEMPLATE(BM_PumpStreamClientToServer, MinInProcessCHTTP2)->Arg(0); +BENCHMARK_TEMPLATE(BM_PumpStreamServerToClient, MinTCP)->Arg(0); +BENCHMARK_TEMPLATE(BM_PumpStreamServerToClient, MinUDS)->Arg(0); +BENCHMARK_TEMPLATE(BM_PumpStreamServerToClient, MinSockPair)->Arg(0); +BENCHMARK_TEMPLATE(BM_PumpStreamServerToClient, MinInProcessCHTTP2)->Arg(0); } // namespace testing } // namespace grpc diff --git a/test/cpp/microbenchmarks/bm_fullstack_trickle.cc b/test/cpp/microbenchmarks/bm_fullstack_trickle.cc index 5011f06368..a5cfeb4f95 100644 --- a/test/cpp/microbenchmarks/bm_fullstack_trickle.cc +++ b/test/cpp/microbenchmarks/bm_fullstack_trickle.cc @@ -33,12 +33,12 @@ /* Benchmark gRPC end2end in various configurations */ +#include <benchmark/benchmark.h> #include "src/core/lib/profiling/timers.h" #include "src/cpp/client/create_channel_internal.h" #include "src/proto/grpc/testing/echo.grpc.pb.h" #include "test/cpp/microbenchmarks/fullstack_context_mutators.h" #include "test/cpp/microbenchmarks/fullstack_fixtures.h" -#include "third_party/benchmark/include/benchmark/benchmark.h" extern "C" { #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" #include "src/core/ext/transport/chttp2/transport/internal.h" @@ -53,7 +53,8 @@ static void* tag(intptr_t x) { return reinterpret_cast<void*>(x); } class TrickledCHTTP2 : public EndpointPairFixture { public: TrickledCHTTP2(Service* service, size_t megabits_per_second) - : EndpointPairFixture(service, MakeEndpoints(megabits_per_second)) {} + : EndpointPairFixture(service, MakeEndpoints(megabits_per_second), + FixtureConfiguration()) {} void AddToLabel(std::ostream& out, benchmark::State& state) { out << " writes/iter:" diff --git a/test/cpp/microbenchmarks/bm_fullstack_unary_ping_pong.cc b/test/cpp/microbenchmarks/bm_fullstack_unary_ping_pong.cc index e51d272b10..7524751fbc 100644 --- a/test/cpp/microbenchmarks/bm_fullstack_unary_ping_pong.cc +++ b/test/cpp/microbenchmarks/bm_fullstack_unary_ping_pong.cc @@ -33,14 +33,13 @@ /* Benchmark gRPC end2end in various configurations */ +#include <benchmark/benchmark.h> #include <sstream> - #include "src/core/lib/profiling/timers.h" #include "src/cpp/client/create_channel_internal.h" #include "src/proto/grpc/testing/echo.grpc.pb.h" #include "test/cpp/microbenchmarks/fullstack_context_mutators.h" #include "test/cpp/microbenchmarks/fullstack_fixtures.h" -#include "third_party/benchmark/include/benchmark/benchmark.h" namespace grpc { namespace testing { @@ -142,12 +141,21 @@ static void SweepSizesArgs(benchmark::internal::Benchmark* b) { BENCHMARK_TEMPLATE(BM_UnaryPingPong, TCP, NoOpMutator, NoOpMutator) ->Apply(SweepSizesArgs); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, MinTCP, NoOpMutator, NoOpMutator) + ->Apply(SweepSizesArgs); BENCHMARK_TEMPLATE(BM_UnaryPingPong, UDS, NoOpMutator, NoOpMutator) ->Args({0, 0}); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, MinUDS, NoOpMutator, NoOpMutator) + ->Args({0, 0}); BENCHMARK_TEMPLATE(BM_UnaryPingPong, SockPair, NoOpMutator, NoOpMutator) ->Args({0, 0}); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, MinSockPair, NoOpMutator, NoOpMutator) + ->Args({0, 0}); BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, NoOpMutator, NoOpMutator) ->Apply(SweepSizesArgs); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, MinInProcessCHTTP2, NoOpMutator, + NoOpMutator) + ->Apply(SweepSizesArgs); BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, Client_AddMetadata<RandomBinaryMetadata<10>, 1>, NoOpMutator) ->Args({0, 0}); diff --git a/test/cpp/microbenchmarks/bm_metadata.cc b/test/cpp/microbenchmarks/bm_metadata.cc index 34874b57f5..7029f369ad 100644 --- a/test/cpp/microbenchmarks/bm_metadata.cc +++ b/test/cpp/microbenchmarks/bm_metadata.cc @@ -33,17 +33,15 @@ /* Test out various metadata handling primitives */ +#include <benchmark/benchmark.h> #include <grpc/grpc.h> extern "C" { -#include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/transport/metadata.h" #include "src/core/lib/transport/static_metadata.h" -#include "src/core/lib/transport/transport.h" } #include "test/cpp/microbenchmarks/helpers.h" -#include "third_party/benchmark/include/benchmark/benchmark.h" auto& force_library_initialization = Library::get(); @@ -65,19 +63,6 @@ static void BM_SliceFromCopied(benchmark::State& state) { } BENCHMARK(BM_SliceFromCopied); -static void BM_SliceFromStreamOwnedBuffer(benchmark::State& state) { - grpc_stream_refcount r; - GRPC_STREAM_REF_INIT(&r, 1, NULL, NULL, "test"); - char buffer[64]; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - while (state.KeepRunning()) { - grpc_slice_unref_internal(&exec_ctx, grpc_slice_from_stream_owned_buffer( - &r, buffer, sizeof(buffer))); - } - grpc_exec_ctx_finish(&exec_ctx); -} -BENCHMARK(BM_SliceFromStreamOwnedBuffer); - static void BM_SliceIntern(benchmark::State& state) { TrackCounters track_counters; gpr_slice slice = grpc_slice_from_static_string("abc"); diff --git a/test/cpp/microbenchmarks/fullstack_fixtures.h b/test/cpp/microbenchmarks/fullstack_fixtures.h index dc29701059..f129ede26a 100644 --- a/test/cpp/microbenchmarks/fullstack_fixtures.h +++ b/test/cpp/microbenchmarks/fullstack_fixtures.h @@ -61,29 +61,33 @@ extern "C" { namespace grpc { namespace testing { -static void ApplyCommonServerBuilderConfig(ServerBuilder* b) { - b->SetMaxReceiveMessageSize(INT_MAX); - b->SetMaxSendMessageSize(INT_MAX); -} +class FixtureConfiguration { + public: + virtual void ApplyCommonChannelArguments(ChannelArguments* c) const { + c->SetInt(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, INT_MAX); + c->SetInt(GRPC_ARG_MAX_SEND_MESSAGE_LENGTH, INT_MAX); + } -static void ApplyCommonChannelArguments(ChannelArguments* c) { - c->SetInt(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, INT_MAX); - c->SetInt(GRPC_ARG_MAX_SEND_MESSAGE_LENGTH, INT_MAX); -} + virtual void ApplyCommonServerBuilderConfig(ServerBuilder* b) const { + b->SetMaxReceiveMessageSize(INT_MAX); + b->SetMaxSendMessageSize(INT_MAX); + } +}; class BaseFixture : public TrackCounters {}; class FullstackFixture : public BaseFixture { public: - FullstackFixture(Service* service, const grpc::string& address) { + FullstackFixture(Service* service, const FixtureConfiguration& config, + const grpc::string& address) { ServerBuilder b; b.AddListeningPort(address, InsecureServerCredentials()); cq_ = b.AddCompletionQueue(true); b.RegisterService(service); - ApplyCommonServerBuilderConfig(&b); + config.ApplyCommonServerBuilderConfig(&b); server_ = b.BuildAndStart(); ChannelArguments args; - ApplyCommonChannelArguments(&args); + config.ApplyCommonChannelArguments(&args); channel_ = CreateCustomChannel(address, InsecureChannelCredentials(), args); } @@ -107,7 +111,9 @@ class FullstackFixture : public BaseFixture { class TCP : public FullstackFixture { public: - TCP(Service* service) : FullstackFixture(service, MakeAddress()) {} + TCP(Service* service, const FixtureConfiguration& fixture_configuration = + FixtureConfiguration()) + : FullstackFixture(service, fixture_configuration, MakeAddress()) {} private: static grpc::string MakeAddress() { @@ -120,7 +126,9 @@ class TCP : public FullstackFixture { class UDS : public FullstackFixture { public: - UDS(Service* service) : FullstackFixture(service, MakeAddress()) {} + UDS(Service* service, const FixtureConfiguration& fixture_configuration = + FixtureConfiguration()) + : FullstackFixture(service, fixture_configuration, MakeAddress()) {} private: static grpc::string MakeAddress() { @@ -134,12 +142,13 @@ class UDS : public FullstackFixture { class EndpointPairFixture : public BaseFixture { public: - EndpointPairFixture(Service* service, grpc_endpoint_pair endpoints) + EndpointPairFixture(Service* service, grpc_endpoint_pair endpoints, + const FixtureConfiguration& fixture_configuration) : endpoint_pair_(endpoints) { ServerBuilder b; cq_ = b.AddCompletionQueue(true); b.RegisterService(service); - ApplyCommonServerBuilderConfig(&b); + fixture_configuration.ApplyCommonServerBuilderConfig(&b); server_ = b.BuildAndStart(); grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; @@ -169,7 +178,7 @@ class EndpointPairFixture : public BaseFixture { { ChannelArguments args; args.SetString(GRPC_ARG_DEFAULT_AUTHORITY, "test.authority"); - ApplyCommonChannelArguments(&args); + fixture_configuration.ApplyCommonChannelArguments(&args); grpc_channel_args c_args = args.c_channel_args(); client_transport_ = @@ -211,15 +220,19 @@ class EndpointPairFixture : public BaseFixture { class SockPair : public EndpointPairFixture { public: - SockPair(Service* service) - : EndpointPairFixture(service, grpc_iomgr_create_endpoint_pair( - "test", Library::get().rq(), 8192)) {} + SockPair(Service* service, const FixtureConfiguration& fixture_configuration = + FixtureConfiguration()) + : EndpointPairFixture(service, + grpc_iomgr_create_endpoint_pair("test", NULL), + fixture_configuration) {} }; class InProcessCHTTP2 : public EndpointPairFixture { public: - InProcessCHTTP2(Service* service) - : EndpointPairFixture(service, MakeEndpoints()) {} + InProcessCHTTP2(Service* service, + const FixtureConfiguration& fixture_configuration = + FixtureConfiguration()) + : EndpointPairFixture(service, MakeEndpoints(), fixture_configuration) {} void AddToLabel(std::ostream& out, benchmark::State& state) { EndpointPairFixture::AddToLabel(out, state); @@ -238,6 +251,32 @@ class InProcessCHTTP2 : public EndpointPairFixture { } }; +//////////////////////////////////////////////////////////////////////////////// +// Minimal stack fixtures + +class MinStackConfiguration : public FixtureConfiguration { + void ApplyCommonChannelArguments(ChannelArguments* a) const override { + a->SetInt(GRPC_ARG_MINIMAL_STACK, 1); + FixtureConfiguration::ApplyCommonChannelArguments(a); + } + + void ApplyCommonServerBuilderConfig(ServerBuilder* b) const override { + b->AddChannelArgument(GRPC_ARG_MINIMAL_STACK, 1); + FixtureConfiguration::ApplyCommonServerBuilderConfig(b); + } +}; + +template <class Base> +class MinStackize : public Base { + public: + MinStackize(Service* service) : Base(service, MinStackConfiguration()) {} +}; + +typedef MinStackize<TCP> MinTCP; +typedef MinStackize<UDS> MinUDS; +typedef MinStackize<SockPair> MinSockPair; +typedef MinStackize<InProcessCHTTP2> MinInProcessCHTTP2; + } // namespace testing } // namespace grpc diff --git a/test/cpp/microbenchmarks/helpers.h b/test/cpp/microbenchmarks/helpers.h index af401dd7bb..7360a1c9f2 100644 --- a/test/cpp/microbenchmarks/helpers.h +++ b/test/cpp/microbenchmarks/helpers.h @@ -41,8 +41,8 @@ extern "C" { #include "test/core/util/memory_counters.h" } +#include <benchmark/benchmark.h> #include <grpc++/impl/grpc_library.h> -#include "third_party/benchmark/include/benchmark/benchmark.h" class Library { public: diff --git a/test/cpp/microbenchmarks/noop-benchmark.cc b/test/cpp/microbenchmarks/noop-benchmark.cc index 99fa6d5f6e..7372ad04f2 100644 --- a/test/cpp/microbenchmarks/noop-benchmark.cc +++ b/test/cpp/microbenchmarks/noop-benchmark.cc @@ -34,7 +34,7 @@ /* This benchmark exists to ensure that the benchmark integration is * working */ -#include "third_party/benchmark/include/benchmark/benchmark.h" +#include <benchmark/benchmark.h> static void BM_NoOp(benchmark::State& state) { while (state.KeepRunning()) { |