Skip to content

perf(h1): pre-allocate read buffer instead of starting at zero#4030

Open
DioCrafts wants to merge 1 commit intohyperium:masterfrom
DioCrafts:perf/read-buf-prealloc
Open

perf(h1): pre-allocate read buffer instead of starting at zero#4030
DioCrafts wants to merge 1 commit intohyperium:masterfrom
DioCrafts:perf/read-buf-prealloc

Conversation

@DioCrafts
Copy link

@DioCrafts DioCrafts commented Mar 3, 2026

Description:

The H1 read buffer was created with BytesMut::with_capacity(0). On the first read, the adaptive strategy immediately grows it to INIT_BUFFER_SIZE (8192 bytes). The H1 read buffer was created with BytesMut::with_capacity(0). On the first read, the adaptive strategy grows it to INIT_BUFFER_SIZE (8 KiB). By pre-allocating at that size directly, we skip the deferred-growth code path.

This PR initializes the buffer directly at INIT_BUFFER_SIZE. One allocation instead of two. The adaptive read strategy still controls growth from there.

What changed

  • io.rs line 71: BytesMut::with_capacity(0)BytesMut::with_capacity(INIT_BUFFER_SIZE)

One line, no API changes.

Change read_buf initialization from BytesMut::with_capacity(0) to
BytesMut::with_capacity(INIT_BUFFER_SIZE) (8192 bytes).

The read buffer almost always grows to INIT_BUFFER_SIZE on the first read.
Pre-allocating avoids the initial zero-capacity allocation and the
subsequent reallocation + copy when the first data arrives.
@paolobarbolini
Copy link
Contributor

That means every new connection paid for a zero-size allocation followed by a reallocation and copy.

One allocation instead of two

Capacity of zero is not an allocation.

@DioCrafts
Copy link
Author

@paolobarbolini You're absolutely right man, with_capacity(0) doesn't allocate, my description was misleading there, sorry about that! The intent is to avoid the deferred allocation path on the first read, since the adaptive strategy will grow it to INIT_BUFFER_SIZE immediately anyway. I'll update the PR description. Thanks for the correction! 🙏

@seanmonstar
Copy link
Member

Does this result in improved performance in some case? Can we measure?

@DioCrafts
Copy link
Author

Does this result in improved performance in some case? Can we measure?

Hi @seanmonstar.

Honestly, for the common case the difference is negligible: with_capacity(0) doesn't allocate, and the first read triggers the 8 KiB allocation anyway. The only gain is skipping the growth check on that first read. I don't have benchmarks showing a measurable improvement.

If you'd prefer, I can run h1_parallel or a similar bench to get numbers before moving forward.

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.

3 participants