-
-
Notifications
You must be signed in to change notification settings - Fork 14.2k
Remove initialized-bytes tracking from BorrowedBuf and BorrowedCursor
#148937
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
Remove initialized-bytes tracking from BorrowedBuf and BorrowedCursor
#148937
Conversation
7de0924 to
d131ff2
Compare
This comment has been minimized.
This comment has been minimized.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
| /// `BorrowedCursor`. The cursor has write-only access to the unfilled portion of the buffer. | ||
| /// | ||
| /// The lifetime `'data` is a bound on the lifetime of the underlying data. | ||
| pub struct BorrowedBuf<'data> { |
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.
Like we previously discussed, this should be made generic over T. For now it's fine to limit it to Copy types, but we may want to relax this later.
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.
@Amanieu My intention was to do that in a separate PR, to make this PR more manageable to review.
This is a great catch, thank you. I would be inclined to remove the support for making a And removing the support for |
|
We need support for constructing from a |
Wouldn't that still require effectively all of the initialized-bytes tracking we currently have? Or did you have some other strategy in mind here? Would it suffice to drop the change making (That does still seem unfortunate, though, as it'd be that much harder to use this API from safe code.) |
|
|
I see. Then I'll remove the |
…sor` As discussed extensively in libs-api, the initialized-bytes tracking primarily benefits calls to `read_buf` that end up initializing the buffer and calling `read`, at the expense of calls to `read_buf` that *don't* need to initialize the buffer. Essentially, this optimizes for the past at the expense of the future. If people observe performance issues using `read_buf` (or something that calls it) with a given `Read` impl, they can fix those performance issues by implementing `read_buf` for that `Read`. Update the documentation to stop talking about initialized-but-unfilled bytes. Remove all functions that just deal with those bytes and their tracking, and remove usage of those methods. Remove `BorrowedCursor::advance` as there's no longer a safe case for advancing within initialized-but-unfilled bytes. Rename `BorrowedCursor::advance_unchecked` to `advance`. Update tests.
d131ff2 to
3825099
Compare
This comment has been minimized.
This comment has been minimized.
|
@bors r+ |
Rollup of 3 pull requests Successful merges: - #148937 (Remove initialized-bytes tracking from `BorrowedBuf` and `BorrowedCursor`) - #149553 (added default_uwtables=true to aarch64_unknown_none targets) - #149578 (rustdoc: Fix broken link to `Itertools::format`) r? `@ghost` `@rustbot` modify labels: rollup
Rollup of 3 pull requests Successful merges: - #148937 (Remove initialized-bytes tracking from `BorrowedBuf` and `BorrowedCursor`) - #149553 (added default_uwtables=true to aarch64_unknown_none targets) - #149578 (rustdoc: Fix broken link to `Itertools::format`) r? `@ghost` `@rustbot` modify labels: rollup
Rollup merge of #148937 - joshtriplett:borrowed-buf-no-init-tracking, r=Amanieu Remove initialized-bytes tracking from `BorrowedBuf` and `BorrowedCursor` As discussed extensively in libs-api, the initialized-bytes tracking primarily benefits calls to `read_buf` that end up initializing the buffer and calling `read`, at the expense of calls to `read_buf` that *don't* need to initialize the buffer. Essentially, this optimizes for the past at the expense of the future. If people observe performance issues using `read_buf` (or something that calls it) with a given `Read` impl, they can fix those performance issues by implementing `read_buf` for that `Read`. Update the documentation to stop talking about initialized-but-unfilled bytes. Remove all functions that just deal with those bytes and their tracking, and remove usage of those methods. Remove `BorrowedCursor::advance` as there's no longer a safe case for advancing within initialized-but-unfilled bytes. Rename `BorrowedCursor::advance_unchecked` to `advance`. Update tests. r? ``@Amanieu``
| /// has write-only access to the unfilled portion of the buffer (you can think of it as a | ||
| /// write-only iterator). | ||
| /// Note that `BorrowedBuf` does not distinguish between uninitialized data and data that was | ||
| /// previously initialized but no longer contains valid data. |
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.
"data... that no longer contains valid data" sounds a bit odd?
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.
Sorry that I missed that PR before it was merged, but I don't think that it should have been merged as-is. I may have missed some discussion, though.
If people observe performance issues using
read_buf(or something that calls it) with a givenReadimpl, they can fix those performance issues by implementingread_buffor thatRead.
Well, actually they can't, because read_buf is unstable.
The design that was removed did not optimize for the past, but the present: most Read impls outside of std cannot implement read_buf, and it costs them quite a bit to remove all initialized bytes tracking.
I agree that such a change is desirable for the API, but removing all "compatibility" code now, when there is no clear path to stabilization, is too early IMHO. It at least should have been discussed a bit more.
What I would have liked to see:
- Make init bytes tracking cheaper by only tracking initialization at
BorrowedCursorlevel, not byte level, eg changinginitfromusizetobool. (I had a draft branch at https://github.com/a1phyr/rust/tree/improve_buf_api if you want, but doingread_to_endright is a bit tricky). - and/or keeping the public API as you propose to make it ready for stabilization, and keep the old one private to keep compatibility until people can actually implement
read_buf
Also, in the future could you link the tracking issue (#78485 or #117693) please? It makes stuff easier to follow.
| let mut buf = BorrowedBuf::from(&mut *self.buf); | ||
| // SAFETY: `self.filled` bytes will always have been initialized. | ||
| unsafe { | ||
| buf.set_init(self.initialized); | ||
| } | ||
|
|
||
| let result = reader.read_buf(buf.unfilled()); | ||
|
|
||
| self.pos = 0; | ||
| self.filled = buf.len(); | ||
| self.initialized = buf.init_len(); |
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 means that for most Read impls, filling the buffer will cost an additional memset, which is not great.
| // SAFETY: init is either 0 or the init_len from the previous iteration. | ||
| read_buf.set_init(init); | ||
| } | ||
|
|
||
| if read_buf.capacity() >= DEFAULT_BUF_SIZE { | ||
| let mut cursor = read_buf.unfilled(); | ||
| match reader.read_buf(cursor.reborrow()) { |
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.
Same here but for all copied bytes.
| // SAFETY: We do not uninitialize any part of the buffer. | ||
| let n = read(unsafe { cursor.as_mut().write_filled(0) })?; | ||
| assert!(n <= cursor.capacity()); | ||
| // SAFETY: We've initialized the entire buffer, and `read` can't make it uninitialized. | ||
| unsafe { | ||
| cursor.advance(n); | ||
| } |
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.
Too bad there is unsafe code here now... I really liked the fact that read_buf was easy to implement before.
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.
Same here, for all copied bytes (this is the base io::copy case).
| unsafe { | ||
| read_buf.set_init(initialized); | ||
| } | ||
|
|
||
| let mut cursor = read_buf.unfilled(); |
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.
Same here, this will be a perf regression for slow readers (eg one that returns one byte at a time).
Right this is all unstable APIs so it only affects nightly users, no?
The point of this change is to put it on a path to stabilization. |
No, because I'm not sure that we really want to regress all these use cases while there is no fix for users. As I said, we can probably provide these API but private to
Granted, but unfortunately we are not there yet. |
As discussed extensively in libs-api, the initialized-bytes tracking primarily benefits calls to
read_bufthat end up initializing the buffer and callingread, at the expense of calls toread_bufthat don't need to initialize the buffer. Essentially, this optimizes for the past at the expense of the future. If people observe performance issues usingread_buf(or something that calls it) with a givenReadimpl, they can fix those performance issues by implementingread_buffor thatRead.Update the documentation to stop talking about initialized-but-unfilled bytes.
Remove all functions that just deal with those bytes and their tracking, and remove usage of those methods.
Remove
BorrowedCursor::advanceas there's no longer a safe case for advancing within initialized-but-unfilled bytes. RenameBorrowedCursor::advance_uncheckedtoadvance.Update tests.
r? @Amanieu