Merge pull request #10238 from ctiller/bm_chttp2_top

Add benchmark suite for chttp2
pull/10253/head
Craig Tiller 8 years ago committed by GitHub
commit eeb3b24f7e
  1. 42
      CMakeLists.txt
  2. 48
      Makefile
  3. 20
      build.yaml
  4. 4
      include/grpc++/support/channel_arguments.h
  5. 4
      src/core/lib/iomgr/closure.c
  6. 2
      src/core/lib/iomgr/combiner.c
  7. 587
      test/cpp/microbenchmarks/bm_chttp2_transport.cc
  8. 21
      tools/run_tests/generated/sources_and_headers.json
  9. 22
      tools/run_tests/generated/tests.json

@ -583,6 +583,9 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_dependencies(buildtests_cxx bm_chttp2_hpack) add_dependencies(buildtests_cxx bm_chttp2_hpack)
endif() endif()
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_dependencies(buildtests_cxx bm_chttp2_transport)
endif()
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_dependencies(buildtests_cxx bm_closure) add_dependencies(buildtests_cxx bm_closure)
endif() endif()
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
@ -7759,6 +7762,45 @@ endif (gRPC_BUILD_TESTS)
if (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_executable(bm_chttp2_transport
test/cpp/microbenchmarks/bm_chttp2_transport.cc
third_party/googletest/src/gtest-all.cc
)
target_include_directories(bm_chttp2_transport
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_transport
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_benchmark
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 add_executable(bm_closure
test/cpp/microbenchmarks/bm_closure.cc test/cpp/microbenchmarks/bm_closure.cc
third_party/googletest/src/gtest-all.cc third_party/googletest/src/gtest-all.cc

@ -1050,6 +1050,7 @@ auth_property_iterator_test: $(BINDIR)/$(CONFIG)/auth_property_iterator_test
bm_arena: $(BINDIR)/$(CONFIG)/bm_arena bm_arena: $(BINDIR)/$(CONFIG)/bm_arena
bm_call_create: $(BINDIR)/$(CONFIG)/bm_call_create bm_call_create: $(BINDIR)/$(CONFIG)/bm_call_create
bm_chttp2_hpack: $(BINDIR)/$(CONFIG)/bm_chttp2_hpack bm_chttp2_hpack: $(BINDIR)/$(CONFIG)/bm_chttp2_hpack
bm_chttp2_transport: $(BINDIR)/$(CONFIG)/bm_chttp2_transport
bm_closure: $(BINDIR)/$(CONFIG)/bm_closure bm_closure: $(BINDIR)/$(CONFIG)/bm_closure
bm_cq: $(BINDIR)/$(CONFIG)/bm_cq bm_cq: $(BINDIR)/$(CONFIG)/bm_cq
bm_error: $(BINDIR)/$(CONFIG)/bm_error bm_error: $(BINDIR)/$(CONFIG)/bm_error
@ -1473,6 +1474,7 @@ buildtests_cxx: privatelibs_cxx \
$(BINDIR)/$(CONFIG)/bm_arena \ $(BINDIR)/$(CONFIG)/bm_arena \
$(BINDIR)/$(CONFIG)/bm_call_create \ $(BINDIR)/$(CONFIG)/bm_call_create \
$(BINDIR)/$(CONFIG)/bm_chttp2_hpack \ $(BINDIR)/$(CONFIG)/bm_chttp2_hpack \
$(BINDIR)/$(CONFIG)/bm_chttp2_transport \
$(BINDIR)/$(CONFIG)/bm_closure \ $(BINDIR)/$(CONFIG)/bm_closure \
$(BINDIR)/$(CONFIG)/bm_cq \ $(BINDIR)/$(CONFIG)/bm_cq \
$(BINDIR)/$(CONFIG)/bm_error \ $(BINDIR)/$(CONFIG)/bm_error \
@ -1590,6 +1592,7 @@ buildtests_cxx: privatelibs_cxx \
$(BINDIR)/$(CONFIG)/bm_arena \ $(BINDIR)/$(CONFIG)/bm_arena \
$(BINDIR)/$(CONFIG)/bm_call_create \ $(BINDIR)/$(CONFIG)/bm_call_create \
$(BINDIR)/$(CONFIG)/bm_chttp2_hpack \ $(BINDIR)/$(CONFIG)/bm_chttp2_hpack \
$(BINDIR)/$(CONFIG)/bm_chttp2_transport \
$(BINDIR)/$(CONFIG)/bm_closure \ $(BINDIR)/$(CONFIG)/bm_closure \
$(BINDIR)/$(CONFIG)/bm_cq \ $(BINDIR)/$(CONFIG)/bm_cq \
$(BINDIR)/$(CONFIG)/bm_error \ $(BINDIR)/$(CONFIG)/bm_error \
@ -1935,6 +1938,8 @@ test_cxx: buildtests_cxx
$(Q) $(BINDIR)/$(CONFIG)/bm_call_create || ( echo test bm_call_create failed ; exit 1 ) $(Q) $(BINDIR)/$(CONFIG)/bm_call_create || ( echo test bm_call_create failed ; exit 1 )
$(E) "[RUN] Testing bm_chttp2_hpack" $(E) "[RUN] Testing bm_chttp2_hpack"
$(Q) $(BINDIR)/$(CONFIG)/bm_chttp2_hpack || ( echo test bm_chttp2_hpack failed ; exit 1 ) $(Q) $(BINDIR)/$(CONFIG)/bm_chttp2_hpack || ( echo test bm_chttp2_hpack failed ; exit 1 )
$(E) "[RUN] Testing bm_chttp2_transport"
$(Q) $(BINDIR)/$(CONFIG)/bm_chttp2_transport || ( echo test bm_chttp2_transport failed ; exit 1 )
$(E) "[RUN] Testing bm_closure" $(E) "[RUN] Testing bm_closure"
$(Q) $(BINDIR)/$(CONFIG)/bm_closure || ( echo test bm_closure failed ; exit 1 ) $(Q) $(BINDIR)/$(CONFIG)/bm_closure || ( echo test bm_closure failed ; exit 1 )
$(E) "[RUN] Testing bm_cq" $(E) "[RUN] Testing bm_cq"
@ -12791,6 +12796,49 @@ endif
endif endif
BM_CHTTP2_TRANSPORT_SRC = \
test/cpp/microbenchmarks/bm_chttp2_transport.cc \
BM_CHTTP2_TRANSPORT_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(BM_CHTTP2_TRANSPORT_SRC))))
ifeq ($(NO_SECURE),true)
# You can't build secure targets if you don't have OpenSSL.
$(BINDIR)/$(CONFIG)/bm_chttp2_transport: 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_transport: protobuf_dep_error
else
$(BINDIR)/$(CONFIG)/bm_chttp2_transport: $(PROTOBUF_DEP) $(BM_CHTTP2_TRANSPORT_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_benchmark.a $(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_TRANSPORT_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_benchmark.a $(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_transport
endif
endif
$(OBJDIR)/$(CONFIG)/test/cpp/microbenchmarks/bm_chttp2_transport.o: $(LIBDIR)/$(CONFIG)/libgrpc_benchmark.a $(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_transport: $(BM_CHTTP2_TRANSPORT_OBJS:.o=.dep)
ifneq ($(NO_SECURE),true)
ifneq ($(NO_DEPS),true)
-include $(BM_CHTTP2_TRANSPORT_OBJS:.o=.dep)
endif
endif
BM_CLOSURE_SRC = \ BM_CLOSURE_SRC = \
test/cpp/microbenchmarks/bm_closure.cc \ test/cpp/microbenchmarks/bm_closure.cc \

@ -3114,6 +3114,26 @@ targets:
- mac - mac
- linux - linux
- posix - posix
- name: bm_chttp2_transport
build: test
language: c++
src:
- test/cpp/microbenchmarks/bm_chttp2_transport.cc
deps:
- grpc_benchmark
- 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 - name: bm_closure
build: test build: test
language: c++ language: c++

@ -117,10 +117,10 @@ class ChannelArguments {
/// Return (by value) a c grpc_channel_args structure which points to /// Return (by value) a c grpc_channel_args structure which points to
/// arguments owned by this ChannelArguments instance /// arguments owned by this ChannelArguments instance
grpc_channel_args c_channel_args() { grpc_channel_args c_channel_args() const {
grpc_channel_args out; grpc_channel_args out;
out.num_args = args_.size(); out.num_args = args_.size();
out.args = args_.empty() ? NULL : &args_[0]; out.args = args_.empty() ? NULL : const_cast<grpc_arg*>(&args_[0]);
return out; return out;
} }

@ -33,6 +33,7 @@
#include "src/core/lib/iomgr/closure.h" #include "src/core/lib/iomgr/closure.h"
#include <assert.h>
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
@ -124,6 +125,7 @@ void grpc_closure_run(grpc_exec_ctx *exec_ctx, grpc_closure *c,
grpc_error *error) { grpc_error *error) {
GPR_TIMER_BEGIN("grpc_closure_run", 0); GPR_TIMER_BEGIN("grpc_closure_run", 0);
if (c != NULL) { if (c != NULL) {
assert(c->cb);
c->scheduler->vtable->run(exec_ctx, c, error); c->scheduler->vtable->run(exec_ctx, c, error);
} else { } else {
GRPC_ERROR_UNREF(error); GRPC_ERROR_UNREF(error);
@ -135,6 +137,7 @@ void grpc_closure_sched(grpc_exec_ctx *exec_ctx, grpc_closure *c,
grpc_error *error) { grpc_error *error) {
GPR_TIMER_BEGIN("grpc_closure_sched", 0); GPR_TIMER_BEGIN("grpc_closure_sched", 0);
if (c != NULL) { if (c != NULL) {
assert(c->cb);
c->scheduler->vtable->sched(exec_ctx, c, error); c->scheduler->vtable->sched(exec_ctx, c, error);
} else { } else {
GRPC_ERROR_UNREF(error); GRPC_ERROR_UNREF(error);
@ -146,6 +149,7 @@ void grpc_closure_list_sched(grpc_exec_ctx *exec_ctx, grpc_closure_list *list) {
grpc_closure *c = list->head; grpc_closure *c = list->head;
while (c != NULL) { while (c != NULL) {
grpc_closure *next = c->next_data.next; grpc_closure *next = c->next_data.next;
assert(c->cb);
c->scheduler->vtable->sched(exec_ctx, c, c->error_data.error); c->scheduler->vtable->sched(exec_ctx, c, c->error_data.error);
c = next; c = next;
} }

@ -33,6 +33,7 @@
#include "src/core/lib/iomgr/combiner.h" #include "src/core/lib/iomgr/combiner.h"
#include <assert.h>
#include <string.h> #include <string.h>
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
@ -216,6 +217,7 @@ static void combiner_exec(grpc_exec_ctx *exec_ctx, grpc_combiner *lock,
GPR_DEBUG, "C:%p grpc_combiner_execute c=%p cov=%d last=%" PRIdPTR, lock, GPR_DEBUG, "C:%p grpc_combiner_execute c=%p cov=%d last=%" PRIdPTR, lock,
cl, covered_by_poller, last)); cl, covered_by_poller, last));
GPR_ASSERT(last & STATE_UNORPHANED); // ensure lock has not been destroyed GPR_ASSERT(last & STATE_UNORPHANED); // ensure lock has not been destroyed
assert(cl->cb);
cl->error_data.scratch = cl->error_data.scratch =
pack_error_data((error_data){error, covered_by_poller}); pack_error_data((error_data){error, covered_by_poller});
if (covered_by_poller) { if (covered_by_poller) {

@ -0,0 +1,587 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* Microbenchmarks around CHTTP2 transport operations */
#include <grpc++/support/channel_arguments.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <string.h>
#include <memory>
#include <queue>
#include <sstream>
extern "C" {
#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
#include "src/core/ext/transport/chttp2/transport/internal.h"
#include "src/core/lib/iomgr/resource_quota.h"
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/transport/static_metadata.h"
}
#include "test/cpp/microbenchmarks/helpers.h"
#include "third_party/benchmark/include/benchmark/benchmark.h"
auto &force_library_initialization = Library::get();
////////////////////////////////////////////////////////////////////////////////
// Helper classes
//
class DummyEndpoint : public grpc_endpoint {
public:
DummyEndpoint() {
static const grpc_endpoint_vtable my_vtable = {read,
write,
get_workqueue,
add_to_pollset,
add_to_pollset_set,
shutdown,
destroy,
get_resource_user,
get_peer,
get_fd};
grpc_endpoint::vtable = &my_vtable;
ru_ = grpc_resource_user_create(Library::get().rq(), "dummy_endpoint");
}
void PushInput(grpc_exec_ctx *exec_ctx, grpc_slice slice) {
if (read_cb_ == nullptr) {
GPR_ASSERT(!have_slice_);
buffered_slice_ = slice;
have_slice_ = true;
return;
}
grpc_slice_buffer_add(slices_, slice);
grpc_closure_sched(exec_ctx, read_cb_, GRPC_ERROR_NONE);
read_cb_ = nullptr;
}
private:
grpc_resource_user *ru_;
grpc_closure *read_cb_ = nullptr;
grpc_slice_buffer *slices_ = nullptr;
bool have_slice_ = false;
grpc_slice buffered_slice_;
void QueueRead(grpc_exec_ctx *exec_ctx, grpc_slice_buffer *slices,
grpc_closure *cb) {
GPR_ASSERT(read_cb_ == nullptr);
if (have_slice_) {
have_slice_ = false;
grpc_slice_buffer_add(slices, buffered_slice_);
grpc_closure_sched(exec_ctx, cb, GRPC_ERROR_NONE);
return;
}
read_cb_ = cb;
slices_ = slices;
}
static void read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
grpc_slice_buffer *slices, grpc_closure *cb) {
static_cast<DummyEndpoint *>(ep)->QueueRead(exec_ctx, slices, cb);
}
static void write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
grpc_slice_buffer *slices, grpc_closure *cb) {
grpc_closure_sched(exec_ctx, cb, GRPC_ERROR_NONE);
}
static grpc_workqueue *get_workqueue(grpc_endpoint *ep) { return NULL; }
static void add_to_pollset(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
grpc_pollset *pollset) {}
static void add_to_pollset_set(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
grpc_pollset_set *pollset) {}
static void shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
grpc_error *why) {
grpc_resource_user_shutdown(exec_ctx,
static_cast<DummyEndpoint *>(ep)->ru_);
grpc_closure_sched(exec_ctx, static_cast<DummyEndpoint *>(ep)->read_cb_,
why);
}
static void destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
grpc_resource_user_unref(exec_ctx, static_cast<DummyEndpoint *>(ep)->ru_);
delete static_cast<DummyEndpoint *>(ep);
}
static grpc_resource_user *get_resource_user(grpc_endpoint *ep) {
return static_cast<DummyEndpoint *>(ep)->ru_;
}
static char *get_peer(grpc_endpoint *ep) { return gpr_strdup("test"); }
static int get_fd(grpc_endpoint *ep) { return 0; }
};
class Fixture {
public:
Fixture(const grpc::ChannelArguments &args, bool client) {
grpc_channel_args c_args = args.c_channel_args();
ep_ = new DummyEndpoint;
t_ = grpc_create_chttp2_transport(exec_ctx(), &c_args, ep_, client);
grpc_chttp2_transport_start_reading(exec_ctx(), t_, NULL);
FlushExecCtx();
}
void FlushExecCtx() { grpc_exec_ctx_flush(&exec_ctx_); }
~Fixture() {
grpc_transport_destroy(&exec_ctx_, t_);
grpc_exec_ctx_finish(&exec_ctx_);
}
grpc_chttp2_transport *chttp2_transport() {
return reinterpret_cast<grpc_chttp2_transport *>(t_);
}
grpc_transport *transport() { return t_; }
grpc_exec_ctx *exec_ctx() { return &exec_ctx_; }
void PushInput(grpc_slice slice) { ep_->PushInput(exec_ctx(), slice); }
private:
DummyEndpoint *ep_;
grpc_exec_ctx exec_ctx_ = GRPC_EXEC_CTX_INIT;
grpc_transport *t_;
};
static void DoNothing(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {}
class Stream {
public:
Stream(Fixture *f) : f_(f) {
GRPC_STREAM_REF_INIT(&refcount_, 1, DoNothing, nullptr, "test_stream");
stream_size_ = grpc_transport_stream_size(f->transport());
stream_ = gpr_malloc(stream_size_);
arena_ = gpr_arena_create(4096);
}
~Stream() {
gpr_free(stream_);
gpr_arena_destroy(arena_);
}
void Init(benchmark::State &state) {
memset(stream_, 0, stream_size_);
if ((state.iterations() & 0xffff) == 0) {
gpr_arena_destroy(arena_);
arena_ = gpr_arena_create(4096);
}
grpc_transport_init_stream(f_->exec_ctx(), f_->transport(),
static_cast<grpc_stream *>(stream_), &refcount_,
NULL, arena_);
}
void DestroyThen(grpc_closure *closure) {
grpc_transport_destroy_stream(f_->exec_ctx(), f_->transport(),
static_cast<grpc_stream *>(stream_), closure);
}
void Op(grpc_transport_stream_op *op) {
grpc_transport_perform_stream_op(f_->exec_ctx(), f_->transport(),
static_cast<grpc_stream *>(stream_), op);
}
grpc_chttp2_stream *chttp2_stream() {
return static_cast<grpc_chttp2_stream *>(stream_);
}
private:
Fixture *f_;
grpc_stream_refcount refcount_;
gpr_arena *arena_;
size_t stream_size_;
void *stream_;
};
class Closure : public grpc_closure {
public:
virtual ~Closure() {}
};
template <class F>
std::unique_ptr<Closure> MakeClosure(
F f, grpc_closure_scheduler *sched = grpc_schedule_on_exec_ctx) {
struct C : public Closure {
C(const F &f, grpc_closure_scheduler *sched) : f_(f) {
grpc_closure_init(this, Execute, this, sched);
}
F f_;
static void Execute(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
static_cast<C *>(arg)->f_(exec_ctx, error);
}
};
return std::unique_ptr<Closure>(new C(f, sched));
}
template <class F>
grpc_closure *MakeOnceClosure(
F f, grpc_closure_scheduler *sched = grpc_schedule_on_exec_ctx) {
struct C : public grpc_closure {
C(const F &f) : f_(f) {}
F f_;
static void Execute(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
static_cast<C *>(arg)->f_(exec_ctx, error);
delete static_cast<C *>(arg);
}
};
auto *c = new C{f};
return grpc_closure_init(c, C::Execute, c, sched);
}
////////////////////////////////////////////////////////////////////////////////
// Benchmarks
//
static void BM_StreamCreateDestroy(benchmark::State &state) {
TrackCounters track_counters;
Fixture f(grpc::ChannelArguments(), true);
Stream s(&f);
std::unique_ptr<Closure> next =
MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) {
if (!state.KeepRunning()) return;
s.Init(state);
s.DestroyThen(next.get());
});
grpc_closure_run(f.exec_ctx(), next.get(), GRPC_ERROR_NONE);
f.FlushExecCtx();
track_counters.Finish(state);
}
BENCHMARK(BM_StreamCreateDestroy);
class RepresentativeClientInitialMetadata {
public:
static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) {
return {
GRPC_MDELEM_SCHEME_HTTP, GRPC_MDELEM_METHOD_POST,
grpc_mdelem_from_slices(
exec_ctx, GRPC_MDSTR_PATH,
grpc_slice_intern(grpc_slice_from_static_string("/foo/bar"))),
grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_AUTHORITY,
grpc_slice_intern(grpc_slice_from_static_string(
"foo.test.google.fr:1234"))),
GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE_COMMA_GZIP,
GRPC_MDELEM_TE_TRAILERS,
GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC,
grpc_mdelem_from_slices(
exec_ctx, GRPC_MDSTR_USER_AGENT,
grpc_slice_intern(grpc_slice_from_static_string(
"grpc-c/3.0.0-dev (linux; chttp2; green)")))};
}
};
template <class Metadata>
static void BM_StreamCreateSendInitialMetadataDestroy(benchmark::State &state) {
TrackCounters track_counters;
Fixture f(grpc::ChannelArguments(), true);
Stream s(&f);
grpc_transport_stream_op op;
std::unique_ptr<Closure> start;
std::unique_ptr<Closure> done;
grpc_metadata_batch b;
grpc_metadata_batch_init(&b);
b.deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
std::vector<grpc_mdelem> elems = Metadata::GetElems(f.exec_ctx());
std::vector<grpc_linked_mdelem> storage(elems.size());
for (size_t i = 0; i < elems.size(); i++) {
GPR_ASSERT(GRPC_LOG_IF_ERROR(
"addmd",
grpc_metadata_batch_add_tail(f.exec_ctx(), &b, &storage[i], elems[i])));
}
f.FlushExecCtx();
start = MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) {
if (!state.KeepRunning()) return;
s.Init(state);
memset(&op, 0, sizeof(op));
op.on_complete = done.get();
op.send_initial_metadata = &b;
s.Op(&op);
});
done = MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) {
memset(&op, 0, sizeof(op));
op.cancel_error = GRPC_ERROR_CANCELLED;
s.Op(&op);
s.DestroyThen(start.get());
});
grpc_closure_sched(f.exec_ctx(), start.get(), GRPC_ERROR_NONE);
f.FlushExecCtx();
grpc_metadata_batch_destroy(f.exec_ctx(), &b);
track_counters.Finish(state);
}
BENCHMARK_TEMPLATE(BM_StreamCreateSendInitialMetadataDestroy,
RepresentativeClientInitialMetadata);
static void BM_TransportEmptyOp(benchmark::State &state) {
TrackCounters track_counters;
Fixture f(grpc::ChannelArguments(), true);
Stream s(&f);
s.Init(state);
grpc_transport_stream_op op;
std::unique_ptr<Closure> c =
MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) {
if (!state.KeepRunning()) return;
memset(&op, 0, sizeof(op));
op.on_complete = c.get();
s.Op(&op);
});
grpc_closure_sched(f.exec_ctx(), c.get(), GRPC_ERROR_NONE);
f.FlushExecCtx();
s.DestroyThen(
MakeOnceClosure([](grpc_exec_ctx *exec_ctx, grpc_error *error) {}));
f.FlushExecCtx();
track_counters.Finish(state);
}
BENCHMARK(BM_TransportEmptyOp);
static void BM_TransportStreamSend(benchmark::State &state) {
TrackCounters track_counters;
Fixture f(grpc::ChannelArguments(), true);
Stream s(&f);
s.Init(state);
grpc_transport_stream_op op;
grpc_slice_buffer_stream send_stream;
grpc_slice_buffer send_buffer;
grpc_slice_buffer_init(&send_buffer);
grpc_slice_buffer_add(&send_buffer, gpr_slice_malloc(state.range(0)));
memset(GRPC_SLICE_START_PTR(send_buffer.slices[0]), 0,
GRPC_SLICE_LENGTH(send_buffer.slices[0]));
grpc_metadata_batch b;
grpc_metadata_batch_init(&b);
b.deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
std::vector<grpc_mdelem> elems =
RepresentativeClientInitialMetadata::GetElems(f.exec_ctx());
std::vector<grpc_linked_mdelem> storage(elems.size());
for (size_t i = 0; i < elems.size(); i++) {
GPR_ASSERT(GRPC_LOG_IF_ERROR(
"addmd",
grpc_metadata_batch_add_tail(f.exec_ctx(), &b, &storage[i], elems[i])));
}
std::unique_ptr<Closure> c =
MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) {
if (!state.KeepRunning()) return;
// force outgoing window to be yuge
s.chttp2_stream()->outgoing_window_delta = 1024 * 1024 * 1024;
f.chttp2_transport()->outgoing_window = 1024 * 1024 * 1024;
grpc_slice_buffer_stream_init(&send_stream, &send_buffer, 0);
memset(&op, 0, sizeof(op));
op.on_complete = c.get();
op.send_message = &send_stream.base;
s.Op(&op);
});
memset(&op, 0, sizeof(op));
op.send_initial_metadata = &b;
op.on_complete = c.get();
s.Op(&op);
f.FlushExecCtx();
memset(&op, 0, sizeof(op));
op.cancel_error = GRPC_ERROR_CANCELLED;
s.Op(&op);
s.DestroyThen(
MakeOnceClosure([](grpc_exec_ctx *exec_ctx, grpc_error *error) {}));
f.FlushExecCtx();
track_counters.Finish(state);
grpc_metadata_batch_destroy(f.exec_ctx(), &b);
grpc_slice_buffer_destroy(&send_buffer);
}
BENCHMARK(BM_TransportStreamSend)->Range(0, 128 * 1024 * 1024);
#define SLICE_FROM_BUFFER(s) grpc_slice_from_static_buffer(s, sizeof(s) - 1)
static grpc_slice CreateIncomingDataSlice(size_t length, size_t frame_size) {
std::queue<char> unframed;
unframed.push(static_cast<uint8_t>(0));
unframed.push(static_cast<uint8_t>(length >> 24));
unframed.push(static_cast<uint8_t>(length >> 16));
unframed.push(static_cast<uint8_t>(length >> 8));
unframed.push(static_cast<uint8_t>(length));
for (size_t i = 0; i < length; i++) {
unframed.push('a');
}
std::vector<char> framed;
while (unframed.size() > frame_size) {
// frame size
framed.push_back(static_cast<uint8_t>(frame_size >> 16));
framed.push_back(static_cast<uint8_t>(frame_size >> 8));
framed.push_back(static_cast<uint8_t>(frame_size));
// data frame
framed.push_back(0);
// no flags
framed.push_back(0);
// stream id
framed.push_back(0);
framed.push_back(0);
framed.push_back(0);
framed.push_back(1);
// frame data
for (size_t i = 0; i < frame_size; i++) {
framed.push_back(unframed.front());
unframed.pop();
}
}
// frame size
framed.push_back(static_cast<uint8_t>(unframed.size() >> 16));
framed.push_back(static_cast<uint8_t>(unframed.size() >> 8));
framed.push_back(static_cast<uint8_t>(unframed.size()));
// data frame
framed.push_back(0);
// no flags
framed.push_back(0);
// stream id
framed.push_back(0);
framed.push_back(0);
framed.push_back(0);
framed.push_back(1);
while (!unframed.empty()) {
framed.push_back(unframed.front());
unframed.pop();
}
return grpc_slice_from_copied_buffer(framed.data(), framed.size());
}
static void BM_TransportStreamRecv(benchmark::State &state) {
TrackCounters track_counters;
Fixture f(grpc::ChannelArguments(), true);
Stream s(&f);
s.Init(state);
grpc_transport_stream_op op;
grpc_byte_stream *recv_stream;
grpc_slice incoming_data = CreateIncomingDataSlice(state.range(0), 16384);
grpc_metadata_batch b;
grpc_metadata_batch_init(&b);
grpc_metadata_batch b_recv;
grpc_metadata_batch_init(&b_recv);
b.deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
std::vector<grpc_mdelem> elems =
RepresentativeClientInitialMetadata::GetElems(f.exec_ctx());
std::vector<grpc_linked_mdelem> storage(elems.size());
for (size_t i = 0; i < elems.size(); i++) {
GPR_ASSERT(GRPC_LOG_IF_ERROR(
"addmd",
grpc_metadata_batch_add_tail(f.exec_ctx(), &b, &storage[i], elems[i])));
}
std::unique_ptr<Closure> do_nothing =
MakeClosure([](grpc_exec_ctx *exec_ctx, grpc_error *error) {});
uint32_t received;
std::unique_ptr<Closure> drain_start;
std::unique_ptr<Closure> drain;
std::unique_ptr<Closure> drain_continue;
grpc_slice recv_slice;
std::unique_ptr<Closure> c =
MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) {
if (!state.KeepRunning()) return;
// force outgoing window to be yuge
s.chttp2_stream()->incoming_window_delta = 1024 * 1024 * 1024;
f.chttp2_transport()->incoming_window = 1024 * 1024 * 1024;
received = 0;
memset(&op, 0, sizeof(op));
op.on_complete = do_nothing.get();
op.recv_message = &recv_stream;
op.recv_message_ready = drain_start.get();
s.Op(&op);
f.PushInput(grpc_slice_ref(incoming_data));
});
drain_start = MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) {
if (recv_stream == NULL) {
GPR_ASSERT(!state.KeepRunning());
return;
}
grpc_closure_run(exec_ctx, drain.get(), GRPC_ERROR_NONE);
});
drain = MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) {
do {
if (received == recv_stream->length) {
grpc_byte_stream_destroy(exec_ctx, recv_stream);
grpc_closure_sched(exec_ctx, c.get(), GRPC_ERROR_NONE);
return;
}
} while (grpc_byte_stream_next(exec_ctx, recv_stream, &recv_slice,
recv_stream->length - received,
drain_continue.get()));
});
drain_continue = MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) {
received += GRPC_SLICE_LENGTH(recv_slice);
grpc_slice_unref_internal(exec_ctx, recv_slice);
grpc_closure_run(exec_ctx, drain.get(), GRPC_ERROR_NONE);
});
memset(&op, 0, sizeof(op));
op.send_initial_metadata = &b;
op.recv_initial_metadata = &b_recv;
op.on_complete = c.get();
s.Op(&op);
f.PushInput(SLICE_FROM_BUFFER(
"\x00\x00\x00\x04\x00\x00\x00\x00\x00"
// Generated using:
// tools/codegen/core/gen_header_frame.py <
// test/cpp/microbenchmarks/representative_server_initial_metadata.headers
"\x00\x00X\x01\x04\x00\x00\x00\x01"
"\x10\x07:status\x03"
"200"
"\x10\x0c"
"content-type\x10"
"application/grpc"
"\x10\x14grpc-accept-encoding\x15identity,deflate,gzip"));
f.FlushExecCtx();
memset(&op, 0, sizeof(op));
op.cancel_error = GRPC_ERROR_CANCELLED;
s.Op(&op);
s.DestroyThen(
MakeOnceClosure([](grpc_exec_ctx *exec_ctx, grpc_error *error) {}));
f.FlushExecCtx();
track_counters.Finish(state);
grpc_metadata_batch_destroy(f.exec_ctx(), &b);
grpc_metadata_batch_destroy(f.exec_ctx(), &b_recv);
grpc_slice_unref(incoming_data);
}
BENCHMARK(BM_TransportStreamRecv)->Range(0, 128 * 1024 * 1024);
BENCHMARK_MAIN();

@ -2489,6 +2489,27 @@
"third_party": false, "third_party": false,
"type": "target" "type": "target"
}, },
{
"deps": [
"benchmark",
"gpr",
"gpr_test_util",
"grpc",
"grpc++",
"grpc++_test_util",
"grpc_benchmark",
"grpc_test_util"
],
"headers": [],
"is_filegroup": false,
"language": "c++",
"name": "bm_chttp2_transport",
"src": [
"test/cpp/microbenchmarks/bm_chttp2_transport.cc"
],
"third_party": false,
"type": "target"
},
{ {
"deps": [ "deps": [
"benchmark", "benchmark",

@ -2671,6 +2671,28 @@
"posix" "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_transport",
"platforms": [
"linux",
"mac",
"posix"
]
},
{ {
"args": [ "args": [
"--benchmark_min_time=0" "--benchmark_min_time=0"

Loading…
Cancel
Save