Repository navigation
perf(header): allocate once in HeaderValue::from for integers - #885
Open
patrick-immometrica wants to merge 1 commit into
Open
patrick-immometrica wants to merge 1 commit into
patrick-immometrica wants to merge 1 commit into
Conversation
The integer conversions wrote into a BytesMut sized for the longest possible number and then froze it. Since the buffer is almost never full, freeze() can't hand the Vec over directly and allocates a shared header on top, so every conversion did two allocations. Copying the itoa output straight into Bytes does one. hyper uses this for content-length on every response with a known body size. Add a bench for HeaderValue::from(u64).
patrick-immometrica
marked this pull request as ready for review
November 5, 2026 11:09
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
HeaderValue::from(u64)(and the other integer impls) format into aBytesMut::with_capacity(20)and thenfreeze()it. Since the buffer's len is smaller than its capacity,freeze()goes throughFrom<Vec<u8>> for Bytes, which allocates a secondBox<Shared>. So every call costs two allocations for a few bytes.hyper calls this for
content-lengthon every response, so it is a fairly hot path.This formats with
itoa::Bufferon the stack (itoa is already a dependency) and doesBytes::copy_from_slice, which is one exact-size allocation. Because the per-type max length isn't needed anymore, the threetarget_pointer_widthblocks collapse into one list.Numbers on an M-series Mac,
HeaderValue::from(1024u64):from_u64bench (nightly, 3 runs)I added the
from_u64bench tobenches/src/header_value.rs. Existing tests cover the formatting for each type, and the behavior is unchanged.One trade-off: the old value already had its
Sharedallocated, so cloning it was only a refcount bump. The new one is a plain promotableBytes, so the first clone allocates theSharedinstead. A value that gets cloned therefore ends up at the same two allocations as before, and one that isn't saves one.