-
Notifications
You must be signed in to change notification settings - Fork 156
Generialise FixedBufRegistry and FixedBufPool #213
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
Extends both registries to accept any `Iter` of `IoBufMut`, instead of Vec<u8>. This is motivated by my usecase, which cannot safely provide its memory as a Vec, and needs custom cleanup. This implementation, which explcitly keeps the backing buffers present, rather then mem::forget and recreate means the Drop handling is preserved. As a trivial example, this would now admit Box<[u8]> as a buffer.
|
@mzabaluev and @FrankReh I'd appreciate your feedback. |
|
This looks good to me. The way drops are handled now, would it let me carve out the buffers from a single mmap'ed block if I wrote the iterator to unsafely step through the allocated memory? That's been my last sticking point as I think some kernel interfaces are more efficient with page aligned buffers or in some cases, even require page aligned buffers. |
I think that's what the system allocator does in some cases, or maybe that's only if the allocation sizes are sufficiently big.
I believe this does not apply to io-uring: I/O operations and |
I beg to differ. (But I'm often wrong.) The tests that come with the liburing C library itself use O_DIRECT in many places, as well as mmap for that very reason. When I have an app that will use the same buffers countless times for I/O, I will certainly want them page aligned. For small use cases, I won't even go to the trouble of registering the buffers first. |
@FrankReh Yes: thats what I do with it. |
For the common case of cassing in a Vec<T>, this should collect in place.
FrankReh
left a comment
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 like it.
This and the related PR about supporting a next Future are making this feel like a very tight aspect of the crate.
@mzabaluev Would you be willing to give this a thumbs up?
|
@FrankReh are all of your concerns addressed? |
|
@mzabaluev, is there anything you would like addressing? |
|
@FrankReh I wanted to reply to this.
You can do this in a unsafe way if that is your use case. Your iterator will produce into something like struct Segment {
backing_buffer: Rc<MmapRaw>,
ptr: *mut u8,
init: usize,
len: usize
}However, because you know they that if the pool is being dropped, none are in use, and that all remain alive until the pool is dropped, you can replace backing_buffer with |
Thanks. I don't mind tapping into unsafe. I had hoped the iterator could be implemented programmatically, but really just to keep from polluting the heap with unnecessary stuff - not to safe space or startup/tear-down time really. For me, setting up a pool would be once at app, or thread start up, and then the buffers get reused for hours or days before a reboot anyway. Also for pools that I want to use, I don't see that tracking init per buf is important. If the compiler lets me, I would avoid tracking init and simply zero all the memory ahead of time. |
FrankReh
left a comment
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.
Looks good to me. I'll plan on merging tomorrow to give others time to still comment. Thanks!
|
I still think this is a wrong place to extend, because it's an imperfect fit for either of the discussed use cases:
I like generics, but not when they de-optimize their most used instantiations. And we already have an extension point in the
While this is a special case where it may be not important (I think zero-initializing is better than admitting UB into your program, though), the proposed change introduces a generic implementation that should work correctly for all buffer types. |
I really don't follow this logic, sorry. I see no extra overhead. |
I believe you get fairly aligned buffers with something like |
A member of the |
Yes. But:
Are you objecting to the presence of the Vec, (pointer, len and capacity) stored?
Its not UB. However, i did update the drop implementation to update the buffers on drop, should they be some Rc backed implementation which takes them elsewhere, and you want to know how many bytes currently rest in them. I'd be very upset if the compiler didn't strip that completely in the case you do just drop, so don't believe this is an overhead. |
|
Hmm, that last change does appear to have broken test - will check Fixed, I think the original had a bug which has just been exposed |
|
@FrankReh You'll want to review the changes here also. I don't think they change the Pr much |
|
I have to wait until much later today to look. Family dentistry emergency. I like the discussion, when it's not personal. It seems the code is getting better as the two main protagonists here are talking. I'll remind everyone the whole idea behind supporting uring is to get at performance gains between the userland and the kernel/hardware and we generally do it in good faith, without benchmarks to prove that we are on the right track. And my opinion is we try to present the most generic approach we can that fits into the boundaries of the tokio ecosystem. If things work out of the box, that makes for a nice user experience. (I hope my opinions aren't used against me.) Thanks. (And a reminder for everyone to take care of their teeth while they can.) |
Indeed. Whilst its true i can find getting things reviewed in this particular repository frustrating, I'll have to admit the review always resulted in better code. As for contributers, I've nothing but thanks for people who give theirs (or their companies time) for improving OS code. I'll note that a lot of time seems to occur here out of hours. |
FrankReh
left a comment
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.
It's looking good. A lot of brain cycles have been put into this little corner of the crate.
FrankReh
left a comment
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.
One unresolved comment left I think.
|
All comments addressed! LGTM? |
FrankReh
left a comment
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.
Well done sir!
Extends both registries to accept any
IterofIoBufMut, instead of Vec.This is motivated by my usecase, which cannot safely provide its memory as a
Vec. Whilst I can unsafely create one,it would be invalid to call drop on the created
VecThis implementation, which explicitly keeps the backing buffers present, rather then
mem::forgetand recreate means theDrophandling is preserved.As a trivial example, this would now admit
Box<[u8]>as a buffer.I realize I could provide my own implementation of the pools, but it seem unnecessary - the generalized version serves both purposes. The overhead is an additional allocation on pool creation, although this will be optimized away by in place specialisation, for the common use case.