diff options
Diffstat (limited to 'test/cpp/microbenchmarks')
-rw-r--r-- | test/cpp/microbenchmarks/bm_call_create.cc | 409 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/bm_chttp2_hpack.cc | 443 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/bm_closure.cc | 464 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/bm_cq.cc | 145 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/bm_error.cc | 248 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/bm_fullstack.cc | 1079 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/bm_metadata.cc | 297 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/noop-benchmark.cc | 45 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/representative_server_initial_metadata.headers | 4 | ||||
-rw-r--r-- | test/cpp/microbenchmarks/representative_server_trailing_metadata.headers | 3 |
10 files changed, 3137 insertions, 0 deletions
diff --git a/test/cpp/microbenchmarks/bm_call_create.cc b/test/cpp/microbenchmarks/bm_call_create.cc new file mode 100644 index 0000000000..e2e5bbbe00 --- /dev/null +++ b/test/cpp/microbenchmarks/bm_call_create.cc @@ -0,0 +1,409 @@ +/* + * + * 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. + * + */ + +/* This benchmark exists to ensure that the benchmark integration is + * working */ + +#include <string.h> +#include <sstream> + +#include <grpc++/support/channel_arguments.h> +#include <grpc/grpc.h> +#include <grpc/support/alloc.h> +#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/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/transport/transport_impl.h" +} + +#include "third_party/benchmark/include/benchmark/benchmark.h" + +static struct Init { + Init() { grpc_init(); } + ~Init() { grpc_shutdown(); } +} g_init; + +class BaseChannelFixture { + public: + BaseChannelFixture(grpc_channel *channel) : channel_(channel) {} + ~BaseChannelFixture() { grpc_channel_destroy(channel_); } + + grpc_channel *channel() const { return channel_; } + + private: + grpc_channel *const channel_; +}; + +class InsecureChannel : public BaseChannelFixture { + public: + InsecureChannel() + : BaseChannelFixture( + grpc_insecure_channel_create("localhost:1234", NULL, NULL)) {} +}; + +class LameChannel : public BaseChannelFixture { + public: + LameChannel() + : BaseChannelFixture(grpc_lame_client_channel_create( + "localhost:1234", GRPC_STATUS_UNAUTHENTICATED, "blah")) {} +}; + +template <class Fixture> +static void BM_CallCreateDestroy(benchmark::State &state) { + Fixture fixture; + grpc_completion_queue *cq = grpc_completion_queue_create(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( + fixture.channel(), NULL, GRPC_PROPAGATE_DEFAULTS, cq, method_hdl, + deadline, NULL)); + } + grpc_completion_queue_destroy(cq); +} + +BENCHMARK_TEMPLATE(BM_CallCreateDestroy, InsecureChannel); +BENCHMARK_TEMPLATE(BM_CallCreateDestroy, LameChannel); + +static void FilterDestroy(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + gpr_free(arg); +} + +static void DoNothing(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {} + +class FakeClientChannelFactory : public grpc_client_channel_factory { + public: + FakeClientChannelFactory() { vtable = &vtable_; } + + private: + static void NoRef(grpc_client_channel_factory *factory) {} + static void NoUnref(grpc_exec_ctx *exec_ctx, + grpc_client_channel_factory *factory) {} + static grpc_subchannel *CreateSubchannel(grpc_exec_ctx *exec_ctx, + grpc_client_channel_factory *factory, + const grpc_subchannel_args *args) { + return nullptr; + } + static grpc_channel *CreateClientChannel(grpc_exec_ctx *exec_ctx, + grpc_client_channel_factory *factory, + const char *target, + grpc_client_channel_type type, + const grpc_channel_args *args) { + return nullptr; + } + + static const grpc_client_channel_factory_vtable vtable_; +}; + +const grpc_client_channel_factory_vtable FakeClientChannelFactory::vtable_ = { + NoRef, NoUnref, CreateSubchannel, CreateClientChannel}; + +static grpc_arg StringArg(const char *key, const char *value) { + grpc_arg a; + a.type = GRPC_ARG_STRING; + a.key = const_cast<char *>(key); + a.value.string = const_cast<char *>(value); + return a; +} + +enum FixtureFlags : uint32_t { + CHECKS_NOT_LAST = 1, + REQUIRES_TRANSPORT = 2, +}; + +template <const grpc_channel_filter *kFilter, uint32_t kFlags> +struct Fixture { + const grpc_channel_filter *filter = kFilter; + const uint32_t flags = kFlags; +}; + +namespace dummy_filter { + +static void StartTransportStreamOp(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_transport_stream_op *op) {} + +static void StartTransportOp(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + grpc_transport_op *op) {} + +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, + void *and_free_memory) {} + +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 dummy_filter = {StartTransportStreamOp, + StartTransportOp, + 0, + InitCallElem, + SetPollsetOrPollsetSet, + DestroyCallElem, + 0, + InitChannelElem, + DestroyChannelElem, + GetPeer, + GetChannelInfo, + "dummy_filter"}; + +} // namespace dummy_filter + +namespace dummy_transport { + +/* Memory required for a single stream element - this is allocated by upper + layers and initialized by the transport */ +size_t sizeof_stream; /* = sizeof(transport stream) */ + +/* name of this transport implementation */ +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) { + return 0; +} + +/* implementation of grpc_transport_set_pollset */ +void SetPollset(grpc_exec_ctx *exec_ctx, grpc_transport *self, + grpc_stream *stream, grpc_pollset *pollset) {} + +/* implementation of grpc_transport_set_pollset */ +void SetPollsetSet(grpc_exec_ctx *exec_ctx, grpc_transport *self, + grpc_stream *stream, grpc_pollset_set *pollset_set) {} + +/* 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_closure_sched(exec_ctx, op->on_complete, GRPC_ERROR_NONE); +} + +/* implementation of grpc_transport_perform_op */ +void PerformOp(grpc_exec_ctx *exec_ctx, grpc_transport *self, + grpc_transport_op *op) {} + +/* implementation of grpc_transport_destroy_stream */ +void DestroyStream(grpc_exec_ctx *exec_ctx, grpc_transport *self, + grpc_stream *stream, void *and_free_memory) {} + +/* implementation of grpc_transport_destroy */ +void Destroy(grpc_exec_ctx *exec_ctx, grpc_transport *self) {} + +/* implementation of grpc_transport_get_peer */ +char *GetPeer(grpc_exec_ctx *exec_ctx, grpc_transport *self) { + return gpr_strdup("transport_peer"); +} + +/* implementation of grpc_transport_get_endpoint */ +grpc_endpoint *GetEndpoint(grpc_exec_ctx *exec_ctx, grpc_transport *self) { + return nullptr; +} + +static const grpc_transport_vtable dummy_transport_vtable = { + 0, "dummy_http2", InitStream, + SetPollset, SetPollsetSet, PerformStreamOp, + PerformOp, DestroyStream, Destroy, + GetPeer, GetEndpoint}; + +static grpc_transport dummy_transport = {&dummy_transport_vtable}; + +} // namespace dummy_transport + +class NoOp { + public: + class Op { + public: + Op(grpc_exec_ctx *exec_ctx, NoOp *p, grpc_call_stack *s) {} + void Finish(grpc_exec_ctx *exec_ctx) {} + }; +}; + +class SendEmptyMetadata { + public: + SendEmptyMetadata() { + memset(&op_, 0, sizeof(op_)); + op_.on_complete = grpc_closure_init(&closure_, DoNothing, nullptr, + grpc_schedule_on_exec_ctx); + } + + 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_; + } + void Finish(grpc_exec_ctx *exec_ctx) { + grpc_metadata_batch_destroy(exec_ctx, &batch_); + } + + private: + grpc_metadata_batch batch_; + }; + + private: + 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_closure closure_; +}; + +// Test a filter in isolation. Fixture specifies the filter under test (use the +// Fixture<> template to specify this), and TestOp defines some unit of work to +// perform on said filter. +template <class Fixture, class TestOp> +static void BM_IsolatedFilter(benchmark::State &state) { + Fixture fixture; + std::ostringstream label; + + std::vector<grpc_arg> args; + FakeClientChannelFactory fake_client_channel_factory; + args.push_back(grpc_client_channel_factory_create_channel_arg( + &fake_client_channel_factory)); + args.push_back(StringArg(GRPC_ARG_SERVER_URI, "localhost")); + + grpc_channel_args channel_args = {args.size(), &args[0]}; + + std::vector<const grpc_channel_filter *> filters; + if (fixture.filter != nullptr) { + filters.push_back(fixture.filter); + } + if (fixture.flags & CHECKS_NOT_LAST) { + filters.push_back(&dummy_filter::dummy_filter); + label << " #has_dummy_filter"; + } + + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + size_t channel_size = grpc_channel_stack_size(&filters[0], filters.size()); + grpc_channel_stack *channel_stack = + static_cast<grpc_channel_stack *>(gpr_zalloc(channel_size)); + GPR_ASSERT(GRPC_LOG_IF_ERROR( + "call_stack_init", + grpc_channel_stack_init(&exec_ctx, 1, FilterDestroy, channel_stack, + &filters[0], filters.size(), &channel_args, + fixture.flags & REQUIRES_TRANSPORT + ? &dummy_transport::dummy_transport + : nullptr, + "CHANNEL", channel_stack))); + grpc_exec_ctx_flush(&exec_ctx); + grpc_call_stack *call_stack = static_cast<grpc_call_stack *>( + gpr_zalloc(channel_stack->call_stack_size)); + gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + gpr_timespec start_time = gpr_now(GPR_CLOCK_MONOTONIC); + grpc_slice method = grpc_slice_from_static_string("/foo/bar"); + grpc_call_final_info final_info; + TestOp test_op_data; + while (state.KeepRunning()) { + GRPC_ERROR_UNREF(grpc_call_stack_init(&exec_ctx, channel_stack, 1, + DoNothing, NULL, NULL, NULL, method, + start_time, deadline, call_stack)); + 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); + } + grpc_channel_stack_destroy(&exec_ctx, channel_stack); + grpc_exec_ctx_finish(&exec_ctx); + gpr_free(channel_stack); + gpr_free(call_stack); + + state.SetLabel(label.str()); +} + +typedef Fixture<nullptr, 0> NoFilter; +BENCHMARK_TEMPLATE(BM_IsolatedFilter, NoFilter, NoOp); +typedef Fixture<&dummy_filter::dummy_filter, 0> DummyFilter; +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; +BENCHMARK_TEMPLATE(BM_IsolatedFilter, CompressFilter, NoOp); +BENCHMARK_TEMPLATE(BM_IsolatedFilter, CompressFilter, SendEmptyMetadata); +typedef Fixture<&grpc_client_deadline_filter, CHECKS_NOT_LAST> + ClientDeadlineFilter; +BENCHMARK_TEMPLATE(BM_IsolatedFilter, ClientDeadlineFilter, NoOp); +BENCHMARK_TEMPLATE(BM_IsolatedFilter, ClientDeadlineFilter, SendEmptyMetadata); +typedef Fixture<&grpc_server_deadline_filter, CHECKS_NOT_LAST> + ServerDeadlineFilter; +BENCHMARK_TEMPLATE(BM_IsolatedFilter, ServerDeadlineFilter, NoOp); +BENCHMARK_TEMPLATE(BM_IsolatedFilter, ServerDeadlineFilter, SendEmptyMetadata); +typedef Fixture<&grpc_http_client_filter, CHECKS_NOT_LAST | REQUIRES_TRANSPORT> + HttpClientFilter; +BENCHMARK_TEMPLATE(BM_IsolatedFilter, HttpClientFilter, NoOp); +BENCHMARK_TEMPLATE(BM_IsolatedFilter, HttpClientFilter, SendEmptyMetadata); +typedef Fixture<&grpc_http_server_filter, CHECKS_NOT_LAST> HttpServerFilter; +BENCHMARK_TEMPLATE(BM_IsolatedFilter, HttpServerFilter, NoOp); +BENCHMARK_TEMPLATE(BM_IsolatedFilter, HttpServerFilter, SendEmptyMetadata); +typedef Fixture<&grpc_message_size_filter, CHECKS_NOT_LAST> MessageSizeFilter; +BENCHMARK_TEMPLATE(BM_IsolatedFilter, MessageSizeFilter, NoOp); +BENCHMARK_TEMPLATE(BM_IsolatedFilter, MessageSizeFilter, SendEmptyMetadata); +typedef Fixture<&grpc_load_reporting_filter, CHECKS_NOT_LAST> + LoadReportingFilter; +BENCHMARK_TEMPLATE(BM_IsolatedFilter, LoadReportingFilter, NoOp); +BENCHMARK_TEMPLATE(BM_IsolatedFilter, LoadReportingFilter, SendEmptyMetadata); + +BENCHMARK_MAIN(); diff --git a/test/cpp/microbenchmarks/bm_chttp2_hpack.cc b/test/cpp/microbenchmarks/bm_chttp2_hpack.cc new file mode 100644 index 0000000000..5fb3f37130 --- /dev/null +++ b/test/cpp/microbenchmarks/bm_chttp2_hpack.cc @@ -0,0 +1,443 @@ +/* + * + * 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 HPACK operations */ + +#include <grpc/support/log.h> +#include <string.h> +#include <sstream> +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/transport/static_metadata.h" +} +#include "third_party/benchmark/include/benchmark/benchmark.h" + +static struct Init { + Init() { grpc_init(); } + ~Init() { grpc_shutdown(); } +} g_init; + +//////////////////////////////////////////////////////////////////////////////// +// HPACK encoder +// + +static void BM_HpackEncoderInitDestroy(benchmark::State &state) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_chttp2_hpack_compressor c; + while (state.KeepRunning()) { + grpc_chttp2_hpack_compressor_init(&c); + grpc_chttp2_hpack_compressor_destroy(&exec_ctx, &c); + grpc_exec_ctx_flush(&exec_ctx); + } + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_HpackEncoderInitDestroy); + +template <class Fixture> +static void BM_HpackEncoderEncodeHeader(benchmark::State &state) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + grpc_metadata_batch b; + grpc_metadata_batch_init(&b); + std::vector<grpc_mdelem> elems = Fixture::GetElems(&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(&exec_ctx, &b, &storage[i], elems[i]))); + } + + grpc_chttp2_hpack_compressor c; + grpc_chttp2_hpack_compressor_init(&c); + grpc_transport_one_way_stats stats; + memset(&stats, 0, sizeof(stats)); + 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_slice_buffer_reset_and_unref_internal(&exec_ctx, &outbuf); + grpc_exec_ctx_flush(&exec_ctx); + } + grpc_metadata_batch_destroy(&exec_ctx, &b); + grpc_chttp2_hpack_compressor_destroy(&exec_ctx, &c); + grpc_slice_buffer_destroy_internal(&exec_ctx, &outbuf); + grpc_exec_ctx_finish(&exec_ctx); + + std::ostringstream label; + label << "framing_bytes/iter:" << (static_cast<double>(stats.framing_bytes) / + static_cast<double>(state.iterations())) + << " header_bytes/iter:" << (static_cast<double>(stats.header_bytes) / + static_cast<double>(state.iterations())); + state.SetLabel(label.str()); +} + +namespace hpack_encoder_fixtures { + +class EmptyBatch { + public: + static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) { + return {}; + } +}; + +class SingleStaticElem { + public: + static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) { + return {GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE}; + } +}; + +class SingleInternedElem { + public: + 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")), + grpc_slice_intern(grpc_slice_from_static_string("def")))}; + } +}; + +class SingleInternedKeyElem { + public: + 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")), + grpc_slice_from_static_string("def"))}; + } +}; + +class SingleNonInternedElem { + public: + static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) { + return {grpc_mdelem_from_slices(exec_ctx, + grpc_slice_from_static_string("abc"), + grpc_slice_from_static_string("def"))}; + } +}; + +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)")))}; + } +}; + +class RepresentativeServerInitialMetadata { + public: + static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) { + return {GRPC_MDELEM_STATUS_200, + GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC, + GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE_COMMA_GZIP}; + } +}; + +class RepresentativeServerTrailingMetadata { + public: + static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) { + return {GRPC_MDELEM_GRPC_STATUS_0}; + } +}; + +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, EmptyBatch)->Args({0, 16384}); +// test with eof (shouldn't affect anything) +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, EmptyBatch)->Args({1, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleStaticElem) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleInternedKeyElem) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleInternedElem) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleNonInternedElem) + ->Args({0, 16384}); +// test with a tiny frame size, to highlight continuation costs +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleNonInternedElem) + ->Args({0, 1}); + +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + RepresentativeClientInitialMetadata) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + RepresentativeServerInitialMetadata) + ->Args({0, 16384}); +BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, + RepresentativeServerTrailingMetadata) + ->Args({1, 16384}); + +} // namespace hpack_encoder_fixtures + +//////////////////////////////////////////////////////////////////////////////// +// HPACK parser +// + +static void BM_HpackParserInitDestroy(benchmark::State &state) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_chttp2_hpack_parser p; + while (state.KeepRunning()) { + grpc_chttp2_hpack_parser_init(&exec_ctx, &p); + grpc_chttp2_hpack_parser_destroy(&exec_ctx, &p); + grpc_exec_ctx_flush(&exec_ctx); + } + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_HpackParserInitDestroy); + +static void UnrefHeader(grpc_exec_ctx *exec_ctx, void *user_data, + grpc_mdelem md) { + GRPC_MDELEM_UNREF(exec_ctx, md); +} + +template <class Fixture> +static void BM_HpackParserParseHeader(benchmark::State &state) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + std::vector<grpc_slice> init_slices = Fixture::GetInitSlices(); + std::vector<grpc_slice> benchmark_slices = Fixture::GetBenchmarkSlices(); + grpc_chttp2_hpack_parser p; + grpc_chttp2_hpack_parser_init(&exec_ctx, &p); + p.on_header = UnrefHeader; + p.on_header_user_data = nullptr; + for (auto slice : init_slices) { + grpc_chttp2_hpack_parser_parse(&exec_ctx, &p, slice); + } + while (state.KeepRunning()) { + for (auto slice : benchmark_slices) { + grpc_chttp2_hpack_parser_parse(&exec_ctx, &p, slice); + } + grpc_exec_ctx_flush(&exec_ctx); + } + grpc_chttp2_hpack_parser_destroy(&exec_ctx, &p); + grpc_exec_ctx_finish(&exec_ctx); +} + +namespace hpack_parser_fixtures { + +static grpc_slice MakeSlice(std::initializer_list<uint8_t> bytes) { + grpc_slice s = grpc_slice_malloc(bytes.size()); + uint8_t *p = GRPC_SLICE_START_PTR(s); + for (auto b : bytes) { + *p++ = b; + } + return s; +} + +class EmptyBatch { + public: + static std::vector<grpc_slice> GetInitSlices() { return {}; } + static std::vector<grpc_slice> GetBenchmarkSlices() { + return {MakeSlice({})}; + } +}; + +class IndexedSingleStaticElem { + public: + static std::vector<grpc_slice> GetInitSlices() { + return {MakeSlice( + {0x40, 0x07, ':', 's', 't', 'a', 't', 'u', 's', 0x03, '2', '0', '0'})}; + } + static std::vector<grpc_slice> GetBenchmarkSlices() { + return {MakeSlice({0xbe})}; + } +}; + +class AddIndexedSingleStaticElem { + public: + static std::vector<grpc_slice> GetInitSlices() { return {}; } + static std::vector<grpc_slice> GetBenchmarkSlices() { + return {MakeSlice( + {0x40, 0x07, ':', 's', 't', 'a', 't', 'u', 's', 0x03, '2', '0', '0'})}; + } +}; + +class KeyIndexedSingleStaticElem { + public: + static std::vector<grpc_slice> GetInitSlices() { + return {MakeSlice( + {0x40, 0x07, ':', 's', 't', 'a', 't', 'u', 's', 0x03, '2', '0', '0'})}; + } + static std::vector<grpc_slice> GetBenchmarkSlices() { + return {MakeSlice({0x7e, 0x03, 'd', 'e', 'f'})}; + } +}; + +class IndexedSingleInternedElem { + public: + static std::vector<grpc_slice> GetInitSlices() { + return {MakeSlice({0x40, 0x03, 'a', 'b', 'c', 0x03, 'd', 'e', 'f'})}; + } + static std::vector<grpc_slice> GetBenchmarkSlices() { + return {MakeSlice({0xbe})}; + } +}; + +class AddIndexedSingleInternedElem { + public: + static std::vector<grpc_slice> GetInitSlices() { return {}; } + static std::vector<grpc_slice> GetBenchmarkSlices() { + return {MakeSlice({0x40, 0x03, 'a', 'b', 'c', 0x03, 'd', 'e', 'f'})}; + } +}; + +class KeyIndexedSingleInternedElem { + public: + static std::vector<grpc_slice> GetInitSlices() { + return {MakeSlice({0x40, 0x03, 'a', 'b', 'c', 0x03, 'd', 'e', 'f'})}; + } + static std::vector<grpc_slice> GetBenchmarkSlices() { + return {MakeSlice({0x7e, 0x03, 'g', 'h', 'i'})}; + } +}; + +class NonIndexedElem { + public: + static std::vector<grpc_slice> GetInitSlices() { return {}; } + static std::vector<grpc_slice> GetBenchmarkSlices() { + return {MakeSlice({0x00, 0x03, 'a', 'b', 'c', 0x03, 'd', 'e', 'f'})}; + } +}; + +class RepresentativeClientInitialMetadata { + public: + static std::vector<grpc_slice> GetInitSlices() { + return {grpc_slice_from_static_string( + // generated with: + // ``` + // tools/codegen/core/gen_header_frame.py --compression inc --no_framing + // < test/core/bad_client/tests/simple_request.headers + // ``` + "@\x05:path\x08/foo/bar" + "@\x07:scheme\x04http" + "@\x07:method\x04POST" + "@\x0a:authority\x09localhost" + "@\x0c" + "content-type\x10" + "application/grpc" + "@\x14grpc-accept-encoding\x15identity,deflate,gzip" + "@\x02te\x08trailers" + "@\x0auser-agent\"bad-client grpc-c/0.12.0.0 (linux)")}; + } + static std::vector<grpc_slice> GetBenchmarkSlices() { + // generated with: + // ``` + // tools/codegen/core/gen_header_frame.py --compression pre --no_framing + // --hex < test/core/bad_client/tests/simple_request.headers + // ``` + return {MakeSlice({0xc5, 0xc4, 0xc3, 0xc2, 0xc1, 0xc0, 0xbf, 0xbe})}; + } +}; + +class RepresentativeServerInitialMetadata { + public: + static std::vector<grpc_slice> GetInitSlices() { + return {grpc_slice_from_static_string( + // generated with: + // ``` + // tools/codegen/core/gen_header_frame.py --compression inc --no_framing + // < + // test/cpp/microbenchmarks/representative_server_initial_metadata.headers + // ``` + "@\x07:status\x03" + "200" + "@\x0c" + "content-type\x10" + "application/grpc" + "@\x14grpc-accept-encoding\x15identity,deflate,gzip")}; + } + static std::vector<grpc_slice> GetBenchmarkSlices() { + // generated with: + // ``` + // tools/codegen/core/gen_header_frame.py --compression pre --no_framing + // --hex < + // test/cpp/microbenchmarks/representative_server_initial_metadata.headers + // ``` + return {MakeSlice({0xc0, 0xbf, 0xbe})}; + } +}; + +class RepresentativeServerTrailingMetadata { + public: + static std::vector<grpc_slice> GetInitSlices() { + return {grpc_slice_from_static_string( + // generated with: + // ``` + // tools/codegen/core/gen_header_frame.py --compression inc --no_framing + // < + // test/cpp/microbenchmarks/representative_server_trailing_metadata.headers + // ``` + "@\x0bgrpc-status\x01" + "0" + "@\x0cgrpc-message\x00")}; + } + static std::vector<grpc_slice> GetBenchmarkSlices() { + // generated with: + // ``` + // tools/codegen/core/gen_header_frame.py --compression pre --no_framing + // --hex < + // test/cpp/microbenchmarks/representative_server_trailing_metadata.headers + // ``` + return {MakeSlice({0xbf, 0xbe})}; + } +}; + +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, EmptyBatch); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, IndexedSingleStaticElem); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, AddIndexedSingleStaticElem); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, KeyIndexedSingleStaticElem); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, IndexedSingleInternedElem); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, AddIndexedSingleInternedElem); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, KeyIndexedSingleInternedElem); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedElem); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, + RepresentativeClientInitialMetadata); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, + RepresentativeServerInitialMetadata); +BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, + RepresentativeServerTrailingMetadata); + +} // namespace hpack_parser_fixtures + +BENCHMARK_MAIN(); diff --git a/test/cpp/microbenchmarks/bm_closure.cc b/test/cpp/microbenchmarks/bm_closure.cc new file mode 100644 index 0000000000..1f54e8c8b1 --- /dev/null +++ b/test/cpp/microbenchmarks/bm_closure.cc @@ -0,0 +1,464 @@ +/* + * + * 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. + * + */ + +/* Test various closure related operations */ + +#include <grpc/grpc.h> + +extern "C" { +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/support/spinlock.h" +} + +#include "third_party/benchmark/include/benchmark/benchmark.h" + +#include <sstream> + +#ifdef GPR_LOW_LEVEL_COUNTERS +extern "C" gpr_atm gpr_mu_locks; +#endif + +static class InitializeStuff { + public: + InitializeStuff() { grpc_init(); } + ~InitializeStuff() { grpc_shutdown(); } +} initialize_stuff; + +class TrackCounters { + public: + TrackCounters(benchmark::State& state) : state_(state) {} + + ~TrackCounters() { + std::ostringstream out; +#ifdef GPR_LOW_LEVEL_COUNTERS + out << " locks/iter:" << ((double)(gpr_atm_no_barrier_load(&gpr_mu_locks) - + mu_locks_at_start_) / + (double)state_.iterations()) + << " atm_cas/iter:" + << ((double)(gpr_atm_no_barrier_load(&gpr_counter_atm_cas) - + atm_cas_at_start_) / + (double)state_.iterations()) + << " atm_add/iter:" + << ((double)(gpr_atm_no_barrier_load(&gpr_counter_atm_add) - + atm_add_at_start_) / + (double)state_.iterations()); +#endif + state_.SetLabel(out.str()); + } + + private: + benchmark::State& state_; +#ifdef GPR_LOW_LEVEL_COUNTERS + const size_t mu_locks_at_start_ = gpr_atm_no_barrier_load(&gpr_mu_locks); + const size_t atm_cas_at_start_ = + gpr_atm_no_barrier_load(&gpr_counter_atm_cas); + const size_t atm_add_at_start_ = + gpr_atm_no_barrier_load(&gpr_counter_atm_add); +#endif +}; + +static void BM_NoOpExecCtx(benchmark::State& state) { + TrackCounters track_counters(state); + while (state.KeepRunning()) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_exec_ctx_finish(&exec_ctx); + } +} +BENCHMARK(BM_NoOpExecCtx); + +static void BM_WellFlushed(benchmark::State& state) { + TrackCounters track_counters(state); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + grpc_exec_ctx_flush(&exec_ctx); + } + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_WellFlushed); + +static void DoNothing(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) {} + +static void BM_ClosureInitAgainstExecCtx(benchmark::State& state) { + TrackCounters track_counters(state); + grpc_closure c; + while (state.KeepRunning()) { + benchmark::DoNotOptimize( + grpc_closure_init(&c, DoNothing, NULL, grpc_schedule_on_exec_ctx)); + } +} +BENCHMARK(BM_ClosureInitAgainstExecCtx); + +static void BM_ClosureInitAgainstCombiner(benchmark::State& state) { + TrackCounters track_counters(state); + grpc_combiner* combiner = grpc_combiner_create(NULL); + grpc_closure c; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + benchmark::DoNotOptimize(grpc_closure_init( + &c, DoNothing, NULL, grpc_combiner_scheduler(combiner, false))); + } + GRPC_COMBINER_UNREF(&exec_ctx, combiner, "finished"); + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_ClosureInitAgainstCombiner); + +static void BM_ClosureRunOnExecCtx(benchmark::State& state) { + TrackCounters track_counters(state); + grpc_closure c; + grpc_closure_init(&c, DoNothing, NULL, grpc_schedule_on_exec_ctx); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + grpc_closure_run(&exec_ctx, &c, GRPC_ERROR_NONE); + grpc_exec_ctx_flush(&exec_ctx); + } + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_ClosureRunOnExecCtx); + +static void BM_ClosureCreateAndRun(benchmark::State& state) { + TrackCounters track_counters(state); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + grpc_closure_run(&exec_ctx, grpc_closure_create(DoNothing, NULL, + grpc_schedule_on_exec_ctx), + GRPC_ERROR_NONE); + } + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_ClosureCreateAndRun); + +static void BM_ClosureInitAndRun(benchmark::State& state) { + TrackCounters track_counters(state); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_closure c; + while (state.KeepRunning()) { + grpc_closure_run(&exec_ctx, grpc_closure_init(&c, DoNothing, NULL, + grpc_schedule_on_exec_ctx), + GRPC_ERROR_NONE); + } + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_ClosureInitAndRun); + +static void BM_ClosureSchedOnExecCtx(benchmark::State& state) { + TrackCounters track_counters(state); + grpc_closure c; + grpc_closure_init(&c, DoNothing, NULL, grpc_schedule_on_exec_ctx); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + grpc_closure_sched(&exec_ctx, &c, GRPC_ERROR_NONE); + grpc_exec_ctx_flush(&exec_ctx); + } + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_ClosureSchedOnExecCtx); + +static void BM_ClosureSched2OnExecCtx(benchmark::State& state) { + TrackCounters track_counters(state); + grpc_closure c1; + grpc_closure c2; + grpc_closure_init(&c1, DoNothing, NULL, grpc_schedule_on_exec_ctx); + grpc_closure_init(&c2, DoNothing, NULL, grpc_schedule_on_exec_ctx); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + grpc_closure_sched(&exec_ctx, &c1, GRPC_ERROR_NONE); + grpc_closure_sched(&exec_ctx, &c2, GRPC_ERROR_NONE); + grpc_exec_ctx_flush(&exec_ctx); + } + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_ClosureSched2OnExecCtx); + +static void BM_ClosureSched3OnExecCtx(benchmark::State& state) { + TrackCounters track_counters(state); + grpc_closure c1; + grpc_closure c2; + grpc_closure c3; + grpc_closure_init(&c1, DoNothing, NULL, grpc_schedule_on_exec_ctx); + grpc_closure_init(&c2, DoNothing, NULL, grpc_schedule_on_exec_ctx); + grpc_closure_init(&c3, DoNothing, NULL, grpc_schedule_on_exec_ctx); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + grpc_closure_sched(&exec_ctx, &c1, GRPC_ERROR_NONE); + grpc_closure_sched(&exec_ctx, &c2, GRPC_ERROR_NONE); + grpc_closure_sched(&exec_ctx, &c3, GRPC_ERROR_NONE); + grpc_exec_ctx_flush(&exec_ctx); + } + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_ClosureSched3OnExecCtx); + +static void BM_AcquireMutex(benchmark::State& state) { + TrackCounters track_counters(state); + // for comparison with the combiner stuff below + gpr_mu mu; + gpr_mu_init(&mu); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + gpr_mu_lock(&mu); + DoNothing(&exec_ctx, NULL, GRPC_ERROR_NONE); + gpr_mu_unlock(&mu); + } + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_AcquireMutex); + +static void BM_TryAcquireMutex(benchmark::State& state) { + TrackCounters track_counters(state); + // for comparison with the combiner stuff below + gpr_mu mu; + gpr_mu_init(&mu); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + if (gpr_mu_trylock(&mu)) { + DoNothing(&exec_ctx, NULL, GRPC_ERROR_NONE); + gpr_mu_unlock(&mu); + } else { + abort(); + } + } + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_TryAcquireMutex); + +static void BM_AcquireSpinlock(benchmark::State& state) { + TrackCounters track_counters(state); + // for comparison with the combiner stuff below + gpr_spinlock mu = GPR_SPINLOCK_INITIALIZER; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + gpr_spinlock_lock(&mu); + DoNothing(&exec_ctx, NULL, GRPC_ERROR_NONE); + gpr_spinlock_unlock(&mu); + } + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_AcquireSpinlock); + +static void BM_TryAcquireSpinlock(benchmark::State& state) { + TrackCounters track_counters(state); + // for comparison with the combiner stuff below + gpr_spinlock mu = GPR_SPINLOCK_INITIALIZER; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + if (gpr_spinlock_trylock(&mu)) { + DoNothing(&exec_ctx, NULL, GRPC_ERROR_NONE); + gpr_spinlock_unlock(&mu); + } else { + abort(); + } + } + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_TryAcquireSpinlock); + +static void BM_ClosureSchedOnCombiner(benchmark::State& state) { + TrackCounters track_counters(state); + grpc_combiner* combiner = grpc_combiner_create(NULL); + grpc_closure c; + grpc_closure_init(&c, DoNothing, NULL, + grpc_combiner_scheduler(combiner, false)); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + grpc_closure_sched(&exec_ctx, &c, GRPC_ERROR_NONE); + grpc_exec_ctx_flush(&exec_ctx); + } + GRPC_COMBINER_UNREF(&exec_ctx, combiner, "finished"); + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_ClosureSchedOnCombiner); + +static void BM_ClosureSched2OnCombiner(benchmark::State& state) { + TrackCounters track_counters(state); + grpc_combiner* combiner = grpc_combiner_create(NULL); + grpc_closure c1; + grpc_closure c2; + grpc_closure_init(&c1, DoNothing, NULL, + grpc_combiner_scheduler(combiner, false)); + grpc_closure_init(&c2, DoNothing, NULL, + grpc_combiner_scheduler(combiner, false)); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + grpc_closure_sched(&exec_ctx, &c1, GRPC_ERROR_NONE); + grpc_closure_sched(&exec_ctx, &c2, GRPC_ERROR_NONE); + grpc_exec_ctx_flush(&exec_ctx); + } + GRPC_COMBINER_UNREF(&exec_ctx, combiner, "finished"); + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_ClosureSched2OnCombiner); + +static void BM_ClosureSched3OnCombiner(benchmark::State& state) { + TrackCounters track_counters(state); + grpc_combiner* combiner = grpc_combiner_create(NULL); + grpc_closure c1; + grpc_closure c2; + grpc_closure c3; + grpc_closure_init(&c1, DoNothing, NULL, + grpc_combiner_scheduler(combiner, false)); + grpc_closure_init(&c2, DoNothing, NULL, + grpc_combiner_scheduler(combiner, false)); + grpc_closure_init(&c3, DoNothing, NULL, + grpc_combiner_scheduler(combiner, false)); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + grpc_closure_sched(&exec_ctx, &c1, GRPC_ERROR_NONE); + grpc_closure_sched(&exec_ctx, &c2, GRPC_ERROR_NONE); + grpc_closure_sched(&exec_ctx, &c3, GRPC_ERROR_NONE); + grpc_exec_ctx_flush(&exec_ctx); + } + GRPC_COMBINER_UNREF(&exec_ctx, combiner, "finished"); + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_ClosureSched3OnCombiner); + +static void BM_ClosureSched2OnTwoCombiners(benchmark::State& state) { + TrackCounters track_counters(state); + grpc_combiner* combiner1 = grpc_combiner_create(NULL); + grpc_combiner* combiner2 = grpc_combiner_create(NULL); + grpc_closure c1; + grpc_closure c2; + grpc_closure_init(&c1, DoNothing, NULL, + grpc_combiner_scheduler(combiner1, false)); + grpc_closure_init(&c2, DoNothing, NULL, + grpc_combiner_scheduler(combiner2, false)); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + grpc_closure_sched(&exec_ctx, &c1, GRPC_ERROR_NONE); + grpc_closure_sched(&exec_ctx, &c2, GRPC_ERROR_NONE); + grpc_exec_ctx_flush(&exec_ctx); + } + GRPC_COMBINER_UNREF(&exec_ctx, combiner1, "finished"); + GRPC_COMBINER_UNREF(&exec_ctx, combiner2, "finished"); + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_ClosureSched2OnTwoCombiners); + +static void BM_ClosureSched4OnTwoCombiners(benchmark::State& state) { + TrackCounters track_counters(state); + grpc_combiner* combiner1 = grpc_combiner_create(NULL); + grpc_combiner* combiner2 = grpc_combiner_create(NULL); + grpc_closure c1; + grpc_closure c2; + grpc_closure c3; + grpc_closure c4; + grpc_closure_init(&c1, DoNothing, NULL, + grpc_combiner_scheduler(combiner1, false)); + grpc_closure_init(&c2, DoNothing, NULL, + grpc_combiner_scheduler(combiner2, false)); + grpc_closure_init(&c3, DoNothing, NULL, + grpc_combiner_scheduler(combiner1, false)); + grpc_closure_init(&c4, DoNothing, NULL, + grpc_combiner_scheduler(combiner2, false)); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + grpc_closure_sched(&exec_ctx, &c1, GRPC_ERROR_NONE); + grpc_closure_sched(&exec_ctx, &c2, GRPC_ERROR_NONE); + grpc_closure_sched(&exec_ctx, &c3, GRPC_ERROR_NONE); + grpc_closure_sched(&exec_ctx, &c4, GRPC_ERROR_NONE); + grpc_exec_ctx_flush(&exec_ctx); + } + GRPC_COMBINER_UNREF(&exec_ctx, combiner1, "finished"); + GRPC_COMBINER_UNREF(&exec_ctx, combiner2, "finished"); + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_ClosureSched4OnTwoCombiners); + +// Helper that continuously reschedules the same closure against something until +// the benchmark is complete +class Rescheduler { + public: + Rescheduler(benchmark::State& state, grpc_closure_scheduler* scheduler) + : state_(state) { + grpc_closure_init(&closure_, Step, this, scheduler); + } + + void ScheduleFirst(grpc_exec_ctx* exec_ctx) { + grpc_closure_sched(exec_ctx, &closure_, GRPC_ERROR_NONE); + } + + void ScheduleFirstAgainstDifferentScheduler( + grpc_exec_ctx* exec_ctx, grpc_closure_scheduler* scheduler) { + grpc_closure_sched(exec_ctx, grpc_closure_create(Step, this, scheduler), + GRPC_ERROR_NONE); + } + + private: + benchmark::State& state_; + grpc_closure closure_; + + static void Step(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) { + Rescheduler* self = static_cast<Rescheduler*>(arg); + if (self->state_.KeepRunning()) { + grpc_closure_sched(exec_ctx, &self->closure_, GRPC_ERROR_NONE); + } + } +}; + +static void BM_ClosureReschedOnExecCtx(benchmark::State& state) { + TrackCounters track_counters(state); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + Rescheduler r(state, grpc_schedule_on_exec_ctx); + r.ScheduleFirst(&exec_ctx); + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_ClosureReschedOnExecCtx); + +static void BM_ClosureReschedOnCombiner(benchmark::State& state) { + TrackCounters track_counters(state); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_combiner* combiner = grpc_combiner_create(NULL); + Rescheduler r(state, grpc_combiner_scheduler(combiner, false)); + r.ScheduleFirst(&exec_ctx); + grpc_exec_ctx_flush(&exec_ctx); + GRPC_COMBINER_UNREF(&exec_ctx, combiner, "finished"); + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_ClosureReschedOnCombiner); + +static void BM_ClosureReschedOnCombinerFinally(benchmark::State& state) { + TrackCounters track_counters(state); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_combiner* combiner = grpc_combiner_create(NULL); + Rescheduler r(state, grpc_combiner_finally_scheduler(combiner, false)); + r.ScheduleFirstAgainstDifferentScheduler( + &exec_ctx, grpc_combiner_scheduler(combiner, false)); + grpc_exec_ctx_flush(&exec_ctx); + GRPC_COMBINER_UNREF(&exec_ctx, combiner, "finished"); + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_ClosureReschedOnCombinerFinally); + +BENCHMARK_MAIN(); diff --git a/test/cpp/microbenchmarks/bm_cq.cc b/test/cpp/microbenchmarks/bm_cq.cc new file mode 100644 index 0000000000..c017474bf4 --- /dev/null +++ b/test/cpp/microbenchmarks/bm_cq.cc @@ -0,0 +1,145 @@ +/* + * + * 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. + * + */ + +/* This benchmark exists to ensure that the benchmark integration is + * working */ + +#include <grpc++/completion_queue.h> +#include <grpc++/impl/grpc_library.h> +#include <grpc/grpc.h> + +#include "third_party/benchmark/include/benchmark/benchmark.h" + +extern "C" { +#include "src/core/lib/surface/completion_queue.h" +} + +namespace grpc { +namespace testing { + +static class InitializeStuff { + public: + InitializeStuff() { init_lib_.init(); } + ~InitializeStuff() { init_lib_.shutdown(); } + + private: + internal::GrpcLibrary init_lib_; + internal::GrpcLibraryInitializer init_; +} initialize_stuff; + +static void BM_CreateDestroyCpp(benchmark::State& state) { + while (state.KeepRunning()) { + CompletionQueue cq; + } +} +BENCHMARK(BM_CreateDestroyCpp); + +static void BM_CreateDestroyCore(benchmark::State& state) { + while (state.KeepRunning()) { + grpc_completion_queue_destroy(grpc_completion_queue_create(NULL)); + } +} +BENCHMARK(BM_CreateDestroyCore); + +static void DoneWithCompletionOnStack(grpc_exec_ctx* exec_ctx, void* arg, + grpc_cq_completion* completion) {} + +class DummyTag final : public CompletionQueueTag { + public: + bool FinalizeResult(void** tag, bool* status) override { return true; } +}; + +static void BM_Pass1Cpp(benchmark::State& state) { + CompletionQueue cq; + grpc_completion_queue* c_cq = cq.cq(); + while (state.KeepRunning()) { + grpc_cq_completion completion; + DummyTag dummy_tag; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_cq_begin_op(c_cq, &dummy_tag); + grpc_cq_end_op(&exec_ctx, c_cq, &dummy_tag, GRPC_ERROR_NONE, + DoneWithCompletionOnStack, NULL, &completion); + grpc_exec_ctx_finish(&exec_ctx); + void* tag; + bool ok; + cq.Next(&tag, &ok); + } +} +BENCHMARK(BM_Pass1Cpp); + +static void BM_Pass1Core(benchmark::State& state) { + grpc_completion_queue* cq = grpc_completion_queue_create(NULL); + gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + while (state.KeepRunning()) { + grpc_cq_completion completion; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_cq_begin_op(cq, NULL); + grpc_cq_end_op(&exec_ctx, cq, NULL, GRPC_ERROR_NONE, + DoneWithCompletionOnStack, NULL, &completion); + grpc_exec_ctx_finish(&exec_ctx); + grpc_completion_queue_next(cq, deadline, NULL); + } + grpc_completion_queue_destroy(cq); +} +BENCHMARK(BM_Pass1Core); + +static void BM_Pluck1Core(benchmark::State& state) { + grpc_completion_queue* cq = grpc_completion_queue_create(NULL); + gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + while (state.KeepRunning()) { + grpc_cq_completion completion; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_cq_begin_op(cq, NULL); + grpc_cq_end_op(&exec_ctx, cq, NULL, GRPC_ERROR_NONE, + DoneWithCompletionOnStack, NULL, &completion); + grpc_exec_ctx_finish(&exec_ctx); + grpc_completion_queue_pluck(cq, NULL, deadline, NULL); + } + grpc_completion_queue_destroy(cq); +} +BENCHMARK(BM_Pluck1Core); + +static void BM_EmptyCore(benchmark::State& state) { + grpc_completion_queue* cq = grpc_completion_queue_create(NULL); + gpr_timespec deadline = gpr_inf_past(GPR_CLOCK_MONOTONIC); + while (state.KeepRunning()) { + grpc_completion_queue_next(cq, deadline, NULL); + } + grpc_completion_queue_destroy(cq); +} +BENCHMARK(BM_EmptyCore); + +} // namespace testing +} // namespace grpc + +BENCHMARK_MAIN(); diff --git a/test/cpp/microbenchmarks/bm_error.cc b/test/cpp/microbenchmarks/bm_error.cc new file mode 100644 index 0000000000..8a4b86f281 --- /dev/null +++ b/test/cpp/microbenchmarks/bm_error.cc @@ -0,0 +1,248 @@ +/* + * + * 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. + * + */ + +/* Test various operations on grpc_error */ + +#include <memory> + +extern "C" { +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/transport/error_utils.h" +} + +#include "third_party/benchmark/include/benchmark/benchmark.h" + +class ErrorDeleter { + public: + void operator()(grpc_error* error) { GRPC_ERROR_UNREF(error); } +}; +typedef std::unique_ptr<grpc_error, ErrorDeleter> ErrorPtr; + +static void BM_ErrorCreate(benchmark::State& state) { + while (state.KeepRunning()) { + GRPC_ERROR_UNREF(GRPC_ERROR_CREATE("Error")); + } +} +BENCHMARK(BM_ErrorCreate); + +static void BM_ErrorCreateAndSetStatus(benchmark::State& state) { + while (state.KeepRunning()) { + GRPC_ERROR_UNREF(grpc_error_set_int(GRPC_ERROR_CREATE("Error"), + GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_ABORTED)); + } +} +BENCHMARK(BM_ErrorCreateAndSetStatus); + +static void BM_ErrorRefUnref(benchmark::State& state) { + grpc_error* error = GRPC_ERROR_CREATE("Error"); + while (state.KeepRunning()) { + GRPC_ERROR_UNREF(GRPC_ERROR_REF(error)); + } + GRPC_ERROR_UNREF(error); +} +BENCHMARK(BM_ErrorRefUnref); + +static void BM_ErrorUnrefNone(benchmark::State& state) { + while (state.KeepRunning()) { + GRPC_ERROR_UNREF(GRPC_ERROR_NONE); + } +} +BENCHMARK(BM_ErrorUnrefNone); + +static void BM_ErrorGetIntFromNoError(benchmark::State& state) { + while (state.KeepRunning()) { + intptr_t value; + grpc_error_get_int(GRPC_ERROR_NONE, GRPC_ERROR_INT_GRPC_STATUS, &value); + } +} +BENCHMARK(BM_ErrorGetIntFromNoError); + +static void BM_ErrorGetMissingInt(benchmark::State& state) { + ErrorPtr error( + grpc_error_set_int(GRPC_ERROR_CREATE("Error"), GRPC_ERROR_INT_INDEX, 1)); + while (state.KeepRunning()) { + intptr_t value; + grpc_error_get_int(error.get(), GRPC_ERROR_INT_OFFSET, &value); + } +} +BENCHMARK(BM_ErrorGetMissingInt); + +static void BM_ErrorGetPresentInt(benchmark::State& state) { + ErrorPtr error( + grpc_error_set_int(GRPC_ERROR_CREATE("Error"), GRPC_ERROR_INT_OFFSET, 1)); + while (state.KeepRunning()) { + intptr_t value; + grpc_error_get_int(error.get(), GRPC_ERROR_INT_OFFSET, &value); + } +} +BENCHMARK(BM_ErrorGetPresentInt); + +// Fixtures for tests: generate different kinds of errors +class ErrorNone { + public: + gpr_timespec deadline() const { return deadline_; } + grpc_error* error() const { return GRPC_ERROR_NONE; } + + private: + const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC); +}; + +class ErrorCancelled { + public: + gpr_timespec deadline() const { return deadline_; } + grpc_error* error() const { return GRPC_ERROR_CANCELLED; } + + private: + const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC); +}; + +class SimpleError { + public: + gpr_timespec deadline() const { return deadline_; } + grpc_error* error() const { return error_.get(); } + + private: + const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC); + ErrorPtr error_{GRPC_ERROR_CREATE("Error")}; +}; + +class ErrorWithGrpcStatus { + public: + gpr_timespec deadline() const { return deadline_; } + grpc_error* error() const { return error_.get(); } + + 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)}; +}; + +class ErrorWithHttpError { + public: + gpr_timespec deadline() const { return deadline_; } + grpc_error* error() const { return error_.get(); } + + 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)}; +}; + +class ErrorWithNestedGrpcStatus { + public: + gpr_timespec deadline() const { return deadline_; } + grpc_error* error() const { return error_.get(); } + + 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)}; + grpc_error* nested_errors_[1] = {nested_error_.get()}; + ErrorPtr error_{GRPC_ERROR_CREATE_REFERENCING("Error", nested_errors_, 1)}; +}; + +template <class Fixture> +static void BM_ErrorStringOnNewError(benchmark::State& state) { + while (state.KeepRunning()) { + Fixture fixture; + grpc_error_string(fixture.error()); + } +} + +template <class Fixture> +static void BM_ErrorStringRepeatedly(benchmark::State& state) { + Fixture fixture; + while (state.KeepRunning()) { + grpc_error_string(fixture.error()); + } +} + +template <class Fixture> +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, + NULL); + } +} + +template <class Fixture> +static void BM_ErrorGetStatusCode(benchmark::State& state) { + Fixture fixture; + while (state.KeepRunning()) { + grpc_status_code status; + grpc_error_get_status(fixture.error(), fixture.deadline(), &status, NULL, + NULL); + } +} + +template <class Fixture> +static void BM_ErrorHttpError(benchmark::State& state) { + Fixture fixture; + while (state.KeepRunning()) { + grpc_http2_error_code error; + grpc_error_get_status(fixture.error(), fixture.deadline(), NULL, NULL, + &error); + } +} + +template <class Fixture> +static void BM_HasClearGrpcStatus(benchmark::State& state) { + Fixture fixture; + while (state.KeepRunning()) { + grpc_error_has_clear_grpc_status(fixture.error()); + } +} + +#define BENCHMARK_SUITE(fixture) \ + BENCHMARK_TEMPLATE(BM_ErrorStringOnNewError, fixture); \ + BENCHMARK_TEMPLATE(BM_ErrorStringRepeatedly, fixture); \ + BENCHMARK_TEMPLATE(BM_ErrorGetStatus, fixture); \ + BENCHMARK_TEMPLATE(BM_ErrorGetStatusCode, fixture); \ + BENCHMARK_TEMPLATE(BM_ErrorHttpError, fixture); \ + BENCHMARK_TEMPLATE(BM_HasClearGrpcStatus, fixture) + +BENCHMARK_SUITE(ErrorNone); +BENCHMARK_SUITE(ErrorCancelled); +BENCHMARK_SUITE(SimpleError); +BENCHMARK_SUITE(ErrorWithGrpcStatus); +BENCHMARK_SUITE(ErrorWithHttpError); +BENCHMARK_SUITE(ErrorWithNestedGrpcStatus); + +BENCHMARK_MAIN(); diff --git a/test/cpp/microbenchmarks/bm_fullstack.cc b/test/cpp/microbenchmarks/bm_fullstack.cc new file mode 100644 index 0000000000..48e131f1be --- /dev/null +++ b/test/cpp/microbenchmarks/bm_fullstack.cc @@ -0,0 +1,1079 @@ +/* + * + * Copyright 2016, 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 gRPC end2end in various configurations */ + +#include <sstream> + +#include <grpc++/channel.h> +#include <grpc++/create_channel.h> +#include <grpc++/impl/grpc_library.h> +#include <grpc++/security/credentials.h> +#include <grpc++/security/server_credentials.h> +#include <grpc++/server.h> +#include <grpc++/server_builder.h> +#include <grpc/support/log.h> + +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/channel/channel_args.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/endpoint_pair.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/tcp_posix.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/surface/completion_queue.h" +#include "src/core/lib/surface/server.h" +#include "test/core/util/memory_counters.h" +#include "test/core/util/passthru_endpoint.h" +#include "test/core/util/port.h" +#include "test/core/util/trickle_endpoint.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 "third_party/benchmark/include/benchmark/benchmark.h" + +namespace grpc { +namespace testing { + +static class InitializeStuff { + public: + InitializeStuff() { + grpc_memory_counters_init(); + init_lib_.init(); + rq_ = grpc_resource_quota_create("bm"); + } + + ~InitializeStuff() { init_lib_.shutdown(); } + + grpc_resource_quota* rq() { return rq_; } + + private: + internal::GrpcLibrary init_lib_; + grpc_resource_quota* rq_; +} initialize_stuff; + +/******************************************************************************* + * FIXTURES + */ + +static void ApplyCommonServerBuilderConfig(ServerBuilder* b) { + b->SetMaxReceiveMessageSize(INT_MAX); + b->SetMaxSendMessageSize(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); +} + +#ifdef GPR_LOW_LEVEL_COUNTERS +extern "C" gpr_atm gpr_mu_locks; +extern "C" gpr_atm gpr_counter_atm_cas; +extern "C" gpr_atm gpr_counter_atm_add; +#endif + +class BaseFixture { + public: + void Finish(benchmark::State& s) { + std::ostringstream out; + this->AddToLabel(out, s); +#ifdef GPR_LOW_LEVEL_COUNTERS + out << " locks/iter:" << ((double)(gpr_atm_no_barrier_load(&gpr_mu_locks) - + mu_locks_at_start_) / + (double)s.iterations()) + << " atm_cas/iter:" + << ((double)(gpr_atm_no_barrier_load(&gpr_counter_atm_cas) - + atm_cas_at_start_) / + (double)s.iterations()) + << " atm_add/iter:" + << ((double)(gpr_atm_no_barrier_load(&gpr_counter_atm_add) - + atm_add_at_start_) / + (double)s.iterations()); +#endif + grpc_memory_counters counters_at_end = grpc_memory_counters_snapshot(); + out << " allocs/iter:" + << ((double)(counters_at_end.total_allocs_absolute - + counters_at_start_.total_allocs_absolute) / + (double)s.iterations()); + auto label = out.str(); + if (label.length() && label[0] == ' ') { + label = label.substr(1); + } + s.SetLabel(label); + } + + virtual void AddToLabel(std::ostream& out, benchmark::State& s) = 0; + + private: +#ifdef GPR_LOW_LEVEL_COUNTERS + const size_t mu_locks_at_start_ = gpr_atm_no_barrier_load(&gpr_mu_locks); + const size_t atm_cas_at_start_ = + gpr_atm_no_barrier_load(&gpr_counter_atm_cas); + const size_t atm_add_at_start_ = + gpr_atm_no_barrier_load(&gpr_counter_atm_add); +#endif + grpc_memory_counters counters_at_start_ = grpc_memory_counters_snapshot(); +}; + +class FullstackFixture : public BaseFixture { + public: + FullstackFixture(Service* service, const grpc::string& address) { + ServerBuilder b; + b.AddListeningPort(address, InsecureServerCredentials()); + cq_ = b.AddCompletionQueue(true); + b.RegisterService(service); + ApplyCommonServerBuilderConfig(&b); + server_ = b.BuildAndStart(); + ChannelArguments args; + ApplyCommonChannelArguments(&args); + channel_ = CreateCustomChannel(address, InsecureChannelCredentials(), args); + } + + virtual ~FullstackFixture() { + server_->Shutdown(); + cq_->Shutdown(); + void* tag; + bool ok; + while (cq_->Next(&tag, &ok)) { + } + } + + ServerCompletionQueue* cq() { return cq_.get(); } + std::shared_ptr<Channel> channel() { return channel_; } + + private: + std::unique_ptr<Server> server_; + std::unique_ptr<ServerCompletionQueue> cq_; + std::shared_ptr<Channel> channel_; +}; + +class TCP : public FullstackFixture { + public: + TCP(Service* service) : FullstackFixture(service, MakeAddress()) {} + + void AddToLabel(std::ostream& out, benchmark::State& state) {} + + private: + static grpc::string MakeAddress() { + int port = grpc_pick_unused_port_or_die(); + std::stringstream addr; + addr << "localhost:" << port; + return addr.str(); + } +}; + +class UDS : public FullstackFixture { + public: + UDS(Service* service) : FullstackFixture(service, MakeAddress()) {} + + void AddToLabel(std::ostream& out, benchmark::State& state) override {} + + private: + static grpc::string MakeAddress() { + int port = grpc_pick_unused_port_or_die(); // just for a unique id - not a + // real port + std::stringstream addr; + addr << "unix:/tmp/bm_fullstack." << port; + return addr.str(); + } +}; + +class EndpointPairFixture : public BaseFixture { + public: + EndpointPairFixture(Service* service, grpc_endpoint_pair endpoints) + : endpoint_pair_(endpoints) { + ServerBuilder b; + cq_ = b.AddCompletionQueue(true); + b.RegisterService(service); + ApplyCommonServerBuilderConfig(&b); + server_ = b.BuildAndStart(); + + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + /* add server endpoint to server_ */ + { + const grpc_channel_args* server_args = + grpc_server_get_channel_args(server_->c_server()); + server_transport_ = grpc_create_chttp2_transport( + &exec_ctx, server_args, endpoints.server, 0 /* is_client */); + + grpc_pollset** pollsets; + size_t num_pollsets = 0; + grpc_server_get_pollsets(server_->c_server(), &pollsets, &num_pollsets); + + for (size_t i = 0; i < num_pollsets; i++) { + grpc_endpoint_add_to_pollset(&exec_ctx, endpoints.server, pollsets[i]); + } + + grpc_server_setup_transport(&exec_ctx, server_->c_server(), + server_transport_, NULL, server_args); + grpc_chttp2_transport_start_reading(&exec_ctx, server_transport_, NULL); + } + + /* create channel */ + { + ChannelArguments args; + args.SetString(GRPC_ARG_DEFAULT_AUTHORITY, "test.authority"); + ApplyCommonChannelArguments(&args); + + grpc_channel_args c_args = args.c_channel_args(); + client_transport_ = + grpc_create_chttp2_transport(&exec_ctx, &c_args, endpoints.client, 1); + GPR_ASSERT(client_transport_); + grpc_channel* channel = + grpc_channel_create(&exec_ctx, "target", &c_args, + GRPC_CLIENT_DIRECT_CHANNEL, client_transport_); + grpc_chttp2_transport_start_reading(&exec_ctx, client_transport_, NULL); + + channel_ = CreateChannelInternal("", channel); + } + + grpc_exec_ctx_finish(&exec_ctx); + } + + virtual ~EndpointPairFixture() { + server_->Shutdown(); + cq_->Shutdown(); + void* tag; + bool ok; + while (cq_->Next(&tag, &ok)) { + } + } + + ServerCompletionQueue* cq() { return cq_.get(); } + std::shared_ptr<Channel> channel() { return channel_; } + + protected: + grpc_endpoint_pair endpoint_pair_; + grpc_transport* client_transport_; + grpc_transport* server_transport_; + + private: + std::unique_ptr<Server> server_; + std::unique_ptr<ServerCompletionQueue> cq_; + std::shared_ptr<Channel> channel_; +}; + +class SockPair : public EndpointPairFixture { + public: + SockPair(Service* service) + : EndpointPairFixture(service, grpc_iomgr_create_endpoint_pair( + "test", initialize_stuff.rq(), 8192)) { + } + + void AddToLabel(std::ostream& out, benchmark::State& state) {} +}; + +class InProcessCHTTP2 : public EndpointPairFixture { + public: + InProcessCHTTP2(Service* service) + : EndpointPairFixture(service, MakeEndpoints()) {} + + void AddToLabel(std::ostream& out, benchmark::State& state) { + out << " writes/iter:" + << ((double)stats_.num_writes / (double)state.iterations()); + } + + private: + grpc_passthru_endpoint_stats stats_; + + grpc_endpoint_pair MakeEndpoints() { + grpc_endpoint_pair p; + grpc_passthru_endpoint_create(&p.client, &p.server, initialize_stuff.rq(), + &stats_); + return p; + } +}; + +class TrickledCHTTP2 : public EndpointPairFixture { + public: + TrickledCHTTP2(Service* service, size_t megabits_per_second) + : EndpointPairFixture(service, MakeEndpoints(megabits_per_second)) {} + + void AddToLabel(std::ostream& out, benchmark::State& state) { + out << " writes/iter:" + << ((double)stats_.num_writes / (double)state.iterations()) + << " cli_transport_stalls/iter:" + << ((double) + client_stats_.streams_stalled_due_to_transport_flow_control / + (double)state.iterations()) + << " cli_stream_stalls/iter:" + << ((double)client_stats_.streams_stalled_due_to_stream_flow_control / + (double)state.iterations()) + << " svr_transport_stalls/iter:" + << ((double) + server_stats_.streams_stalled_due_to_transport_flow_control / + (double)state.iterations()) + << " svr_stream_stalls/iter:" + << ((double)server_stats_.streams_stalled_due_to_stream_flow_control / + (double)state.iterations()); + } + + void Step() { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + size_t client_backlog = + grpc_trickle_endpoint_trickle(&exec_ctx, endpoint_pair_.client); + size_t server_backlog = + grpc_trickle_endpoint_trickle(&exec_ctx, endpoint_pair_.server); + grpc_exec_ctx_finish(&exec_ctx); + + UpdateStats((grpc_chttp2_transport*)client_transport_, &client_stats_, + client_backlog); + UpdateStats((grpc_chttp2_transport*)server_transport_, &server_stats_, + server_backlog); + } + + private: + grpc_passthru_endpoint_stats stats_; + struct Stats { + int streams_stalled_due_to_stream_flow_control = 0; + int streams_stalled_due_to_transport_flow_control = 0; + }; + Stats client_stats_; + Stats server_stats_; + + grpc_endpoint_pair MakeEndpoints(size_t kilobits) { + grpc_endpoint_pair p; + grpc_passthru_endpoint_create(&p.client, &p.server, initialize_stuff.rq(), + &stats_); + double bytes_per_second = 125.0 * kilobits; + p.client = grpc_trickle_endpoint_create(p.client, bytes_per_second); + p.server = grpc_trickle_endpoint_create(p.server, bytes_per_second); + return p; + } + + void UpdateStats(grpc_chttp2_transport* t, Stats* s, size_t backlog) { + if (backlog == 0) { + if (t->lists[GRPC_CHTTP2_LIST_STALLED_BY_STREAM].head != NULL) { + s->streams_stalled_due_to_stream_flow_control++; + } + if (t->lists[GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT].head != NULL) { + s->streams_stalled_due_to_transport_flow_control++; + } + } + } +}; + +/******************************************************************************* + * CONTEXT MUTATORS + */ + +static const int kPregenerateKeyCount = 100000; + +template <class F> +auto MakeVector(size_t length, F f) -> std::vector<decltype(f())> { + std::vector<decltype(f())> out; + out.reserve(length); + for (size_t i = 0; i < length; i++) { + out.push_back(f()); + } + return out; +} + +class NoOpMutator { + public: + template <class ContextType> + NoOpMutator(ContextType* context) {} +}; + +template <int length> +class RandomBinaryMetadata { + public: + static const grpc::string& Key() { return kKey; } + + static const grpc::string& Value() { + return kValues[rand() % kValues.size()]; + } + + private: + static const grpc::string kKey; + static const std::vector<grpc::string> kValues; + + static grpc::string GenerateOneString() { + grpc::string s; + s.reserve(length + 1); + for (int i = 0; i < length; i++) { + s += (char)rand(); + } + return s; + } +}; + +template <int length> +const grpc::string RandomBinaryMetadata<length>::kKey = "foo-bin"; + +template <int length> +const std::vector<grpc::string> RandomBinaryMetadata<length>::kValues = + MakeVector(kPregenerateKeyCount, GenerateOneString); + +template <int length> +class RandomAsciiMetadata { + public: + static const grpc::string& Key() { return kKey; } + + static const grpc::string& Value() { + return kValues[rand() % kValues.size()]; + } + + private: + static const grpc::string kKey; + static const std::vector<grpc::string> kValues; + + static grpc::string GenerateOneString() { + grpc::string s; + s.reserve(length + 1); + for (int i = 0; i < length; i++) { + s += (char)(rand() % 26 + 'a'); + } + return s; + } +}; + +template <int length> +const grpc::string RandomAsciiMetadata<length>::kKey = "foo"; + +template <int length> +const std::vector<grpc::string> RandomAsciiMetadata<length>::kValues = + MakeVector(kPregenerateKeyCount, GenerateOneString); + +template <class Generator, int kNumKeys> +class Client_AddMetadata : public NoOpMutator { + public: + Client_AddMetadata(ClientContext* context) : NoOpMutator(context) { + for (int i = 0; i < kNumKeys; i++) { + context->AddMetadata(Generator::Key(), Generator::Value()); + } + } +}; + +template <class Generator, int kNumKeys> +class Server_AddInitialMetadata : public NoOpMutator { + public: + Server_AddInitialMetadata(ServerContext* context) : NoOpMutator(context) { + for (int i = 0; i < kNumKeys; i++) { + context->AddInitialMetadata(Generator::Key(), Generator::Value()); + } + } +}; + +/******************************************************************************* + * BENCHMARKING KERNELS + */ + +static void* tag(intptr_t x) { return reinterpret_cast<void*>(x); } + +template <class Fixture, class ClientContextMutator, class ServerContextMutator> +static void BM_UnaryPingPong(benchmark::State& state) { + EchoTestService::AsyncService service; + std::unique_ptr<Fixture> fixture(new Fixture(&service)); + EchoRequest send_request; + EchoResponse send_response; + EchoResponse recv_response; + if (state.range(0) > 0) { + send_request.set_message(std::string(state.range(0), 'a')); + } + if (state.range(1) > 0) { + send_response.set_message(std::string(state.range(1), 'a')); + } + Status recv_status; + struct ServerEnv { + ServerContext ctx; + EchoRequest recv_request; + grpc::ServerAsyncResponseWriter<EchoResponse> response_writer; + ServerEnv() : response_writer(&ctx) {} + }; + uint8_t server_env_buffer[2 * sizeof(ServerEnv)]; + ServerEnv* server_env[2] = { + reinterpret_cast<ServerEnv*>(server_env_buffer), + reinterpret_cast<ServerEnv*>(server_env_buffer + sizeof(ServerEnv))}; + new (server_env[0]) ServerEnv; + new (server_env[1]) ServerEnv; + service.RequestEcho(&server_env[0]->ctx, &server_env[0]->recv_request, + &server_env[0]->response_writer, fixture->cq(), + fixture->cq(), tag(0)); + service.RequestEcho(&server_env[1]->ctx, &server_env[1]->recv_request, + &server_env[1]->response_writer, fixture->cq(), + fixture->cq(), tag(1)); + std::unique_ptr<EchoTestService::Stub> stub( + EchoTestService::NewStub(fixture->channel())); + while (state.KeepRunning()) { + GPR_TIMER_SCOPE("BenchmarkCycle", 0); + recv_response.Clear(); + ClientContext cli_ctx; + ClientContextMutator cli_ctx_mut(&cli_ctx); + std::unique_ptr<ClientAsyncResponseReader<EchoResponse>> response_reader( + stub->AsyncEcho(&cli_ctx, send_request, fixture->cq())); + void* t; + bool ok; + GPR_ASSERT(fixture->cq()->Next(&t, &ok)); + GPR_ASSERT(ok); + GPR_ASSERT(t == tag(0) || t == tag(1)); + intptr_t slot = reinterpret_cast<intptr_t>(t); + ServerEnv* senv = server_env[slot]; + ServerContextMutator svr_ctx_mut(&senv->ctx); + senv->response_writer.Finish(send_response, Status::OK, tag(3)); + response_reader->Finish(&recv_response, &recv_status, tag(4)); + for (int i = (1 << 3) | (1 << 4); i != 0;) { + GPR_ASSERT(fixture->cq()->Next(&t, &ok)); + GPR_ASSERT(ok); + int tagnum = (int)reinterpret_cast<intptr_t>(t); + GPR_ASSERT(i & (1 << tagnum)); + i -= 1 << tagnum; + } + GPR_ASSERT(recv_status.ok()); + + senv->~ServerEnv(); + senv = new (senv) ServerEnv(); + service.RequestEcho(&senv->ctx, &senv->recv_request, &senv->response_writer, + fixture->cq(), fixture->cq(), tag(slot)); + } + fixture->Finish(state); + fixture.reset(); + server_env[0]->~ServerEnv(); + server_env[1]->~ServerEnv(); + state.SetBytesProcessed(state.range(0) * state.iterations() + + state.range(1) * state.iterations()); +} + +// 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 (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); + 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)); + 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(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(msg_size * state.iterations() * max_ping_pongs * 2); +} + +// 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; + 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())); + + 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)); + GPR_ASSERT(ok); + int i = (int)(intptr_t)t; + GPR_ASSERT(need_tags & (1 << i)); + need_tags &= ~(1 << i); + } + + while (state.KeepRunning()) { + GPR_TIMER_SCOPE("BenchmarkCycle", 0); + 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); + } + } + + 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(msg_size * state.iterations() * 2); +} + +template <class Fixture> +static void BM_PumpStreamClientToServer(benchmark::State& state) { + EchoTestService::AsyncService service; + std::unique_ptr<Fixture> fixture(new Fixture(&service)); + { + EchoRequest send_request; + EchoRequest recv_request; + if (state.range(0) > 0) { + send_request.set_message(std::string(state.range(0), 'a')); + } + 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) { + 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); + } + } + } + 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); + } + } + fixture->Finish(state); + fixture.reset(); + state.SetBytesProcessed(state.range(0) * state.iterations()); +} + +template <class Fixture> +static void BM_PumpStreamServerToClient(benchmark::State& state) { + 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')); + } + 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); + } + request_rw->Read(&recv_response, tag(0)); + while (state.KeepRunning()) { + GPR_TIMER_SCOPE("BenchmarkCycle", 0); + response_rw.Write(send_response, tag(1)); + while (true) { + 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); + } + } + } + response_rw.Finish(Status::OK, 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); + } + } + fixture->Finish(state); + fixture.reset(); + state.SetBytesProcessed(state.range(0) * state.iterations()); +} + +static void TrickleCQNext(TrickledCHTTP2* fixture, void** t, bool* ok) { + while (true) { + switch (fixture->cq()->AsyncNext( + t, ok, gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), + gpr_time_from_micros(100, GPR_TIMESPAN)))) { + case CompletionQueue::TIMEOUT: + fixture->Step(); + break; + case CompletionQueue::SHUTDOWN: + GPR_ASSERT(false); + break; + case CompletionQueue::GOT_EVENT: + return; + } + } +} + +static void BM_PumpStreamServerToClient_Trickle(benchmark::State& state) { + EchoTestService::AsyncService service; + std::unique_ptr<TrickledCHTTP2> fixture( + new TrickledCHTTP2(&service, state.range(1))); + { + EchoResponse send_response; + EchoResponse recv_response; + if (state.range(0) > 0) { + send_response.set_message(std::string(state.range(0), 'a')); + } + 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) { + TrickleCQNext(fixture.get(), &t, &ok); + GPR_ASSERT(ok); + int i = (int)(intptr_t)t; + 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) { + TrickleCQNext(fixture.get(), &t, &ok); + if (t == tag(0)) { + request_rw->Read(&recv_response, tag(0)); + } else if (t == tag(1)) { + break; + } else { + GPR_ASSERT(false); + } + } + } + response_rw.Finish(Status::OK, tag(1)); + need_tags = (1 << 0) | (1 << 1); + while (need_tags) { + TrickleCQNext(fixture.get(), &t, &ok); + int i = (int)(intptr_t)t; + GPR_ASSERT(need_tags & (1 << i)); + need_tags &= ~(1 << i); + } + } + fixture->Finish(state); + fixture.reset(); + state.SetBytesProcessed(state.range(0) * state.iterations()); +} + +/******************************************************************************* + * CONFIGURATIONS + */ + +static void SweepSizesArgs(benchmark::internal::Benchmark* b) { + b->Args({0, 0}); + for (int i = 1; i <= 128 * 1024 * 1024; i *= 8) { + b->Args({i, 0}); + b->Args({0, i}); + b->Args({i, i}); + } +} + +BENCHMARK_TEMPLATE(BM_UnaryPingPong, TCP, NoOpMutator, NoOpMutator) + ->Apply(SweepSizesArgs); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, UDS, NoOpMutator, NoOpMutator) + ->Args({0, 0}); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, SockPair, NoOpMutator, NoOpMutator) + ->Args({0, 0}); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, NoOpMutator, NoOpMutator) + ->Apply(SweepSizesArgs); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, + Client_AddMetadata<RandomBinaryMetadata<10>, 1>, NoOpMutator) + ->Args({0, 0}); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, + Client_AddMetadata<RandomBinaryMetadata<31>, 1>, NoOpMutator) + ->Args({0, 0}); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, + Client_AddMetadata<RandomBinaryMetadata<100>, 1>, + NoOpMutator) + ->Args({0, 0}); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, + Client_AddMetadata<RandomBinaryMetadata<10>, 2>, NoOpMutator) + ->Args({0, 0}); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, + Client_AddMetadata<RandomBinaryMetadata<31>, 2>, NoOpMutator) + ->Args({0, 0}); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, + Client_AddMetadata<RandomBinaryMetadata<100>, 2>, + NoOpMutator) + ->Args({0, 0}); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, NoOpMutator, + Server_AddInitialMetadata<RandomBinaryMetadata<10>, 1>) + ->Args({0, 0}); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, NoOpMutator, + Server_AddInitialMetadata<RandomBinaryMetadata<31>, 1>) + ->Args({0, 0}); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, NoOpMutator, + Server_AddInitialMetadata<RandomBinaryMetadata<100>, 1>) + ->Args({0, 0}); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, + Client_AddMetadata<RandomAsciiMetadata<10>, 1>, NoOpMutator) + ->Args({0, 0}); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, + Client_AddMetadata<RandomAsciiMetadata<31>, 1>, NoOpMutator) + ->Args({0, 0}); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, + Client_AddMetadata<RandomAsciiMetadata<100>, 1>, NoOpMutator) + ->Args({0, 0}); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, NoOpMutator, + Server_AddInitialMetadata<RandomAsciiMetadata<10>, 1>) + ->Args({0, 0}); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, NoOpMutator, + Server_AddInitialMetadata<RandomAsciiMetadata<31>, 1>) + ->Args({0, 0}); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, NoOpMutator, + Server_AddInitialMetadata<RandomAsciiMetadata<100>, 1>) + ->Args({0, 0}); +BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, NoOpMutator, + Server_AddInitialMetadata<RandomAsciiMetadata<10>, 100>) + ->Args({0, 0}); + +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) + ->Range(0, 128 * 1024 * 1024); +BENCHMARK_TEMPLATE(BM_PumpStreamServerToClient, UDS) + ->Range(0, 128 * 1024 * 1024); +BENCHMARK_TEMPLATE(BM_PumpStreamServerToClient, SockPair) + ->Range(0, 128 * 1024 * 1024); +BENCHMARK_TEMPLATE(BM_PumpStreamServerToClient, InProcessCHTTP2) + ->Range(0, 128 * 1024 * 1024); + +static void TrickleArgs(benchmark::internal::Benchmark* b) { + for (int i = 1; i <= 128 * 1024 * 1024; i *= 8) { + for (int j = 1; j <= 128 * 1024 * 1024; j *= 8) { + double expected_time = + static_cast<double>(14 + i) / (125.0 * static_cast<double>(j)); + if (expected_time > 0.01) continue; + b->Args({i, j}); + } + } +} + +BENCHMARK(BM_PumpStreamServerToClient_Trickle)->Apply(TrickleArgs); + +// 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_StreamingPingPongMsgs, TCP, NoOpMutator, NoOpMutator) + ->Range(0, 128 * 1024 * 1024); + +} // namespace testing +} // namespace grpc + +BENCHMARK_MAIN(); diff --git a/test/cpp/microbenchmarks/bm_metadata.cc b/test/cpp/microbenchmarks/bm_metadata.cc new file mode 100644 index 0000000000..f468690834 --- /dev/null +++ b/test/cpp/microbenchmarks/bm_metadata.cc @@ -0,0 +1,297 @@ +/* + * + * 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. + * + */ + +/* Test out various metadata handling primitives */ + +#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 "third_party/benchmark/include/benchmark/benchmark.h" + +static class InitializeStuff { + public: + InitializeStuff() { grpc_init(); } + ~InitializeStuff() { grpc_shutdown(); } +} initialize_stuff; + +static void BM_SliceFromStatic(benchmark::State& state) { + while (state.KeepRunning()) { + benchmark::DoNotOptimize(grpc_slice_from_static_string("abc")); + } +} +BENCHMARK(BM_SliceFromStatic); + +static void BM_SliceFromCopied(benchmark::State& state) { + while (state.KeepRunning()) { + grpc_slice_unref(grpc_slice_from_copied_string("abc")); + } +} +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) { + gpr_slice slice = grpc_slice_from_static_string("abc"); + while (state.KeepRunning()) { + grpc_slice_unref(grpc_slice_intern(slice)); + } +} +BENCHMARK(BM_SliceIntern); + +static void BM_SliceReIntern(benchmark::State& state) { + gpr_slice slice = grpc_slice_intern(grpc_slice_from_static_string("abc")); + while (state.KeepRunning()) { + grpc_slice_unref(grpc_slice_intern(slice)); + } + grpc_slice_unref(slice); +} +BENCHMARK(BM_SliceReIntern); + +static void BM_SliceInternStaticMetadata(benchmark::State& state) { + while (state.KeepRunning()) { + grpc_slice_intern(GRPC_MDSTR_GZIP); + } +} +BENCHMARK(BM_SliceInternStaticMetadata); + +static void BM_SliceInternEqualToStaticMetadata(benchmark::State& state) { + gpr_slice slice = grpc_slice_from_static_string("gzip"); + while (state.KeepRunning()) { + grpc_slice_intern(slice); + } +} +BENCHMARK(BM_SliceInternEqualToStaticMetadata); + +static void BM_MetadataFromNonInternedSlices(benchmark::State& state) { + gpr_slice k = grpc_slice_from_static_string("key"); + gpr_slice v = grpc_slice_from_static_string("value"); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_create(&exec_ctx, k, v, NULL)); + } + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_MetadataFromNonInternedSlices); + +static void BM_MetadataFromInternedSlices(benchmark::State& state) { + gpr_slice k = grpc_slice_intern(grpc_slice_from_static_string("key")); + gpr_slice v = grpc_slice_intern(grpc_slice_from_static_string("value")); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_create(&exec_ctx, k, v, NULL)); + } + grpc_exec_ctx_finish(&exec_ctx); + grpc_slice_unref(k); + grpc_slice_unref(v); +} +BENCHMARK(BM_MetadataFromInternedSlices); + +static void BM_MetadataFromInternedSlicesAlreadyInIndex( + benchmark::State& state) { + gpr_slice k = grpc_slice_intern(grpc_slice_from_static_string("key")); + gpr_slice v = grpc_slice_intern(grpc_slice_from_static_string("value")); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_mdelem seed = grpc_mdelem_create(&exec_ctx, k, v, NULL); + while (state.KeepRunning()) { + GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_create(&exec_ctx, k, v, NULL)); + } + GRPC_MDELEM_UNREF(&exec_ctx, seed); + grpc_exec_ctx_finish(&exec_ctx); + grpc_slice_unref(k); + grpc_slice_unref(v); +} +BENCHMARK(BM_MetadataFromInternedSlicesAlreadyInIndex); + +static void BM_MetadataFromInternedKey(benchmark::State& state) { + gpr_slice k = grpc_slice_intern(grpc_slice_from_static_string("key")); + gpr_slice v = grpc_slice_from_static_string("value"); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_create(&exec_ctx, k, v, NULL)); + } + grpc_exec_ctx_finish(&exec_ctx); + grpc_slice_unref(k); +} +BENCHMARK(BM_MetadataFromInternedKey); + +static void BM_MetadataFromNonInternedSlicesWithBackingStore( + benchmark::State& state) { + gpr_slice k = grpc_slice_from_static_string("key"); + gpr_slice v = grpc_slice_from_static_string("value"); + char backing_store[sizeof(grpc_mdelem_data)]; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + GRPC_MDELEM_UNREF( + &exec_ctx, + grpc_mdelem_create(&exec_ctx, k, v, + reinterpret_cast<grpc_mdelem_data*>(backing_store))); + } + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_MetadataFromNonInternedSlicesWithBackingStore); + +static void BM_MetadataFromInternedSlicesWithBackingStore( + benchmark::State& state) { + gpr_slice k = grpc_slice_intern(grpc_slice_from_static_string("key")); + gpr_slice v = grpc_slice_intern(grpc_slice_from_static_string("value")); + char backing_store[sizeof(grpc_mdelem_data)]; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + GRPC_MDELEM_UNREF( + &exec_ctx, + grpc_mdelem_create(&exec_ctx, k, v, + reinterpret_cast<grpc_mdelem_data*>(backing_store))); + } + grpc_exec_ctx_finish(&exec_ctx); + grpc_slice_unref(k); + grpc_slice_unref(v); +} +BENCHMARK(BM_MetadataFromInternedSlicesWithBackingStore); + +static void BM_MetadataFromInternedKeyWithBackingStore( + benchmark::State& state) { + gpr_slice k = grpc_slice_intern(grpc_slice_from_static_string("key")); + gpr_slice v = grpc_slice_from_static_string("value"); + char backing_store[sizeof(grpc_mdelem_data)]; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + GRPC_MDELEM_UNREF( + &exec_ctx, + grpc_mdelem_create(&exec_ctx, k, v, + reinterpret_cast<grpc_mdelem_data*>(backing_store))); + } + grpc_exec_ctx_finish(&exec_ctx); + grpc_slice_unref(k); +} +BENCHMARK(BM_MetadataFromInternedKeyWithBackingStore); + +static void BM_MetadataFromStaticMetadataStrings(benchmark::State& state) { + gpr_slice k = GRPC_MDSTR_STATUS; + gpr_slice v = GRPC_MDSTR_200; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_create(&exec_ctx, k, v, NULL)); + } + grpc_exec_ctx_finish(&exec_ctx); + grpc_slice_unref(k); +} +BENCHMARK(BM_MetadataFromStaticMetadataStrings); + +static void BM_MetadataFromStaticMetadataStringsNotIndexed( + benchmark::State& state) { + gpr_slice k = GRPC_MDSTR_STATUS; + gpr_slice v = GRPC_MDSTR_GZIP; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (state.KeepRunning()) { + GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_create(&exec_ctx, k, v, NULL)); + } + grpc_exec_ctx_finish(&exec_ctx); + grpc_slice_unref(k); +} +BENCHMARK(BM_MetadataFromStaticMetadataStringsNotIndexed); + +static void BM_MetadataRefUnrefExternal(benchmark::State& state) { + char backing_store[sizeof(grpc_mdelem_data)]; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_mdelem el = + grpc_mdelem_create(&exec_ctx, grpc_slice_from_static_string("a"), + grpc_slice_from_static_string("b"), + reinterpret_cast<grpc_mdelem_data*>(backing_store)); + while (state.KeepRunning()) { + GRPC_MDELEM_UNREF(&exec_ctx, GRPC_MDELEM_REF(el)); + } + GRPC_MDELEM_UNREF(&exec_ctx, el); + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_MetadataRefUnrefExternal); + +static void BM_MetadataRefUnrefInterned(benchmark::State& state) { + char backing_store[sizeof(grpc_mdelem_data)]; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + gpr_slice k = grpc_slice_intern(grpc_slice_from_static_string("key")); + gpr_slice v = grpc_slice_intern(grpc_slice_from_static_string("value")); + grpc_mdelem el = grpc_mdelem_create( + &exec_ctx, k, v, reinterpret_cast<grpc_mdelem_data*>(backing_store)); + grpc_slice_unref(k); + grpc_slice_unref(v); + while (state.KeepRunning()) { + GRPC_MDELEM_UNREF(&exec_ctx, GRPC_MDELEM_REF(el)); + } + GRPC_MDELEM_UNREF(&exec_ctx, el); + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_MetadataRefUnrefInterned); + +static void BM_MetadataRefUnrefAllocated(benchmark::State& state) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_mdelem el = + grpc_mdelem_create(&exec_ctx, grpc_slice_from_static_string("a"), + grpc_slice_from_static_string("b"), NULL); + while (state.KeepRunning()) { + GRPC_MDELEM_UNREF(&exec_ctx, GRPC_MDELEM_REF(el)); + } + GRPC_MDELEM_UNREF(&exec_ctx, el); + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_MetadataRefUnrefAllocated); + +static void BM_MetadataRefUnrefStatic(benchmark::State& state) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_mdelem el = + grpc_mdelem_create(&exec_ctx, GRPC_MDSTR_STATUS, GRPC_MDSTR_200, NULL); + while (state.KeepRunning()) { + GRPC_MDELEM_UNREF(&exec_ctx, GRPC_MDELEM_REF(el)); + } + GRPC_MDELEM_UNREF(&exec_ctx, el); + grpc_exec_ctx_finish(&exec_ctx); +} +BENCHMARK(BM_MetadataRefUnrefStatic); + +BENCHMARK_MAIN(); diff --git a/test/cpp/microbenchmarks/noop-benchmark.cc b/test/cpp/microbenchmarks/noop-benchmark.cc new file mode 100644 index 0000000000..99fa6d5f6e --- /dev/null +++ b/test/cpp/microbenchmarks/noop-benchmark.cc @@ -0,0 +1,45 @@ +/* + * + * 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. + * + */ + +/* This benchmark exists to ensure that the benchmark integration is + * working */ + +#include "third_party/benchmark/include/benchmark/benchmark.h" + +static void BM_NoOp(benchmark::State& state) { + while (state.KeepRunning()) { + } +} +BENCHMARK(BM_NoOp); + +BENCHMARK_MAIN(); diff --git a/test/cpp/microbenchmarks/representative_server_initial_metadata.headers b/test/cpp/microbenchmarks/representative_server_initial_metadata.headers new file mode 100644 index 0000000000..d3e6933366 --- /dev/null +++ b/test/cpp/microbenchmarks/representative_server_initial_metadata.headers @@ -0,0 +1,4 @@ +:status: 200 +content-type: application/grpc +grpc-accept-encoding: identity,deflate,gzip + diff --git a/test/cpp/microbenchmarks/representative_server_trailing_metadata.headers b/test/cpp/microbenchmarks/representative_server_trailing_metadata.headers new file mode 100644 index 0000000000..544d089853 --- /dev/null +++ b/test/cpp/microbenchmarks/representative_server_trailing_metadata.headers @@ -0,0 +1,3 @@ +grpc-status: 0 +grpc-message: + |