From 49ce265954e6555a00a66750649f26db1f442dfc Mon Sep 17 00:00:00 2001 From: Juan Alvarez Date: Sun, 22 Dec 2019 15:51:34 -0600 Subject: [PATCH] chore: Add decode benchmarks (#207) * disable current benchmarks * temporarily expose Streaming constructor * hide Streaming constructor from docs * remove crossbeam-queue from deny skip list * add decode benches * fmt * use Bytes as MockDecoder Item --- deny.toml | 1 - tonic/Cargo.toml | 5 +- tonic/{benches => benches-disabled}/README.md | 0 .../bench_main.rs | 0 .../compiled_protos/diverse_types.rs | 0 .../benchmarks/compiled_protos/helloworld.rs | 0 .../benchmarks/compiled_protos/mod.rs | 0 .../benchmarks/mod.rs | 0 .../benchmarks/request_response.rs | 0 .../request_response_diverse_types.rs | 0 .../benchmarks/utils.rs | 0 .../proto/diverse_types/diverse_types.proto | 0 .../proto/helloworld/helloworld.proto | 0 tonic/benches/decode.rs | 159 ++++++++++++++++++ tonic/src/codec/decode.rs | 3 +- 15 files changed, 165 insertions(+), 3 deletions(-) rename tonic/{benches => benches-disabled}/README.md (100%) rename tonic/{benches => benches-disabled}/bench_main.rs (100%) rename tonic/{benches => benches-disabled}/benchmarks/compiled_protos/diverse_types.rs (100%) rename tonic/{benches => benches-disabled}/benchmarks/compiled_protos/helloworld.rs (100%) rename tonic/{benches => benches-disabled}/benchmarks/compiled_protos/mod.rs (100%) rename tonic/{benches => benches-disabled}/benchmarks/mod.rs (100%) rename tonic/{benches => benches-disabled}/benchmarks/request_response.rs (100%) rename tonic/{benches => benches-disabled}/benchmarks/request_response_diverse_types.rs (100%) rename tonic/{benches => benches-disabled}/benchmarks/utils.rs (100%) rename tonic/{benches => benches-disabled}/proto/diverse_types/diverse_types.proto (100%) rename tonic/{benches => benches-disabled}/proto/helloworld/helloworld.proto (100%) create mode 100644 tonic/benches/decode.rs diff --git a/deny.toml b/deny.toml index a7713265a..6a3b18e51 100644 --- a/deny.toml +++ b/deny.toml @@ -15,7 +15,6 @@ deny = [ { name = "term" }, ] skip = [ - { name = "crossbeam-queue", version = "=0.2.0" }, { name = "bytes", version = "=0.4.12" }, ] skip-tree = [ diff --git a/tonic/Cargo.toml b/tonic/Cargo.toml index 0a6f850f2..b549d2734 100644 --- a/tonic/Cargo.toml +++ b/tonic/Cargo.toml @@ -79,10 +79,13 @@ rustls-native-certs = { version = "0.1", optional = true } tokio = { version = "0.2", features = ["rt-core", "macros"] } static_assertions = "1.0" rand = "0.6" -criterion = "0.3" +bencher = "0.1.5" [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] +[[bench]] +name = "decode" +harness = false diff --git a/tonic/benches/README.md b/tonic/benches-disabled/README.md similarity index 100% rename from tonic/benches/README.md rename to tonic/benches-disabled/README.md diff --git a/tonic/benches/bench_main.rs b/tonic/benches-disabled/bench_main.rs similarity index 100% rename from tonic/benches/bench_main.rs rename to tonic/benches-disabled/bench_main.rs diff --git a/tonic/benches/benchmarks/compiled_protos/diverse_types.rs b/tonic/benches-disabled/benchmarks/compiled_protos/diverse_types.rs similarity index 100% rename from tonic/benches/benchmarks/compiled_protos/diverse_types.rs rename to tonic/benches-disabled/benchmarks/compiled_protos/diverse_types.rs diff --git a/tonic/benches/benchmarks/compiled_protos/helloworld.rs b/tonic/benches-disabled/benchmarks/compiled_protos/helloworld.rs similarity index 100% rename from tonic/benches/benchmarks/compiled_protos/helloworld.rs rename to tonic/benches-disabled/benchmarks/compiled_protos/helloworld.rs diff --git a/tonic/benches/benchmarks/compiled_protos/mod.rs b/tonic/benches-disabled/benchmarks/compiled_protos/mod.rs similarity index 100% rename from tonic/benches/benchmarks/compiled_protos/mod.rs rename to tonic/benches-disabled/benchmarks/compiled_protos/mod.rs diff --git a/tonic/benches/benchmarks/mod.rs b/tonic/benches-disabled/benchmarks/mod.rs similarity index 100% rename from tonic/benches/benchmarks/mod.rs rename to tonic/benches-disabled/benchmarks/mod.rs diff --git a/tonic/benches/benchmarks/request_response.rs b/tonic/benches-disabled/benchmarks/request_response.rs similarity index 100% rename from tonic/benches/benchmarks/request_response.rs rename to tonic/benches-disabled/benchmarks/request_response.rs diff --git a/tonic/benches/benchmarks/request_response_diverse_types.rs b/tonic/benches-disabled/benchmarks/request_response_diverse_types.rs similarity index 100% rename from tonic/benches/benchmarks/request_response_diverse_types.rs rename to tonic/benches-disabled/benchmarks/request_response_diverse_types.rs diff --git a/tonic/benches/benchmarks/utils.rs b/tonic/benches-disabled/benchmarks/utils.rs similarity index 100% rename from tonic/benches/benchmarks/utils.rs rename to tonic/benches-disabled/benchmarks/utils.rs diff --git a/tonic/benches/proto/diverse_types/diverse_types.proto b/tonic/benches-disabled/proto/diverse_types/diverse_types.proto similarity index 100% rename from tonic/benches/proto/diverse_types/diverse_types.proto rename to tonic/benches-disabled/proto/diverse_types/diverse_types.proto diff --git a/tonic/benches/proto/helloworld/helloworld.proto b/tonic/benches-disabled/proto/helloworld/helloworld.proto similarity index 100% rename from tonic/benches/proto/helloworld/helloworld.proto rename to tonic/benches-disabled/proto/helloworld/helloworld.proto diff --git a/tonic/benches/decode.rs b/tonic/benches/decode.rs new file mode 100644 index 000000000..91160f78e --- /dev/null +++ b/tonic/benches/decode.rs @@ -0,0 +1,159 @@ +extern crate bencher; + +use std::fmt::{Error, Formatter}; +use std::{ + pin::Pin, + task::{Context, Poll}, +}; + +use bencher::{benchmark_group, benchmark_main, Bencher}; +use bytes::{Buf, BufMut, Bytes, BytesMut}; +use http_body::Body; +use tokio_util::codec::Decoder; +use tonic::{Status, Streaming}; + +macro_rules! bench { + ($name:ident, $message_size:expr, $chunk_size:expr, $message_count:expr) => { + fn $name(b: &mut Bencher) { + let mut rt = tokio::runtime::Builder::new() + .basic_scheduler() + .build() + .expect("runtime"); + + let payload = make_payload($message_size, $message_count); + let body = MockBody::new(payload, $chunk_size); + b.bytes = body.len() as u64; + + b.iter(|| { + rt.block_on(async { + let decoder = MockDecoder::new($message_size); + let mut stream = Streaming::new_request(decoder, body.clone()); + + let mut count = 0; + while let Some(msg) = stream.message().await.unwrap() { + assert_eq!($message_size, msg.len()); + count += 1; + } + + assert_eq!(count, $message_count); + assert!(stream.trailers().await.unwrap().is_none()); + }) + }) + } + }; +} + +#[derive(Clone)] +struct MockBody { + data: Bytes, + chunk_size: usize, +} + +impl MockBody { + pub fn new(data: Bytes, chunk_size: usize) -> Self { + MockBody { data, chunk_size } + } + + pub fn len(&self) -> usize { + self.data.len() + } +} + +impl Body for MockBody { + type Data = Bytes; + type Error = Status; + + fn poll_data( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + ) -> Poll>> { + if self.data.has_remaining() { + let split = std::cmp::min(self.chunk_size, self.data.remaining()); + Poll::Ready(Some(Ok(self.data.split_to(split)))) + } else { + Poll::Ready(None) + } + } + + fn poll_trailers( + self: Pin<&mut Self>, + _: &mut Context<'_>, + ) -> Poll, Self::Error>> { + Poll::Ready(Ok(None)) + } +} + +impl std::fmt::Debug for MockBody { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + let sample = self.data.iter().take(10).collect::>(); + write!(f, "{:?}...({})", sample, self.data.len()) + } +} + +#[derive(Debug, Clone)] +struct MockDecoder { + message_size: usize, +} + +impl MockDecoder { + fn new(message_size: usize) -> Self { + MockDecoder { message_size } + } +} + +impl Decoder for MockDecoder { + type Item = Bytes; + type Error = Status; + + fn decode(&mut self, buf: &mut BytesMut) -> Result, Self::Error> { + let item = buf.split_to(self.message_size).freeze(); + Ok(Some(item)) + } +} + +fn make_payload(message_length: usize, message_count: usize) -> Bytes { + let mut buf = BytesMut::new(); + + for _ in 0..message_count { + let msg = vec![97u8; message_length]; + buf.reserve(msg.len() + 5); + buf.put_u8(0); + buf.put_u32(msg.len() as u32); + buf.put(&msg[..]); + } + + buf.freeze() +} + +// change body chunk size only +bench!(chunk_size_100, 1_000, 100, 1); +bench!(chunk_size_500, 1_000, 500, 1); +bench!(chunk_size_1005, 1_000, 1_005, 1); + +// change message size only +bench!(message_size_1k, 1_000, 1_005, 2); +bench!(message_size_5k, 5_000, 1_005, 2); +bench!(message_size_10k, 10_000, 1_005, 2); + +// change message count only +bench!(message_count_1, 500, 505, 1); +bench!(message_count_10, 500, 505, 10); +bench!(message_count_20, 500, 505, 20); + +benchmark_group!(chunk_size, chunk_size_100, chunk_size_500, chunk_size_1005); + +benchmark_group!( + message_size, + message_size_1k, + message_size_5k, + message_size_10k +); + +benchmark_group!( + message_count, + message_count_1, + message_count_10, + message_count_20 +); + +benchmark_main!(chunk_size, message_size, message_count); diff --git a/tonic/src/codec/decode.rs b/tonic/src/codec/decode.rs index 750a0dc52..421339dd7 100644 --- a/tonic/src/codec/decode.rs +++ b/tonic/src/codec/decode.rs @@ -61,7 +61,8 @@ impl Streaming { Self::new(decoder, body, Direction::EmptyResponse) } - pub(crate) fn new_request(decoder: D, body: B) -> Self + #[doc(hidden)] + pub fn new_request(decoder: D, body: B) -> Self where B: Body + Send + Sync + 'static, B::Error: Into,