-
Notifications
You must be signed in to change notification settings - Fork 697
Add a pooled recv buffer allocator #2362
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Side note: I don't think this is entirely true. It's only true if a |
@swift-server-bot test this please |
3 similar comments
@swift-server-bot test this please |
@swift-server-bot test this please |
@swift-server-bot test this please |
@swift-server-bot test this please |
abeb277
to
ab1cece
Compare
API breakage is the usual false positive of adding to a protocol with a default implementation:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, this is getting a lot closer! A few more notes.
var index = startIndex | ||
|
||
// Sweep from after the lasted used index through to the end. | ||
while index != self.buffers.endIndex { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is definitely better, but these methods are still large and repetitive. This pair of while
loops, for example, can certainly be refactored into an extension method on array which will allow us to reduce the number of lines of code in this method.
if let result = try self.buffer?.modifyIfUniquelyOwned(minimumCapacity: nextBufferSize, body) { | ||
// `result` can only be non-nil if `buffer` is non-nil. | ||
return (self.buffer!, result) | ||
} else if !self.buffers.isEmpty { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need this conditional: the code written below has to work correctly regardless so I think we can just remove it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice one, thanks @glbrntt!
Motivation: Channels can read `ChannelOptions.maxMessagesPerRead` times from a socket in each read cycle. They typically re-use the same buffer for each read and rely on it CoWing if necessary. If we read more than once in a cycle then we may CoW the buffer. Instead of reusing one buffer we can reuse a pool of buffers limited by `maxMessagesPerRead` and cycle through each, reducing the chance of CoWing the buffers. Modifications: - Extend `RecvByteBufferAllocator` to provide the size of the next buffer with a default implementation returning `nil`. - Add an recv buffer pool which lazily grows up to a fixed size and attempts to reuse buffers where possible if doing so avoids CoWing. Results: Fewer allocations
15b5e80
to
3fd3245
Compare
Motivation:
Channels can read
ChannelOptions.maxMessagesPerRead
times from a socket in each read cycle. They typically re-use the same buffer for each read and rely on it CoWing if necessary. If we read more than once in a cycle then we will always CoW the buffer. Instead of reusing one buffer we can reuse a pool of buffers limited bymaxMessagesPerRead
and cycle through each, reducing the chance of CoWing the buffers.Modifications:
NIOPoolableRecvByteBufferAllocator
which extendsRecvByteBufferAllocator
to provide the size of the next buffer.NIOPoolableRecvByteBufferAllocator
Results:
Fewer allocations