Skip to content

Recycles slices allocated by OwnedImpl back to the thread-local freelist#46136

Draft
birenroy wants to merge 4 commits into
envoyproxy:mainfrom
birenroy:buffer-recycle
Draft

Recycles slices allocated by OwnedImpl back to the thread-local freelist#46136
birenroy wants to merge 4 commits into
envoyproxy:mainfrom
birenroy:buffer-recycle

Conversation

@birenroy

@birenroy birenroy commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

This change allows mutable slices of default size (16KB) to be recycled
back to the thread-local freelist when they are drained and destroyed,
rather than being deleted. This reduces memory allocation frequency
for steady-state read operations.

We introduce a static recycle method to OwnedImplReservationSlicesOwnerMultiple
and call it from Slice destructor. To resolve circular dependency, the
Slice destructor is declared in the class and defined inline at the
end of the header. We also make Slice a friend of OwnedImpl to allow
access to the private nested class.

Added unit tests in buffer_test.cc to verify:

  • Successful recycling and reuse of default-sized slices.
  • Non-default-sized slices are not recycled.

This change was entirely generated by the Gemini AI model. I have read the code and
understand how it operates.

Benchmarks before:

--------------------------------------------------------------------------------
Benchmark                                      Time             CPU   Iterations
--------------------------------------------------------------------------------
bufferReserveCommitPartial/1                73.7 ns         73.6 ns      9787234
bufferReserveCommitPartial/4096             76.0 ns         76.0 ns      9377358
bufferReserveCommitPartial/16384            85.7 ns         85.7 ns      8166592
bufferReserveCommitPartial/65536             118 ns          118 ns      6029029
bufferReserveCommitPartial/131072            176 ns          176 ns      3468302
bufferReserveCommitMultiBuffer/16384         775 ns          775 ns       925720
bufferReserveCommitMultiBuffer/65536        2182 ns         2181 ns       327569
bufferReserveCommitMultiBuffer/131072       4016 ns         4014 ns       181183

Benchmarks after:

--------------------------------------------------------------------------------                  
Benchmark                                      Time             CPU   Iterations                  
--------------------------------------------------------------------------------                  
bufferReserveCommitPartial/1                74.0 ns         74.0 ns      9710692
bufferReserveCommitPartial/4096             74.0 ns         73.9 ns      9479766                  
bufferReserveCommitPartial/16384            88.5 ns         88.5 ns      7909427                  
bufferReserveCommitPartial/65536             115 ns          115 ns      6097813                  
bufferReserveCommitPartial/131072            168 ns          168 ns      4068071                  
bufferReserveCommitMultiBuffer/16384         687 ns          687 ns       927754                                                                                                                     
bufferReserveCommitMultiBuffer/65536        1839 ns         1839 ns       381225                                                                                                                     
bufferReserveCommitMultiBuffer/131072       3220 ns         3219 ns       222127

Commit Message:
Additional Description:
Risk Level:
Testing:
Docs Changes:
Release Notes:
Platform Specific Features:

birenroy added 2 commits July 14, 2026 22:28
This change allows mutable slices of default size (16KB) to be recycled
back to the thread-local freelist when they are drained and destroyed,
rather than being deleted. This reduces memory allocation frequency
for steady-state read operations.

We introduce a static `recycle` method to `OwnedImplReservationSlicesOwnerMultiple`
and call it from `Slice` destructor. To resolve circular dependency, the
`Slice` destructor is declared in the class and defined inline at the
end of the header. We also make `Slice` a friend of `OwnedImpl` to allow
access to the private nested class.

Added unit tests in `buffer_test.cc` to verify:
- Successful recycling and reuse of default-sized slices.
- Non-default-sized slices are not recycled.

TAG=agy
CONV=95b3d3e4-0d80-4daa-b3ba-7097db5a23ce

Signed-off-by: Biren Roy <birenroy@google.com>
Signed-off-by: Biren Roy <birenroy@google.com>
@repokitteh-read-only

Copy link
Copy Markdown

As a reminder, PRs marked as draft will not be automatically assigned reviewers,
or be handled by maintainer-oncall triage.

Please mark your PR as ready when you want it to be reviewed!

🐱

Caused by: #46136 was opened by birenroy.

see: more, trace.

birenroy added 2 commits July 15, 2026 00:52
Ensure that the old storage of a Slice is recycled when the Slice is overwritten via move assignment. This occurs during pop_front and pop_back operations in SliceDeque, which are used when draining the buffer.

Also update recycling tests to use spoiler allocations, preventing false-positive passes caused by the allocator reusing the same addresses for freed blocks.

TAG=agy

CONV=95b3d3e4-0d80-4daa-b3ba-7097db5a23ce

Signed-off-by: Biren Roy <birenroy@google.com>
Increase the thread-local freelist max size to 32 (4 * MAX_SLICES_) to better support concurrent connections on the same thread without freelist thrashing.

Add a multi-buffer benchmark bufferReserveCommitMultiBuffer to simulate concurrent connections and demonstrate the benefit of the larger freelist.

TAG=agy

CONV=95b3d3e4-0d80-4daa-b3ba-7097db5a23ce

Signed-off-by: Biren Roy <birenroy@google.com>
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