perf: optimize

This commit is contained in:
wisdgod
2025-08-13 05:17:47 +08:00
parent 4e809ccfb3
commit 8f48693d22
29 changed files with 3337 additions and 6304 deletions

View File

@@ -1,3 +1,7 @@
# 0.4.12 (August 5, 2025)
* Fix default limits on max stored reset streams and duration to more reasonable values.
# 0.4.11 (June 30, 2025)
* Fix client to not return an error when a clean shutdown otherwise doesn't get a TLS close_notify, which some servers don't bother sending.

View File

@@ -3,7 +3,7 @@ name = "h2"
# When releasing to crates.io:
# - Update CHANGELOG.md.
# - Create git tag
version = "0.4.11"
version = "0.4.12"
license = "MIT"
authors = [
"Carl Lerche <me@carllerche.com>",

View File

@@ -53,19 +53,6 @@ fn main() {
## FAQ
**How does h2 compare to [solicit] or [rust-http2]?**
The h2 library has implemented more of the details of the HTTP/2 specification
than any other Rust library. It also passes the [h2spec] set of tests. The h2
library is rapidly approaching "production ready" quality.
Besides the above, Solicit is built on blocking I/O and does not appear to be
actively maintained.
**Is this an embedded Java SQL database engine?**
[No](https://www.h2database.com).
[solicit]: https://github.com/mlalic/solicit
[rust-http2]: https://github.com/stepancheg/rust-http2
[h2spec]: https://github.com/summerwind/h2spec

View File

@@ -923,7 +923,7 @@ impl Builder {
/// received for that stream will result in a connection level protocol
/// error, forcing the connection to terminate.
///
/// The default value is 10.
/// The default value is currently 50.
///
/// # Examples
///
@@ -968,7 +968,7 @@ impl Builder {
/// received for that stream will result in a connection level protocol
/// error, forcing the connection to terminate.
///
/// The default value is 30 seconds.
/// The default value is currently 1 second.
///
/// # Examples
///

View File

@@ -33,6 +33,10 @@ pub type WindowSize = u32;
pub const MAX_WINDOW_SIZE: WindowSize = (1 << 31) - 1; // i32::MAX as u32
pub const DEFAULT_REMOTE_RESET_STREAM_MAX: usize = 20;
pub const DEFAULT_LOCAL_RESET_COUNT_MAX: usize = 1024;
pub const DEFAULT_RESET_STREAM_MAX: usize = 10;
pub const DEFAULT_RESET_STREAM_SECS: u64 = 30;
// RFC 9113 suggests allowing at minimum 100 streams, it seems reasonable to
// by default allow a portion of that to be remembered as reset for some time.
pub const DEFAULT_RESET_STREAM_MAX: usize = 50;
// RFC 9113#5.4.2 suggests ~1 RTT. We don't track that closely, but use a
// reasonable guess of the average here.
pub const DEFAULT_RESET_STREAM_SECS: u64 = 1;
pub const DEFAULT_MAX_SEND_BUFFER_SIZE: usize = 1024 * 400;

View File

@@ -73,3 +73,56 @@ pub struct Config {
/// When this gets exceeded, we issue GOAWAYs.
pub local_max_error_reset_streams: Option<usize>,
}
trait DebugStructExt<'a, 'b> {
// h2_ prefixes to protect against possible future name collisions
fn h2_field_if(&mut self, name: &str, val: &bool) -> &mut std::fmt::DebugStruct<'a, 'b>;
fn h2_field_if_then<T: std::fmt::Debug>(
&mut self,
name: &str,
cond: bool,
val: &T,
) -> &mut std::fmt::DebugStruct<'a, 'b>;
fn h2_field_some<T: std::fmt::Debug>(
&mut self,
name: &str,
val: &Option<T>,
) -> &mut std::fmt::DebugStruct<'a, 'b>;
}
impl<'a, 'b> DebugStructExt<'a, 'b> for std::fmt::DebugStruct<'a, 'b> {
fn h2_field_if(&mut self, name: &str, val: &bool) -> &mut std::fmt::DebugStruct<'a, 'b> {
if *val {
self.field(name, val)
} else {
self
}
}
fn h2_field_if_then<T: std::fmt::Debug>(
&mut self,
name: &str,
cond: bool,
val: &T,
) -> &mut std::fmt::DebugStruct<'a, 'b> {
if cond {
self.field(name, val)
} else {
self
}
}
fn h2_field_some<T: std::fmt::Debug>(
&mut self,
name: &str,
val: &Option<T>,
) -> &mut std::fmt::DebugStruct<'a, 'b> {
if val.is_some() {
self.field(name, val)
} else {
self
}
}
}

View File

@@ -912,11 +912,15 @@ impl Recv {
return;
}
tracing::trace!("enqueue_reset_expiration; {:?}", stream.id);
if counts.can_inc_num_reset_streams() {
counts.inc_num_reset_streams();
tracing::trace!("enqueue_reset_expiration; added {:?}", stream.id);
self.pending_reset_expired.push(stream);
} else {
tracing::trace!(
"enqueue_reset_expiration; dropped {:?}, over max_concurrent_reset_streams",
stream.id
);
}
}

View File

@@ -1,3 +1,4 @@
use std::fmt;
use std::io;
use crate::codec::UserError;
@@ -47,7 +48,7 @@ use self::Peer::*;
/// ES: END_STREAM flag
/// R: RST_STREAM frame
/// ```
#[derive(Debug, Clone)]
#[derive(Clone)]
pub struct State {
inner: Inner,
}
@@ -465,3 +466,10 @@ impl Default for State {
State { inner: Inner::Idle }
}
}
// remove some noise for debug output
impl fmt::Debug for State {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}

View File

@@ -34,7 +34,6 @@ pub(crate) struct Key {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct SlabIndex(u32);
#[derive(Debug)]
pub(super) struct Queue<N> {
indices: Option<store::Indices>,
_p: PhantomData<N>,
@@ -378,6 +377,15 @@ where
}
}
impl<N> fmt::Debug for Queue<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Queue")
.field("indices", &self.indices)
// skip phantom data
.finish()
}
}
// ===== impl Ptr =====
impl<'a> Ptr<'a> {

View File

@@ -398,35 +398,47 @@ impl fmt::Debug for Stream {
.field("state", &self.state)
.field("is_counted", &self.is_counted)
.field("ref_count", &self.ref_count)
.field("next_pending_send", &self.next_pending_send)
.field("is_pending_send", &self.is_pending_send)
.h2_field_some("next_pending_send", &self.next_pending_send)
.h2_field_if("is_pending_send", &self.is_pending_send)
.field("send_flow", &self.send_flow)
.field("requested_send_capacity", &self.requested_send_capacity)
.field("buffered_send_data", &self.buffered_send_data)
.field("send_task", &self.send_task.as_ref().map(|_| ()))
.field("pending_send", &self.pending_send)
.field(
.h2_field_some("send_task", &self.send_task.as_ref().map(|_| ()))
.h2_field_if_then(
"pending_send",
!self.pending_send.is_empty(),
&self.pending_send,
)
.h2_field_some(
"next_pending_send_capacity",
&self.next_pending_send_capacity,
)
.field("is_pending_send_capacity", &self.is_pending_send_capacity)
.field("send_capacity_inc", &self.send_capacity_inc)
.field("next_open", &self.next_open)
.field("is_pending_open", &self.is_pending_open)
.field("is_pending_push", &self.is_pending_push)
.field("next_pending_accept", &self.next_pending_accept)
.field("is_pending_accept", &self.is_pending_accept)
.h2_field_if("is_pending_send_capacity", &self.is_pending_send_capacity)
.h2_field_if("send_capacity_inc", &self.send_capacity_inc)
.h2_field_some("next_open", &self.next_open)
.h2_field_if("is_pending_open", &self.is_pending_open)
.h2_field_if("is_pending_push", &self.is_pending_push)
.h2_field_some("next_pending_accept", &self.next_pending_accept)
.h2_field_if("is_pending_accept", &self.is_pending_accept)
.field("recv_flow", &self.recv_flow)
.field("in_flight_recv_data", &self.in_flight_recv_data)
.field("next_window_update", &self.next_window_update)
.field("is_pending_window_update", &self.is_pending_window_update)
.field("reset_at", &self.reset_at)
.field("next_reset_expire", &self.next_reset_expire)
.field("pending_recv", &self.pending_recv)
.field("is_recv", &self.is_recv)
.field("recv_task", &self.recv_task.as_ref().map(|_| ()))
.field("push_task", &self.push_task.as_ref().map(|_| ()))
.field("pending_push_promises", &self.pending_push_promises)
.h2_field_some("next_window_update", &self.next_window_update)
.h2_field_if("is_pending_window_update", &self.is_pending_window_update)
.h2_field_some("reset_at", &self.reset_at)
.h2_field_some("next_reset_expire", &self.next_reset_expire)
.h2_field_if_then(
"pending_recv",
!self.pending_recv.is_empty(),
&self.pending_recv,
)
.h2_field_if("is_recv", &self.is_recv)
.h2_field_some("recv_task", &self.recv_task.as_ref().map(|_| ()))
.h2_field_some("push_task", &self.push_task.as_ref().map(|_| ()))
.h2_field_if_then(
"pending_push_promises",
!self.pending_push_promises.is_empty(),
&self.pending_push_promises,
)
.field("content_length", &self.content_length)
.finish()
}

View File

@@ -868,7 +868,7 @@ impl Builder {
/// received for that stream will result in a connection level protocol
/// error, forcing the connection to terminate.
///
/// The default value is 10.
/// The default value is currently 50.
///
/// # Examples
///
@@ -993,7 +993,7 @@ impl Builder {
/// received for that stream will result in a connection level protocol
/// error, forcing the connection to terminate.
///
/// The default value is 30 seconds.
/// The default value is currently 1 second.
///
/// # Examples
///