Skip to content

perf: allow multiple DATA frames per write#903

Open
NedAnd1 wants to merge 2 commits intohyperium:masterfrom
NedAnd1:batch-data-frame-writes
Open

perf: allow multiple DATA frames per write#903
NedAnd1 wants to merge 2 commits intohyperium:masterfrom
NedAnd1:batch-data-frame-writes

Conversation

@NedAnd1
Copy link
Copy Markdown

@NedAnd1 NedAnd1 commented May 5, 2026

Summary

This PR addresses the sender-side perf issue described in #902
by allowing multiple DATA frames to be coalesced into a single write_vectored() syscall,
rather than the current behavior of flushing each DATA frame individually.

Problem

The existing encoder holds at most one pending DATA frame at a time and blocks accepting new frames until it is fully flushed. With TCP_NODELAY enabled, each frame triggers its own write() syscall and TCP segment — inflating the syscall-to-payload ratio and cutting throughput roughly in half compared to raw TCP.

Solution

  • Replace the single next Option<Next<B>> slot with a VecDeque<BufElement<B>> that can queue multiple DATA frames (up to 512 on vectored-IO transports).
  • Implement Buf directly on the Encoder, providing chunks_vectored() so that all queued frame headers + payloads are written in one poll_write_vectored() call.
  • Replace the per-connection in_flight_data_frame field with a per-stream in_flight_partial_send field of type Option<ControlFlow<()>>, allowing each stream to send up to one partial data frame at a time without blocking other streams.
  • Introduce take_used_data_frames() iterator to reclaim all fully-written frames in a single pass, replacing the old take_last_data_frame() which could only return one.
  • Add a custom poll_write_buf that returns ControlFlow to cleanly distinguish "nothing left to write" from "wrote some bytes, keep going".

Validation

  • h2 cargo tests
  • external scenario tests
  • external perf tests

Perf results for a branch including the commit in this PR:

   OS: linux (kernel 6.6), Arch: arm64, TCP_NODELAY: True

   Data Frame Size │ master (throughput) │ batch-data-frames (throughput) │ Improvement
      4 kiB        │ 0.97 GB/s           │ 1.14 GB/s                      │ 1.18x
     16 kiB        │ 2.68 GB/s           │ 3.47 GB/s                      │ 1.29x
     64 kiB        │ 3.54 GB/s           │ 6.19 GB/s                      │ 1.75x
    256 kiB        │ 4.64 GB/s           │ 7.50 GB/s                      │ 1.62x
   1024 kiB        │ 4.28 GB/s           │ 7.81 GB/s                      │ 1.82x

Receiver-side PR: #904

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