From 22695968d23c5576acbe73e321038c3feb244641 Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Tue, 8 Oct 2019 15:37:33 -0700 Subject: [PATCH] perf(http2): improve default HTTP2 flow control settings Set default HTTP2 window sizes much larger values than the spec default. ref #1960 --- benches/end_to_end.rs | 52 +++++++++++++++++++++++++++++++++++++++++-- src/client/conn.rs | 21 ++++++++++++++--- src/client/mod.rs | 8 +++++-- src/server/conn.rs | 28 ++++++++++++++++++----- src/server/mod.rs | 10 ++++++--- 5 files changed, 104 insertions(+), 15 deletions(-) diff --git a/benches/end_to_end.rs b/benches/end_to_end.rs index 8fa496d1aa..8d83ef3be1 100644 --- a/benches/end_to_end.rs +++ b/benches/end_to_end.rs @@ -11,6 +11,8 @@ use tokio::runtime::current_thread::Runtime; use hyper::{Body, Method, Request, Response, Server}; use hyper::client::HttpConnector; +// HTTP1 + #[bench] fn http1_get(b: &mut test::Bencher) { opts() @@ -62,6 +64,28 @@ fn http1_parallel_x10_req_10mb(b: &mut test::Bencher) { .bench(b) } +#[bench] +fn http1_parallel_x10_res_1mb(b: &mut test::Bencher) { + let body = &[b'x'; 1024 * 1024 * 1]; + opts() + .parallel(10) + .response_body(body) + .bench(b) +} + +#[bench] +fn http1_parallel_x10_res_10mb(b: &mut test::Bencher) { + let body = &[b'x'; 1024 * 1024 * 10]; + opts() + .parallel(10) + .response_body(body) + .bench(b) +} + +// HTTP2 + +const HTTP2_MAX_WINDOW: u32 = std::u32::MAX >> 1; + #[bench] fn http2_get(b: &mut test::Bencher) { opts() @@ -104,8 +128,32 @@ fn http2_parallel_x10_req_10mb(b: &mut test::Bencher) { .parallel(10) .method(Method::POST) .request_body(body) - .http2_stream_window(std::u32::MAX >> 1) - .http2_conn_window(std::u32::MAX >> 1) + //.http2_stream_window(HTTP2_MAX_WINDOW) + //.http2_conn_window(HTTP2_MAX_WINDOW) + .bench(b) +} + +#[bench] +fn http2_parallel_x10_res_1mb(b: &mut test::Bencher) { + let body = &[b'x'; 1024 * 1024 * 1]; + opts() + .http2() + .parallel(10) + .response_body(body) + .http2_stream_window(HTTP2_MAX_WINDOW) + .http2_conn_window(HTTP2_MAX_WINDOW) + .bench(b) +} + +#[bench] +fn http2_parallel_x10_res_10mb(b: &mut test::Bencher) { + let body = &[b'x'; 1024 * 1024 * 10]; + opts() + .http2() + .parallel(10) + .response_body(body) + .http2_stream_window(HTTP2_MAX_WINDOW) + .http2_conn_window(HTTP2_MAX_WINDOW) .bench(b) } diff --git a/src/client/conn.rs b/src/client/conn.rs index ef5db41f9e..f26791b396 100644 --- a/src/client/conn.rs +++ b/src/client/conn.rs @@ -34,6 +34,14 @@ type ConnEither = Either< proto::h2::ClientTask, >; +// Our defaults are chosen for the "majority" case, which usually are not +// resource contrained, and so the spec default of 64kb can be too limiting +// for performance. +const DEFAULT_HTTP2_CONN_WINDOW: u32 = 1024 * 1024 * 5; // 5mb +const DEFAULT_HTTP2_STREAM_WINDOW: u32 = 1024 * 1024 * 2; // 2mb + + + /// Returns a handshake future over some IO. /// /// This is a shortcut for `Builder::new().handshake(io)`. @@ -425,7 +433,10 @@ impl Builder { #[inline] pub fn new() -> Builder { let mut h2_builder = h2::client::Builder::default(); - h2_builder.enable_push(false); + h2_builder + .initial_window_size(DEFAULT_HTTP2_STREAM_WINDOW) + .initial_connection_window_size(DEFAULT_HTTP2_CONN_WINDOW) + .enable_push(false); Builder { exec: Exec::Default, @@ -486,7 +497,9 @@ impl Builder { /// Sets the [`SETTINGS_INITIAL_WINDOW_SIZE`][spec] option for HTTP2 /// stream-level flow control. /// - /// Default is 65,535 + /// Passing `None` will do nothing. + /// + /// If not set, hyper will use a default. /// /// [spec]: https://http2.github.io/http2-spec/#SETTINGS_INITIAL_WINDOW_SIZE pub fn http2_initial_stream_window_size(&mut self, sz: impl Into>) -> &mut Self { @@ -498,7 +511,9 @@ impl Builder { /// Sets the max connection-level flow control for HTTP2 /// - /// Default is 65,535 + /// Passing `None` will do nothing. + /// + /// If not set, hyper will use a default. pub fn http2_initial_connection_window_size(&mut self, sz: impl Into>) -> &mut Self { if let Some(sz) = sz.into() { self.h2_builder.initial_connection_window_size(sz); diff --git a/src/client/mod.rs b/src/client/mod.rs index f0e6f00201..318d804572 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -954,7 +954,9 @@ impl Builder { /// Sets the [`SETTINGS_INITIAL_WINDOW_SIZE`][spec] option for HTTP2 /// stream-level flow control. /// - /// Default is 65,535 + /// Passing `None` will do nothing. + /// + /// If not set, hyper will use a default. /// /// [spec]: https://http2.github.io/http2-spec/#SETTINGS_INITIAL_WINDOW_SIZE pub fn http2_initial_stream_window_size(&mut self, sz: impl Into>) -> &mut Self { @@ -964,7 +966,9 @@ impl Builder { /// Sets the max connection-level flow control for HTTP2 /// - /// Default is 65,535 + /// Passing `None` will do nothing. + /// + /// If not set, hyper will use a default. pub fn http2_initial_connection_window_size(&mut self, sz: impl Into>) -> &mut Self { self.conn_builder.http2_initial_connection_window_size(sz.into()); self diff --git a/src/server/conn.rs b/src/server/conn.rs index 352170f0bf..726a95ac14 100644 --- a/src/server/conn.rs +++ b/src/server/conn.rs @@ -37,6 +37,15 @@ pub(super) use self::upgrades::UpgradeableConnection; #[cfg(feature = "tcp")] pub use super::tcp::{AddrIncoming, AddrStream}; +// Our defaults are chosen for the "majority" case, which usually are not +// resource contrained, and so the spec default of 64kb can be too limiting +// for performance. +// +// At the same time, a server more often has multiple clients connected, and +// so is more likely to use more resources than a client would. +const DEFAULT_HTTP2_CONN_WINDOW: u32 = 1024 * 1024; // 1mb +const DEFAULT_HTTP2_STREAM_WINDOW: u32 = 1024 * 1024; // 1mb + /// A lower-level configuration of the HTTP protocol. /// /// This structure is used to configure options for an HTTP server connection. @@ -178,11 +187,16 @@ impl Http { /// Creates a new instance of the HTTP protocol, ready to spawn a server or /// start accepting connections. pub fn new() -> Http { + let mut h2_builder = h2::server::Builder::default(); + h2_builder + .initial_window_size(DEFAULT_HTTP2_STREAM_WINDOW) + .initial_connection_window_size(DEFAULT_HTTP2_CONN_WINDOW); + Http { exec: Exec::Default, h1_half_close: true, h1_writev: true, - h2_builder: h2::server::Builder::default(), + h2_builder, mode: ConnectionMode::Fallback, keep_alive: true, max_buf_size: None, @@ -247,7 +261,9 @@ impl Http { /// Sets the [`SETTINGS_INITIAL_WINDOW_SIZE`][spec] option for HTTP2 /// stream-level flow control. /// - /// Default is 65,535 + /// Passing `None` will do nothing. + /// + /// If not set, hyper will use a default. /// /// [spec]: https://http2.github.io/http2-spec/#SETTINGS_INITIAL_WINDOW_SIZE pub fn http2_initial_stream_window_size(&mut self, sz: impl Into>) -> &mut Self { @@ -257,9 +273,11 @@ impl Http { self } - /// Sets the max connection-level flow control for HTTP2 + /// Sets the max connection-level flow control for HTTP2. + /// + /// Passing `None` will do nothing. /// - /// Default is 65,535 + /// If not set, hyper will use a default. pub fn http2_initial_connection_window_size(&mut self, sz: impl Into>) -> &mut Self { if let Some(sz) = sz.into() { self.h2_builder.initial_connection_window_size(sz); @@ -270,7 +288,7 @@ impl Http { /// Sets the [`SETTINGS_MAX_CONCURRENT_STREAMS`][spec] option for HTTP2 /// connections. /// - /// Default is no limit (`None`). + /// Default is no limit (`std::u32::MAX`). Passing `None` will do nothing. /// /// [spec]: https://http2.github.io/http2-spec/#SETTINGS_MAX_CONCURRENT_STREAMS pub fn http2_max_concurrent_streams(&mut self, max: impl Into>) -> &mut Self { diff --git a/src/server/mod.rs b/src/server/mod.rs index b9a550e8f4..31f508ca82 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -306,7 +306,9 @@ impl Builder { /// Sets the [`SETTINGS_INITIAL_WINDOW_SIZE`][spec] option for HTTP2 /// stream-level flow control. /// - /// Default is 65,535 + /// Passing `None` will do nothing. + /// + /// If not set, hyper will use a default. /// /// [spec]: https://http2.github.io/http2-spec/#SETTINGS_INITIAL_WINDOW_SIZE pub fn http2_initial_stream_window_size(mut self, sz: impl Into>) -> Self { @@ -316,7 +318,9 @@ impl Builder { /// Sets the max connection-level flow control for HTTP2 /// - /// Default is 65,535 + /// Passing `None` will do nothing. + /// + /// If not set, hyper will use a default. pub fn http2_initial_connection_window_size(mut self, sz: impl Into>) -> Self { self.protocol.http2_initial_connection_window_size(sz.into()); self @@ -325,7 +329,7 @@ impl Builder { /// Sets the [`SETTINGS_MAX_CONCURRENT_STREAMS`][spec] option for HTTP2 /// connections. /// - /// Default is no limit (`None`). + /// Default is no limit (`std::u32::MAX`). Passing `None` will do nothing. /// /// [spec]: https://http2.github.io/http2-spec/#SETTINGS_MAX_CONCURRENT_STREAMS pub fn http2_max_concurrent_streams(mut self, max: impl Into>) -> Self {