Skip to content

perf(codec): don't flush the transport in FramedWrite::poll_ready - #998

Open
Iaotle wants to merge 1 commit into
hyperium:masterfrom
Iaotle:poll-ready-no-flush
Open

Iaotle wants to merge 1 commit into
hyperium:masterfrom
Iaotle:poll-ready-no-flush

Conversation

@Iaotle

@Iaotle Iaotle commented Nov 3, 2026

Copy link
Copy Markdown

FramedWrite::poll_ready calls flush, which writes the buffered frame and then calls poll_flush on the transport. While a DATA frame is queued as next, the codec has no capacity, so poll_ready runs before every DATA frame. A large response therefore flushes the transport once per frame.

poll_ready only needs the buffer written out to free capacity. This change splits the write loop out of flush into write_out, and poll_ready calls only that. flush still writes out and then flushes. The transport is still flushed once every pending frame is buffered (Streams::poll_complete calls flush), so nothing is held back.

A transport that does work per flush pays it per frame. With kernel TLS (kTLS), each write ends a TLS record. A 16,393-byte frame (9-byte header plus 16 KiB of data) then goes out as one full record and one 9-byte record, which doubles the record count and the AEAD operations. With this change, a transport that gathers writes until poll_flush can send whole records. On a 2 GB HTTP/2 download over loopback (i7-9700K), the server's CPU per GB fell from 0.850 s to 0.715 s on kTLS, and from 0.690 s to 0.655 s on rustls. Transports that write on every poll_write behave as before.

poll_ready only needs the buffer written out to free capacity, so it now calls a new
write_out (the write loop split out of flush) instead of flush. flush still writes out
and then flushes the transport, and Streams::poll_complete still flushes once every
pending frame is buffered.

While a DATA frame is queued the codec has no capacity, so poll_ready ran before every
DATA frame and flushed the transport once per frame.

Assisted-by: Claude:Opus-5.5
AzulEterno added a commit to AzulEterno/hyper-h2-edge that referenced this pull request Nov 6, 2026
Carry the optional validated WriteBatchSize API and lazy full-frame batching.
Integrate hyperium#980 (c8bfb22)
while preserving stable flush boundaries. Include protocol regressions and
upstream attribution; omit hyperium#998 after matched local benchmarks.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant