Introduce grpc_byte_buffer_reader_peek and use it for Protobuf parsing.

grpc_byte_buffer_reader_next() copies and references the slice. This
is not always necessary since the caller will not use the slice
after destroying the byte buffer.

A prominent example is the protobuf parser, which
calls grpc_byte_buffer_reader_next() and immediately unrefs the slice
after the call. This ref() and unref() calls can be very expensive
in the hot path.

This commit introduces grpc_byte_buffer_reader_peek() which
essentialy return a pointer to the slice in the buffer, i.e.,
no copies, and no refs.

QPS of 1MiB 1 Channel callback benchmark increases by 5%.
More importantly insructions per cycle is increased by 10%.

Also add tests and benchmarks for byte_buffer_reader_peek()

This commit reaplies 509e77a5a3
This commit is contained in:
Soheil Hassas Yeganeh
2019-02-27 23:41:18 -05:00
parent 50576179f8
commit ad1b3e5094
12 changed files with 194 additions and 11 deletions

View File

@@ -29,9 +29,8 @@
namespace grpc {
namespace testing {
auto& force_library_initialization = Library::get();
static void BM_ByteBuffer_Copy(benchmark::State& state) {
Library::get();
int num_slices = state.range(0);
size_t slice_size = state.range(1);
std::vector<grpc::Slice> slices;
@@ -48,6 +47,74 @@ static void BM_ByteBuffer_Copy(benchmark::State& state) {
}
BENCHMARK(BM_ByteBuffer_Copy)->Ranges({{1, 64}, {1, 1024 * 1024}});
static void BM_ByteBufferReader_Next(benchmark::State& state) {
Library::get();
const int num_slices = state.range(0);
constexpr size_t kSliceSize = 16;
std::vector<grpc_slice> slices;
for (int i = 0; i < num_slices; ++i) {
std::unique_ptr<char[]> buf(new char[kSliceSize]);
slices.emplace_back(g_core_codegen_interface->grpc_slice_from_copied_buffer(
buf.get(), kSliceSize));
}
grpc_byte_buffer* bb = g_core_codegen_interface->grpc_raw_byte_buffer_create(
slices.data(), num_slices);
grpc_byte_buffer_reader reader;
GPR_ASSERT(
g_core_codegen_interface->grpc_byte_buffer_reader_init(&reader, bb));
while (state.KeepRunning()) {
grpc_slice* slice;
if (GPR_UNLIKELY(!g_core_codegen_interface->grpc_byte_buffer_reader_peek(
&reader, &slice))) {
g_core_codegen_interface->grpc_byte_buffer_reader_destroy(&reader);
GPR_ASSERT(
g_core_codegen_interface->grpc_byte_buffer_reader_init(&reader, bb));
continue;
}
}
g_core_codegen_interface->grpc_byte_buffer_reader_destroy(&reader);
g_core_codegen_interface->grpc_byte_buffer_destroy(bb);
for (auto& slice : slices) {
g_core_codegen_interface->grpc_slice_unref(slice);
}
}
BENCHMARK(BM_ByteBufferReader_Next)->Ranges({{64 * 1024, 1024 * 1024}});
static void BM_ByteBufferReader_Peek(benchmark::State& state) {
Library::get();
const int num_slices = state.range(0);
constexpr size_t kSliceSize = 16;
std::vector<grpc_slice> slices;
for (int i = 0; i < num_slices; ++i) {
std::unique_ptr<char[]> buf(new char[kSliceSize]);
slices.emplace_back(g_core_codegen_interface->grpc_slice_from_copied_buffer(
buf.get(), kSliceSize));
}
grpc_byte_buffer* bb = g_core_codegen_interface->grpc_raw_byte_buffer_create(
slices.data(), num_slices);
grpc_byte_buffer_reader reader;
GPR_ASSERT(
g_core_codegen_interface->grpc_byte_buffer_reader_init(&reader, bb));
while (state.KeepRunning()) {
grpc_slice* slice;
if (GPR_UNLIKELY(!g_core_codegen_interface->grpc_byte_buffer_reader_peek(
&reader, &slice))) {
g_core_codegen_interface->grpc_byte_buffer_reader_destroy(&reader);
GPR_ASSERT(
g_core_codegen_interface->grpc_byte_buffer_reader_init(&reader, bb));
continue;
}
}
g_core_codegen_interface->grpc_byte_buffer_reader_destroy(&reader);
g_core_codegen_interface->grpc_byte_buffer_destroy(bb);
for (auto& slice : slices) {
g_core_codegen_interface->grpc_slice_unref(slice);
}
}
BENCHMARK(BM_ByteBufferReader_Peek)->Ranges({{64 * 1024, 1024 * 1024}});
} // namespace testing
} // namespace grpc