-
Notifications
You must be signed in to change notification settings - Fork 656
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
ByteBuffer: take ownership / relinquish storage #1836
Conversation
Motivation: Maybe it'd be nice if we could take ownership & relinquish the storage pretty cheaply. Modification: Allow ByteBuffer to take ownership of foreign storage & relinquish its storage. Result: Better performance in certain special scenarios?
@Lukasa wdyt of this idea? |
I don't think this is really worth doing: to my eye it seems like it's solving a problem we don't really have. |
Probably agreed (which is also why we've never done it before). Only real thing I can think of is speeding up the send path for TS because we could |
We can do that anyway, I think, by just using the existing ByteBuffer constructors. |
No. What we can do is to give |
We can also do that any way using |
That's what we do today (if more than X bytes). But it will allocate because you need to give return self.withUnsafeReadableBytesWithStorageManagement { ptr, storageRef in
if doCopy {
return Data(bytes: UnsafeMutableRawPointer(mutating: ptr.baseAddress!.advanced(by: index)),
count: Int(length))
} else {
_ = storageRef.retain()
return Data(bytesNoCopy: UnsafeMutableRawPointer(mutating: ptr.baseAddress!.advanced(by: index)),
count: Int(length),
deallocator: .custom { _, _ in storageRef.release() })
/// ^^^^ alloc here for that closure because it captures the `storageRef`
}
} Swift could be smarter and use an |
What if instead of using the explicit
That might not capture, right? |
Yes. We could possibly even do it today, the |
We should confirm that this does in fact not allocate though. |
--- a/Sources/NIOFoundationCompat/ByteBuffer-foundation.swift
+++ b/Sources/NIOFoundationCompat/ByteBuffer-foundation.swift
@@ -126,10 +126,10 @@ extension ByteBuffer {
return Data(bytes: UnsafeMutableRawPointer(mutating: ptr.baseAddress!.advanced(by: index)),
count: Int(length))
} else {
- _ = storageRef.retain()
+ let storage = storageRef.takeUnretainedValue()
return Data(bytesNoCopy: UnsafeMutableRawPointer(mutating: ptr.baseAddress!.advanced(by: index)),
count: Int(length),
- deallocator: .custom { _, _ in storageRef.release() })
+ deallocator: .custom { _, _ in withExtendedLifetime(storage) {} })
}
}
} should be all we need. Yes, we do have an alloc test for that already I think. |
Motivation: When getting a `Data` from a `ByteBuffer` we currently allocate twice (`__DataStorage`) and the closure for `Data.Deallocator`. Modifications: We can optimise that by making the closure capture exactly one `AnyObject` which is already a reference counted object. The compiler realises that (on Linux) and saves us an alloc. Thanks @Lukasa for the suggestion here: apple#1836 (comment) Result: Fewer allocs.
Motivation: When getting a `Data` from a `ByteBuffer` we currently allocate twice (`__DataStorage`) and the closure for `Data.Deallocator`. Modifications: We can optimise that by making the closure capture exactly one `AnyObject` which is already a reference counted object. The compiler realises that (on Linux) and saves us an alloc. Thanks @Lukasa for the suggestion here: apple#1836 (comment) Result: Fewer allocs.
Motivation: When getting a `Data` from a `ByteBuffer` we currently allocate twice (`__DataStorage`) and the closure for `Data.Deallocator`. Modifications: We can optimise that by making the closure capture exactly one `AnyObject` which is already a reference counted object. The compiler realises that (on Linux) and saves us an alloc. Thanks @Lukasa for the suggestion here: apple#1836 (comment) Result: Fewer allocs.
Motivation: When getting a `Data` from a `ByteBuffer` we currently allocate twice (`__DataStorage`) and the closure for `Data.Deallocator`. Modifications: We can optimise that by making the closure capture exactly one `AnyObject` which is already a reference counted object. The compiler realises that (on Linux) and saves us an alloc. Thanks @Lukasa for the suggestion here: apple#1836 (comment) Result: Fewer allocs.
Motivation: When getting a `Data` from a `ByteBuffer` we currently allocate twice (`__DataStorage`) and the closure for `Data.Deallocator`. Modifications: We can optimise that by making the closure capture exactly one `AnyObject` which is already a reference counted object. The compiler realises that (on Linux) and saves us an alloc. Thanks @Lukasa for the suggestion here: #1836 (comment) Result: Fewer allocs.
WRONG, INCOMPLETE, AND UNTESTED
This is literally just the description of an idea really. If we want it, I can make it nice, if we don't that's cool too.
The reason to bring it up now is because this would stop working if we switched to tail-allocated storage.
Motivation:
Maybe it'd be nice if we could take ownership & relinquish the storage
pretty cheaply.
Modification:
Allow ByteBuffer to take ownership of foreign storage & relinquish its
storage.
Result:
Better performance in certain special scenarios?