-
Notifications
You must be signed in to change notification settings - Fork 132
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
Introduce a StreamingBuffer #348
Conversation
As for the rationale for this change, a non-trivial portion of the time necessary to create the metadata object file in rustc is spent during |
I don't see the reason for all of these API changes. Although it isn't documented well, I believe the current API already does what you want. So all that is needed is a |
The change from the The exact allocation of several vecs is just a perf optimization to avoid unnecessary reallocations that copy the full contents of the vec over to the new allocation, which is expensive for bigger vecs. The stricter api contract is not necessary for |
I don't see why that optimisation can't be done with the old
Those changes are fine.
yurydelendik already used it for something like this in wasmtime (although I'm not sure that those changes ever landed). It was the reason this trait was added. It isn't documented properly, but the contract always was the we do a single reserve before writing. |
How do you then cheaply create a slice to write? You can't slice a promoted |
Ok, so delete the |
This avoids unnecessary reallocations that need to copy the already written data over.
In preparation for adding a streaming file writer
5bc3945
to
4ecc720
Compare
Done |
fn reserve(&mut self, additional: usize) -> Result<(), ()> { | ||
self.reserve(additional); | ||
fn reserve(&mut self, size: usize) -> Result<(), ()> { | ||
assert_eq!(self.len(), 0, "Buffer must be empty"); |
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.
assert_eq!(self.len(), 0, "Buffer must be empty"); | |
debug_assert!(self.is_empty()); |
|
||
#[inline] | ||
fn write_bytes(&mut self, val: &[u8]) { | ||
self.writer.write_all(val).unwrap(); |
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'm not sure that unwrap is correct here.
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 isn't. I forgot to change this function to return std::io::Result<()>
.
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'd be nice if instead of returning a std::io::Result
here, we cached the error value and ignored all future writes, and then returned the error value at the end.
By the way, it becomes a memset anyway when using a |
In the |
Yes, but if that is then immediately copied into a |
This will probably conflict with #350 (please don't do the write result change until after it is merged). |
This streams an object file to the given writer without keeping it all in memory at once. This avoids an unnecessary copy of all data when you want to write the object file to the disk immediately afterwards anyway. I also made the api contract of
WritableBuffer
a bit stricter to for example allow writing to a file using memory mapping by requiring that the size given to.reserve()
matches matches the final size of the object file. Finally I pre-allocated a couple of vecs with the final size to prevent re-allocations.