-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
Reserve before write_fmt
for owned buffers
#137762
base: master
Are you sure you want to change the base?
Reserve before write_fmt
for owned buffers
#137762
Conversation
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
…e-fmt, r=<try> Reserve before `write_fmt` `fmt::Arguments::estimated_capacity()` is currently only used for `fmt::format()` to reserve the initial string capacity. Also use it for `impl Write` for `Vec<u8>`, `VecDeque<u8>`, `Cursor<&mut Vec<u8>>`, and `Cursor<Vec<u8>>`. This may be worth checking perf.
☀️ Try build successful - checks-actions |
This comment has been minimized.
This comment has been minimized.
Finished benchmarking commit (9cdfd5d): comparison URL. Overall result: no relevant changes - no action neededBenchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. While you can manually mark this PR as fit for rollup, we strongly recommend not doing so since this PR may lead to changes in compiler perf. @bors rollup=never Instruction countThis benchmark run did not return any relevant results for this metric. Max RSS (memory usage)Results (primary 10.1%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesThis benchmark run did not return any relevant results for this metric. Binary sizeResults (primary 0.0%, secondary 0.1%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Bootstrap: 770.19s -> 771.304s (0.14%) |
No regressions, so are we good? Also, it probably shouldn't be rollup=never. |
0e82180
to
d8b6fe8
Compare
I had only considered the |
I think the @bors try @rust-timer queue |
@thaliaarchi: 🔑 Insufficient privileges: not in try users |
This comment has been minimized.
This comment has been minimized.
rustdoc uses a lot of formatting, so this might have a positive impact there |
This comment has been minimized.
This comment has been minimized.
…e-fmt, r=<try> Reserve before `write_fmt` `fmt::Arguments::estimated_capacity()` is currently only used by `fmt::format()` to reserve the initial string capacity. Also use it for `impl Write` for `Vec<u8>`, `VecDeque<u8>`, `Cursor<&mut Vec<u8>>`, and `Cursor<Vec<u8>>`. This may be worth checking perf.
☀️ Try build successful - checks-actions |
9c2a9ae
to
93f4a09
Compare
I'm not entirely sure what you changed if anything in your last rebase, @thaliaarchi? |
Since I rebased onto master at the same time, the changes got muddled; sorry. You can use - #[inline]
- fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
- self.reserve(fmt.estimated_capacity());
- io::default_write_fmt(self, fmt)
+ fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> {
+ if let Some(s) = args.as_statically_known_str() {
+ self.write_all(s.as_bytes())
+ } else {
+ self.reserve(args.estimated_capacity());
+ io::default_write_fmt(self, args)
+ }
} I'm marking this PR as a draft for now, pending my investigation into a better heuristic for Since |
…rsors, r=joboet Override default `Write` methods for cursor-like types Override the default `io::Write` methods for cursor-like types to provide more efficient versions. Writes to resizable containers already write everything, so implement `write_all` and `write_all_vectored` in terms of those. For fixed-sized containers, cut out unnecessary error checking and looping for those same methods. | `impl Write for T` | `vectored` | `all` | `all_vectored` | `fmt` | | ------------------------------- | ---------- | ----- | -------------- | ------- | | `&mut [u8]` | Y | Y | new | | | `Vec<u8>` | Y | Y | new | rust-lang#137762 | | `VecDeque<u8>` | Y | Y | new | rust-lang#137762 | | `std::io::Cursor<&mut [u8]>` | Y | new | new | | | `std::io::Cursor<&mut Vec<u8>>` | Y | new | new | rust-lang#137762 | | `std::io::Cursor<Vec<u8>>` | Y | new | new | rust-lang#137762 | | `std::io::Cursor<Box<[u8]>>` | Y | new | new | | | `std::io::Cursor<[u8; N]>` | Y | new | new | | | `core::io::BorrowedCursor<'_>` | new | new | new | | Tracked in rust-lang#136756. # Open questions Is it guaranteed by `Write::write_all` that the maximal write is performed when not everything can be written? Its documentation describes the behavior of the default implementation, which writes until a 0-length write is encountered, thus implying that a maximal write is expected. In contrast, `Read::read_exact` declares that the contents of the buffer are unspecified for short reads. If it were allowed, these cursor-like types could bail on the write altogether if it has insufficient capacity.
…rsors, r=joboet Override default `Write` methods for cursor-like types Override the default `io::Write` methods for cursor-like types to provide more efficient versions. Writes to resizable containers already write everything, so implement `write_all` and `write_all_vectored` in terms of those. For fixed-sized containers, cut out unnecessary error checking and looping for those same methods. | `impl Write for T` | `vectored` | `all` | `all_vectored` | `fmt` | | ------------------------------- | ---------- | ----- | -------------- | ------- | | `&mut [u8]` | Y | Y | new | | | `Vec<u8>` | Y | Y | new | rust-lang#137762 | | `VecDeque<u8>` | Y | Y | new | rust-lang#137762 | | `std::io::Cursor<&mut [u8]>` | Y | new | new | | | `std::io::Cursor<&mut Vec<u8>>` | Y | new | new | rust-lang#137762 | | `std::io::Cursor<Vec<u8>>` | Y | new | new | rust-lang#137762 | | `std::io::Cursor<Box<[u8]>>` | Y | new | new | | | `std::io::Cursor<[u8; N]>` | Y | new | new | | | `core::io::BorrowedCursor<'_>` | new | new | new | | Tracked in rust-lang#136756. # Open questions Is it guaranteed by `Write::write_all` that the maximal write is performed when not everything can be written? Its documentation describes the behavior of the default implementation, which writes until a 0-length write is encountered, thus implying that a maximal write is expected. In contrast, `Read::read_exact` declares that the contents of the buffer are unspecified for short reads. If it were allowed, these cursor-like types could bail on the write altogether if it has insufficient capacity.
…rsors, r=joboet Override default `Write` methods for cursor-like types Override the default `io::Write` methods for cursor-like types to provide more efficient versions. Writes to resizable containers already write everything, so implement `write_all` and `write_all_vectored` in terms of those. For fixed-sized containers, cut out unnecessary error checking and looping for those same methods. | `impl Write for T` | `vectored` | `all` | `all_vectored` | `fmt` | | ------------------------------- | ---------- | ----- | -------------- | ------- | | `&mut [u8]` | Y | Y | new | | | `Vec<u8>` | Y | Y | new | rust-lang#137762 | | `VecDeque<u8>` | Y | Y | new | rust-lang#137762 | | `std::io::Cursor<&mut [u8]>` | Y | new | new | | | `std::io::Cursor<&mut Vec<u8>>` | Y | new | new | rust-lang#137762 | | `std::io::Cursor<Vec<u8>>` | Y | new | new | rust-lang#137762 | | `std::io::Cursor<Box<[u8]>>` | Y | new | new | | | `std::io::Cursor<[u8; N]>` | Y | new | new | | | `core::io::BorrowedCursor<'_>` | new | new | new | | Tracked in rust-lang#136756. # Open questions Is it guaranteed by `Write::write_all` that the maximal write is performed when not everything can be written? Its documentation describes the behavior of the default implementation, which writes until a 0-length write is encountered, thus implying that a maximal write is expected. In contrast, `Read::read_exact` declares that the contents of the buffer are unspecified for short reads. If it were allowed, these cursor-like types could bail on the write altogether if it has insufficient capacity.
…rsors, r=joboet Override default `Write` methods for cursor-like types Override the default `io::Write` methods for cursor-like types to provide more efficient versions. Writes to resizable containers already write everything, so implement `write_all` and `write_all_vectored` in terms of those. For fixed-sized containers, cut out unnecessary error checking and looping for those same methods. | `impl Write for T` | `vectored` | `all` | `all_vectored` | `fmt` | | ------------------------------- | ---------- | ----- | -------------- | ------- | | `&mut [u8]` | Y | Y | new | | | `Vec<u8>` | Y | Y | new | rust-lang#137762 | | `VecDeque<u8>` | Y | Y | new | rust-lang#137762 | | `std::io::Cursor<&mut [u8]>` | Y | new | new | | | `std::io::Cursor<&mut Vec<u8>>` | Y | new | new | rust-lang#137762 | | `std::io::Cursor<Vec<u8>>` | Y | new | new | rust-lang#137762 | | `std::io::Cursor<Box<[u8]>>` | Y | new | new | | | `std::io::Cursor<[u8; N]>` | Y | new | new | | | `core::io::BorrowedCursor<'_>` | new | new | new | | Tracked in rust-lang#136756. # Open questions Is it guaranteed by `Write::write_all` that the maximal write is performed when not everything can be written? Its documentation describes the behavior of the default implementation, which writes until a 0-length write is encountered, thus implying that a maximal write is expected. In contrast, `Read::read_exact` declares that the contents of the buffer are unspecified for short reads. If it were allowed, these cursor-like types could bail on the write altogether if it has insufficient capacity.
Rollup merge of rust-lang#137107 - thaliaarchi:io-optional-methods/cursors, r=joboet Override default `Write` methods for cursor-like types Override the default `io::Write` methods for cursor-like types to provide more efficient versions. Writes to resizable containers already write everything, so implement `write_all` and `write_all_vectored` in terms of those. For fixed-sized containers, cut out unnecessary error checking and looping for those same methods. | `impl Write for T` | `vectored` | `all` | `all_vectored` | `fmt` | | ------------------------------- | ---------- | ----- | -------------- | ------- | | `&mut [u8]` | Y | Y | new | | | `Vec<u8>` | Y | Y | new | rust-lang#137762 | | `VecDeque<u8>` | Y | Y | new | rust-lang#137762 | | `std::io::Cursor<&mut [u8]>` | Y | new | new | | | `std::io::Cursor<&mut Vec<u8>>` | Y | new | new | rust-lang#137762 | | `std::io::Cursor<Vec<u8>>` | Y | new | new | rust-lang#137762 | | `std::io::Cursor<Box<[u8]>>` | Y | new | new | | | `std::io::Cursor<[u8; N]>` | Y | new | new | | | `core::io::BorrowedCursor<'_>` | new | new | new | | Tracked in rust-lang#136756. # Open questions Is it guaranteed by `Write::write_all` that the maximal write is performed when not everything can be written? Its documentation describes the behavior of the default implementation, which writes until a 0-length write is encountered, thus implying that a maximal write is expected. In contrast, `Read::read_exact` declares that the contents of the buffer are unspecified for short reads. If it were allowed, these cursor-like types could bail on the write altogether if it has insufficient capacity.
☔ The latest upstream changes (presumably #138151) made this pull request unmergeable. Please resolve the merge conflicts. |
93f4a09
to
b86d755
Compare
…rsors, r=joboet Override default `Write` methods for cursor-like types Override the default `io::Write` methods for cursor-like types to provide more efficient versions. Writes to resizable containers already write everything, so implement `write_all` and `write_all_vectored` in terms of those. For fixed-sized containers, cut out unnecessary error checking and looping for those same methods. | `impl Write for T` | `vectored` | `all` | `all_vectored` | `fmt` | | ------------------------------- | ---------- | ----- | -------------- | ------- | | `&mut [u8]` | Y | Y | new | | | `Vec<u8>` | Y | Y | new | rust-lang#137762 | | `VecDeque<u8>` | Y | Y | new | rust-lang#137762 | | `std::io::Cursor<&mut [u8]>` | Y | new | new | | | `std::io::Cursor<&mut Vec<u8>>` | Y | new | new | rust-lang#137762 | | `std::io::Cursor<Vec<u8>>` | Y | new | new | rust-lang#137762 | | `std::io::Cursor<Box<[u8]>>` | Y | new | new | | | `std::io::Cursor<[u8; N]>` | Y | new | new | | | `core::io::BorrowedCursor<'_>` | new | new | new | | Tracked in rust-lang#136756. # Open questions Is it guaranteed by `Write::write_all` that the maximal write is performed when not everything can be written? Its documentation describes the behavior of the default implementation, which writes until a 0-length write is encountered, thus implying that a maximal write is expected. In contrast, `Read::read_exact` declares that the contents of the buffer are unspecified for short reads. If it were allowed, these cursor-like types could bail on the write altogether if it has insufficient capacity.
b86d755
to
a1c03f3
Compare
Now that #138650 has merged, so the non- |
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
…e-fmt, r=<try> Reserve before `write_fmt` for owned buffers `fmt::Arguments::estimated_capacity()` is currently only used by `fmt::format()` to reserve the initial string capacity. Also use it in `fmt::Write::write_fmt` for `String` and `OsString`; and `io::Write::write_fmt` for `Vec<u8>`, `VecDeque<u8>`, `Cursor<&mut Vec<u8>>`, and `Cursor<Vec<u8>>`. This may be worth checking perf.
☀️ Try build successful - checks-actions |
This comment has been minimized.
This comment has been minimized.
Finished benchmarking commit (464aedc): comparison URL. Overall result: ❌ regressions - please read the text belowBenchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. While you can manually mark this PR as fit for rollup, we strongly recommend not doing so since this PR may lead to changes in compiler perf. Next Steps: If you can justify the regressions found in this try perf run, please indicate this with @bors rollup=never Instruction countThis is the most reliable metric that we have; it was used to determine the overall result at the top of this comment. However, even this metric can sometimes exhibit noise.
Max RSS (memory usage)Results (primary -3.2%, secondary -3.2%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResults (primary 1.1%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeResults (primary 0.1%, secondary 0.3%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Bootstrap: 775.351s -> 776.139s (0.10%) |
The max RSS results look promising. Since this aims to reduce memory usage, maybe that overrules the other results? I'm not very experienced at interpreting perf results. |
a1c03f3
to
295e573
Compare
I dropped my commit which assumed that |
Reserve before formatting with `fmt::Arguments::estimated_capacity()` in `fmt::Write::write_fmt` and `io::Write::write_fmt` implementations for owned buffer types. Adding `#[inline]` to `write_fmt` shows minor perf regressions, so leave it off like the default impl.
295e573
to
b0a8187
Compare
fmt::Arguments::estimated_capacity()
is currently only used byfmt::format()
to reserve the initial string capacity. Also use it infmt::Write::write_fmt
forString
andOsString
; andio::Write::write_fmt
forVec<u8>
,VecDeque<u8>
,Cursor<&mut Vec<u8>>
, andCursor<Vec<u8>>
.This may be worth checking perf.