diff --git a/CMakeLists.txt b/CMakeLists.txt index 6de986e19924dcd39ddb952359d0eb18789f7270..e7e0a487dc7308fc2c214898a8948bc4ea743741 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -570,6 +570,9 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_cxx bm_call_create) endif() if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) +add_dependencies(buildtests_cxx bm_chttp2_hpack) +endif() +if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_cxx bm_closure) endif() if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) @@ -7406,6 +7409,44 @@ endif (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) +add_executable(bm_chttp2_hpack + test/cpp/microbenchmarks/bm_chttp2_hpack.cc + third_party/googletest/src/gtest-all.cc +) + + +target_include_directories(bm_chttp2_hpack + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + PRIVATE ${BORINGSSL_ROOT_DIR}/include + PRIVATE ${PROTOBUF_ROOT_DIR}/src + PRIVATE ${BENCHMARK_ROOT_DIR}/include + PRIVATE ${ZLIB_ROOT_DIR} + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include + PRIVATE third_party/googletest/include + PRIVATE third_party/googletest + PRIVATE ${_gRPC_PROTO_GENS_DIR} +) + +target_link_libraries(bm_chttp2_hpack + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + benchmark + grpc++_test_util + grpc_test_util + grpc++ + grpc + gpr_test_util + gpr + ${_gRPC_GFLAGS_LIBRARIES} +) + +endif() +endif (gRPC_BUILD_TESTS) +if (gRPC_BUILD_TESTS) +if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) + add_executable(bm_closure test/cpp/microbenchmarks/bm_closure.cc third_party/googletest/src/gtest-all.cc diff --git a/Makefile b/Makefile index 558c0eea0f0b7a1d812d1725151843c741127799..14f773ae05e7888caa4400ff08b046e76b3eea9c 100644 --- a/Makefile +++ b/Makefile @@ -1041,6 +1041,7 @@ alarm_cpp_test: $(BINDIR)/$(CONFIG)/alarm_cpp_test async_end2end_test: $(BINDIR)/$(CONFIG)/async_end2end_test auth_property_iterator_test: $(BINDIR)/$(CONFIG)/auth_property_iterator_test bm_call_create: $(BINDIR)/$(CONFIG)/bm_call_create +bm_chttp2_hpack: $(BINDIR)/$(CONFIG)/bm_chttp2_hpack bm_closure: $(BINDIR)/$(CONFIG)/bm_closure bm_cq: $(BINDIR)/$(CONFIG)/bm_cq bm_error: $(BINDIR)/$(CONFIG)/bm_error @@ -1449,6 +1450,7 @@ buildtests_cxx: privatelibs_cxx \ $(BINDIR)/$(CONFIG)/async_end2end_test \ $(BINDIR)/$(CONFIG)/auth_property_iterator_test \ $(BINDIR)/$(CONFIG)/bm_call_create \ + $(BINDIR)/$(CONFIG)/bm_chttp2_hpack \ $(BINDIR)/$(CONFIG)/bm_closure \ $(BINDIR)/$(CONFIG)/bm_cq \ $(BINDIR)/$(CONFIG)/bm_error \ @@ -1558,6 +1560,7 @@ buildtests_cxx: privatelibs_cxx \ $(BINDIR)/$(CONFIG)/async_end2end_test \ $(BINDIR)/$(CONFIG)/auth_property_iterator_test \ $(BINDIR)/$(CONFIG)/bm_call_create \ + $(BINDIR)/$(CONFIG)/bm_chttp2_hpack \ $(BINDIR)/$(CONFIG)/bm_closure \ $(BINDIR)/$(CONFIG)/bm_cq \ $(BINDIR)/$(CONFIG)/bm_error \ @@ -1879,6 +1882,8 @@ test_cxx: buildtests_cxx $(Q) $(BINDIR)/$(CONFIG)/auth_property_iterator_test || ( echo test auth_property_iterator_test failed ; exit 1 ) $(E) "[RUN] Testing bm_call_create" $(Q) $(BINDIR)/$(CONFIG)/bm_call_create || ( echo test bm_call_create failed ; exit 1 ) + $(E) "[RUN] Testing bm_chttp2_hpack" + $(Q) $(BINDIR)/$(CONFIG)/bm_chttp2_hpack || ( echo test bm_chttp2_hpack failed ; exit 1 ) $(E) "[RUN] Testing bm_closure" $(Q) $(BINDIR)/$(CONFIG)/bm_closure || ( echo test bm_closure failed ; exit 1 ) $(E) "[RUN] Testing bm_cq" @@ -12347,6 +12352,49 @@ endif endif +BM_CHTTP2_HPACK_SRC = \ + test/cpp/microbenchmarks/bm_chttp2_hpack.cc \ + +BM_CHTTP2_HPACK_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(BM_CHTTP2_HPACK_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/bm_chttp2_hpack: openssl_dep_error + +else + + + + +ifeq ($(NO_PROTOBUF),true) + +# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+. + +$(BINDIR)/$(CONFIG)/bm_chttp2_hpack: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/bm_chttp2_hpack: $(PROTOBUF_DEP) $(BM_CHTTP2_HPACK_OBJS) $(LIBDIR)/$(CONFIG)/libbenchmark.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(BM_CHTTP2_HPACK_OBJS) $(LIBDIR)/$(CONFIG)/libbenchmark.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/bm_chttp2_hpack + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/microbenchmarks/bm_chttp2_hpack.o: $(LIBDIR)/$(CONFIG)/libbenchmark.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + +deps_bm_chttp2_hpack: $(BM_CHTTP2_HPACK_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(BM_CHTTP2_HPACK_OBJS:.o=.dep) +endif +endif + + BM_CLOSURE_SRC = \ test/cpp/microbenchmarks/bm_closure.cc \ diff --git a/build.yaml b/build.yaml index 3e2dca5b1ce7a7cd790e1249106980bc04dd5484..a9287e663206bba271c58a87d302b6ed0ed3b2d1 100644 --- a/build.yaml +++ b/build.yaml @@ -2981,6 +2981,25 @@ targets: - mac - linux - posix +- name: bm_chttp2_hpack + build: test + language: c++ + src: + - test/cpp/microbenchmarks/bm_chttp2_hpack.cc + deps: + - benchmark + - grpc++_test_util + - grpc_test_util + - grpc++ + - grpc + - gpr_test_util + - gpr + args: + - --benchmark_min_time=0 + platforms: + - mac + - linux + - posix - name: bm_closure build: test language: c++ diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.c b/src/core/ext/transport/chttp2/transport/hpack_encoder.c index 63df8e135f8abcbd104e2169bc027b70ef9d8e75..84586cd99887416ca88c53dd383310a302c87a53 100644 --- a/src/core/ext/transport/chttp2/transport/hpack_encoder.c +++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.c @@ -173,6 +173,7 @@ static void add_header_data(framer_state *st, grpc_slice slice) { static uint8_t *add_tiny_header_data(framer_state *st, size_t len) { ensure_space(st, len); + st->stats->header_bytes += len; return grpc_slice_buffer_tiny_add(st->output, len); } diff --git a/test/cpp/microbenchmarks/bm_chttp2_hpack.cc b/test/cpp/microbenchmarks/bm_chttp2_hpack.cc new file mode 100644 index 0000000000000000000000000000000000000000..5fb3f37130653dc483fba77b5592e4cd9809607f --- /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/representative_server_initial_metadata.headers b/test/cpp/microbenchmarks/representative_server_initial_metadata.headers new file mode 100644 index 0000000000000000000000000000000000000000..d3e69333668c99ce3b53e30a73c65234e005bf57 --- /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 0000000000000000000000000000000000000000..544d089853995bfd1a6a931e50994c6d08b06979 --- /dev/null +++ b/test/cpp/microbenchmarks/representative_server_trailing_metadata.headers @@ -0,0 +1,3 @@ +grpc-status: 0 +grpc-message: + diff --git a/tools/codegen/core/gen_header_frame.py b/tools/codegen/core/gen_header_frame.py index ee476267f23ac230d21e51ae183b1dc9ab993791..c92ff3c579d2e0eeebba959edc40a4178d826f5c 100755 --- a/tools/codegen/core/gen_header_frame.py +++ b/tools/codegen/core/gen_header_frame.py @@ -37,8 +37,41 @@ import json import sys +import argparse -set_end_stream = len(sys.argv) > 1 and sys.argv[1] == '--set_end_stream' +def append_never_indexed(payload_line, n, count, key, value): + payload_line.append(0x10) + assert(len(key) <= 126) + payload_line.append(len(key)) + payload_line.extend(ord(c) for c in key) + assert(len(value) <= 126) + payload_line.append(len(value)) + payload_line.extend(ord(c) for c in value) + +def append_inc_indexed(payload_line, n, count, key, value): + payload_line.append(0x40) + assert(len(key) <= 126) + payload_line.append(len(key)) + payload_line.extend(ord(c) for c in key) + assert(len(value) <= 126) + payload_line.append(len(value)) + payload_line.extend(ord(c) for c in value) + +def append_pre_indexed(payload_line, n, count, key, value): + payload_line.append(0x80 + 61 + count - n) + +_COMPRESSORS = { + 'never': append_never_indexed, + 'inc': append_inc_indexed, + 'pre': append_pre_indexed, +} + +argp = argparse.ArgumentParser('Generate header frames') +argp.add_argument('--set_end_stream', default=False, action='store_const', const=True) +argp.add_argument('--no_framing', default=False, action='store_const', const=True) +argp.add_argument('--compression', choices=sorted(_COMPRESSORS.keys()), default='never') +argp.add_argument('--hex', default=False, action='store_const', const=True) +args = argp.parse_args() # parse input, fill in vals vals = [] @@ -52,38 +85,37 @@ for line in sys.stdin: vals.append((key, value)) # generate frame payload binary data -payload_bytes = [[]] # reserve space for header +payload_bytes = [] +if not args.no_framing: + payload_bytes.append([]) # reserve space for header payload_len = 0 +n = 0 for key, value in vals: payload_line = [] - payload_line.append(0x10) - assert(len(key) <= 126) - payload_line.append(len(key)) - payload_line.extend(ord(c) for c in key) - assert(len(value) <= 126) - payload_line.append(len(value)) - payload_line.extend(ord(c) for c in value) + _COMPRESSORS[args.compression](payload_line, n, len(vals), key, value) + n += 1 payload_len += len(payload_line) payload_bytes.append(payload_line) # fill in header -flags = 0x04 # END_HEADERS -if set_end_stream: - flags |= 0x01 # END_STREAM -payload_bytes[0].extend([ - (payload_len >> 16) & 0xff, - (payload_len >> 8) & 0xff, - (payload_len) & 0xff, - # header frame - 0x01, - # flags - flags, - # stream id - 0x00, - 0x00, - 0x00, - 0x01 -]) +if not args.no_framing: + flags = 0x04 # END_HEADERS + if args.set_end_stream: + flags |= 0x01 # END_STREAM + payload_bytes[0].extend([ + (payload_len >> 16) & 0xff, + (payload_len >> 8) & 0xff, + (payload_len) & 0xff, + # header frame + 0x01, + # flags + flags, + # stream id + 0x00, + 0x00, + 0x00, + 0x01 + ]) hex_bytes = [ord(c) for c in "abcdefABCDEF0123456789"] @@ -105,6 +137,11 @@ def esc_c(line): return out + "\"" # dump bytes -for line in payload_bytes: - print esc_c(line) - +if args.hex: + all_bytes = [] + for line in payload_bytes: + all_bytes.extend(line) + print '{%s}' % ', '.join('0x%02x' % c for c in all_bytes) +else: + for line in payload_bytes: + print esc_c(line) diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json index f83a63987102ea3531c94ad8f3659d28ffd43f5d..33d024921a4b36911e72ce37927496809c06b7ba 100644 --- a/tools/run_tests/generated/sources_and_headers.json +++ b/tools/run_tests/generated/sources_and_headers.json @@ -2332,6 +2332,26 @@ "third_party": false, "type": "target" }, + { + "deps": [ + "benchmark", + "gpr", + "gpr_test_util", + "grpc", + "grpc++", + "grpc++_test_util", + "grpc_test_util" + ], + "headers": [], + "is_filegroup": false, + "language": "c++", + "name": "bm_chttp2_hpack", + "src": [ + "test/cpp/microbenchmarks/bm_chttp2_hpack.cc" + ], + "third_party": false, + "type": "target" + }, { "deps": [ "benchmark", diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index b2b41413ac798fc331bde4f8d4b2a7fac4db2686..35bb43f8727783ad411cbc79ef3b54209af4b8cb 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -2469,6 +2469,28 @@ "posix" ] }, + { + "args": [ + "--benchmark_min_time=0" + ], + "ci_platforms": [ + "linux", + "mac", + "posix" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": false, + "language": "c++", + "name": "bm_chttp2_hpack", + "platforms": [ + "linux", + "mac", + "posix" + ] + }, { "args": [ "--benchmark_min_time=0" diff --git a/tools/run_tests/run_microbenchmark.py b/tools/run_tests/run_microbenchmark.py index c5247761ef69cd2d20fdab524b9224772dd80d69..8b74d31d50046b92cb00c564dfda83ca484fadb8 100755 --- a/tools/run_tests/run_microbenchmark.py +++ b/tools/run_tests/run_microbenchmark.py @@ -199,7 +199,12 @@ argp.add_argument('-c', '--collect', default=sorted(collectors.keys()), help='Which collectors should be run against each benchmark') argp.add_argument('-b', '--benchmarks', - default=['bm_fullstack', 'bm_closure', 'bm_cq', 'bm_call_create', 'bm_error'], + default=['bm_fullstack', + 'bm_closure', + 'bm_cq', + 'bm_call_create', + 'bm_error', + 'bm_chttp2_hpack'], nargs='+', type=str, help='Which microbenchmarks should be run')