Skip to content
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

Optimize File::read_to_end and read_to_string #89582

Merged
merged 1 commit into from
Oct 9, 2021

Conversation

jkugelman
Copy link
Contributor

@jkugelman jkugelman commented Oct 6, 2021

Reading a file into an empty vector or string buffer can incur unnecessary read syscalls and memory re-allocations as the buffer "warms up" and grows to its final size. This is perhaps a necessary evil with generic readers, but files can be read in more intelligently by checking the file size and reserving that much capacity.

std::fs::read and std::fs::read_to_string already perform this optimization: they open the file, reads its metadata, and call with_capacity with the file size. This ensures that the buffer does not need to be resized and avoids an initial string of small read syscalls.

However, if a user opens the File themselves and calls file.read_to_end or file.read_to_string they do not get this optimization.

let mut buf = Vec::new();
file.read_to_end(&mut buf)?;

I searched through this project's codebase and even here there are a lot of examples of this. They're found all over in unit tests, which isn't a big deal, but there are also several real instances in the compiler and in Cargo. I've documented the ones I found in a comment here:

#89516 (comment)

Most telling, the documentation for both the Read trait and the Read::read_to_end method both show this exact pattern as examples of how to use readers. What this says to me is that this shouldn't be solved by simply fixing the instances of it in this codebase. If it's here it's certain to be prevalent in the wider Rust ecosystem.

To that end, this commit adds specializations of read_to_end and read_to_string directly on File. This way it's no longer a minor footgun to start with an empty buffer when reading a file in.

A nice side effect of this change is that code that accesses a File as impl Read or dyn Read will benefit. For example, this code from compiler/rustc_serialize/src/json.rs:

pub fn from_reader(rdr: &mut dyn Read) -> Result<Json, BuilderError> {
    let mut contents = Vec::new();
    match rdr.read_to_end(&mut contents) {

Related changes:

  • I also added specializations to BufReader to delegate to self.inner's methods. That way it can call File's optimized implementations if the inner reader is a file.

  • The private std::io::append_to_string function is now marked unsafe.

  • File::read_to_string being more efficient means that the performance note for io::read_to_string can be softened. I've added @camelid's suggested wording from Tracking Issue for std::io::read_to_string #80218 (comment).

r? @joshtriplett

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Oct 6, 2021
@rust-timer
Copy link
Collaborator

Insufficient permissions to issue commands to rust-timer.

@bors
Copy link
Contributor

bors commented Oct 6, 2021

@jkugelman: 🔑 Insufficient privileges: not in try users

@rust-timer
Copy link
Collaborator

Insufficient permissions to issue commands to rust-timer.

@jkugelman
Copy link
Contributor Author

jkugelman commented Oct 6, 2021

I tested both read_to_end and read_to_string on a 10MB input file. It took 21 read syscalls to read it before, and now it only takes 2.

Before and after straces of read_to_end
use std::env;
use std::fs::File;
use std::io::Read;

pub fn main() {
    let mut file = File::open(env::args().nth(1).expect("no file name")).expect("open failed");
    let mut buf = Vec::new();
    file.read_to_end(&mut buf).expect("read_to_end failed");
}

Before:

$ strace -s 0 ./read_to_end large.txt
...
openat(AT_FDCWD, "large.txt", O_RDONLY|O_CLOEXEC) = 3
read(3, ""..., 32)                      = 32
read(3, ""..., 32)                      = 32
read(3, ""..., 64)                      = 64
read(3, ""..., 128)                     = 128
read(3, ""..., 256)                     = 256
read(3, ""..., 512)                     = 512
read(3, ""..., 1024)                    = 1024
read(3, ""..., 2048)                    = 2048
read(3, ""..., 4096)                    = 4096
read(3, ""..., 8192)                    = 8192
read(3, ""..., 16384)                   = 16384
read(3, ""..., 32768)                   = 32768
read(3, ""..., 65536)                   = 65536
mmap(NULL, 266240, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fbd9ad0b000
read(3, ""..., 131072)                  = 131072
mremap(0x7fbd9ad0b000, 266240, 528384, MREMAP_MAYMOVE) = 0x7fbd9ac8a000
read(3, ""..., 262144)                  = 262144
mremap(0x7fbd9ac8a000, 528384, 1052672, MREMAP_MAYMOVE) = 0x7fbd9ab89000
read(3, ""..., 524288)                  = 524288
mremap(0x7fbd9ab89000, 1052672, 2101248, MREMAP_MAYMOVE) = 0x7fbd9a988000
read(3, ""..., 1048576)                 = 1048576
mremap(0x7fbd9a988000, 2101248, 4198400, MREMAP_MAYMOVE) = 0x7fbd9a587000
read(3, ""..., 2097152)                 = 2097152
mremap(0x7fbd9a587000, 4198400, 8392704, MREMAP_MAYMOVE) = 0x7fbd99d86000
read(3, ""..., 4194304)                 = 4194304
mremap(0x7fbd99d86000, 8392704, 16781312, MREMAP_MAYMOVE) = 0x7fbd98d85000
read(3, ""..., 8388608)                 = 1711392
read(3, "", 6677216)                    = 0
munmap(0x7fbd98d85000, 16781312)        = 0
close(3)                                = 0
...

After:

$ strace -s 0 ./read_to_end large.txt
...
openat(AT_FDCWD, "large.txt", O_RDONLY|O_CLOEXEC) = 3
futex(0x7fb36787f0c8, FUTEX_WAKE_PRIVATE, 2147483647) = 0
statx(0, NULL, AT_STATX_SYNC_AS_STAT, STATX_ALL, NULL) = -1 EFAULT (Bad address)
statx(3, "", AT_STATX_SYNC_AS_STAT|AT_EMPTY_PATH, STATX_ALL, {stx_mask=STATX_ALL|0x1000, stx_attributes=0, stx_mode=S_IFREG|0644, stx_size=10100000, ...}) = 0
mmap(NULL, 10100736, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb366ce4000
read(3, ""..., 10100000)                = 10100000
read(3, "", 32)                         = 0
munmap(0x7fb366ce4000, 10100736)        = 0
close(3)                                = 0
...
Before and after straces of read_to_string
use std::env;
use std::fs::File;
use std::io::Read;

pub fn main() {
    let mut file = File::open(env::args().nth(1).expect("no file name")).expect("open failed");
    let mut buf = String::new();
    file.read_to_string(&mut buf).expect("read_to_string failed");
}

Before:

$ strace -s 0 ./read_to_string large.txt
...
openat(AT_FDCWD, "large.txt", O_RDONLY|O_CLOEXEC) = 3
read(3, ""..., 32)                      = 32
read(3, ""..., 32)                      = 32
read(3, ""..., 64)                      = 64
read(3, ""..., 128)                     = 128
read(3, ""..., 256)                     = 256
read(3, ""..., 512)                     = 512
read(3, ""..., 1024)                    = 1024
read(3, ""..., 2048)                    = 2048
read(3, ""..., 4096)                    = 4096
read(3, ""..., 8192)                    = 8192
read(3, ""..., 16384)                   = 16384
read(3, ""..., 32768)                   = 32768
read(3, ""..., 65536)                   = 65536
mmap(NULL, 266240, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0d54602000
read(3, ""..., 131072)                  = 131072
mremap(0x7f0d54602000, 266240, 528384, MREMAP_MAYMOVE) = 0x7f0d54581000
read(3, ""..., 262144)                  = 262144
mremap(0x7f0d54581000, 528384, 1052672, MREMAP_MAYMOVE) = 0x7f0d54480000
read(3, ""..., 524288)                  = 524288
mremap(0x7f0d54480000, 1052672, 2101248, MREMAP_MAYMOVE) = 0x7f0d5427f000
read(3, ""..., 1048576)                 = 1048576
mremap(0x7f0d5427f000, 2101248, 4198400, MREMAP_MAYMOVE) = 0x7f0d53e7e000
read(3, ""..., 2097152)                 = 2097152
mremap(0x7f0d53e7e000, 4198400, 8392704, MREMAP_MAYMOVE) = 0x7f0d5367d000
read(3, ""..., 4194304)                 = 4194304
mremap(0x7f0d5367d000, 8392704, 16781312, MREMAP_MAYMOVE) = 0x7f0d5267c000
read(3, ""..., 8388608)                 = 1711392
read(3, "", 6677216)                    = 0
munmap(0x7f0d5267c000, 16781312)        = 0
close(3)                                = 0
...

After:

$ strace -s 0 ./read_to_string large.txt
...
openat(AT_FDCWD, "large.txt", O_RDONLY|O_CLOEXEC) = 3
futex(0x7f65ecb160c8, FUTEX_WAKE_PRIVATE, 2147483647) = 0
statx(0, NULL, AT_STATX_SYNC_AS_STAT, STATX_ALL, NULL) = -1 EFAULT (Bad address)
statx(3, "", AT_STATX_SYNC_AS_STAT|AT_EMPTY_PATH, STATX_ALL, {stx_mask=STATX_ALL|0x1000, stx_attributes=0, stx_mode=S_IFREG|0644, stx_size=10100000, ...}) = 0
mmap(NULL, 10100736, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f65ebf7b000
read(3, ""..., 10100000)                = 10100000
read(3, "", 32)                         = 0
munmap(0x7f65ebf7b000, 10100736)        = 0
close(3)                                = 0
...

@jkugelman jkugelman marked this pull request as ready for review October 6, 2021 02:32
@joshtriplett
Copy link
Member

@bors try @rust-timer queue

@rust-timer
Copy link
Collaborator

Awaiting bors try build completion.

@rustbot label: +S-waiting-on-perf

@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Oct 6, 2021
@bors
Copy link
Contributor

bors commented Oct 6, 2021

⌛ Trying commit e5a65adb22f1984fc191b41966c9be0fc8596105 with merge 5b3e6dca0007e54ee39f29f8cce557d735716218...

@bors
Copy link
Contributor

bors commented Oct 6, 2021

☀️ Try build successful - checks-actions
Build commit: 5b3e6dca0007e54ee39f29f8cce557d735716218 (5b3e6dca0007e54ee39f29f8cce557d735716218)

@rust-timer
Copy link
Collaborator

Queued 5b3e6dca0007e54ee39f29f8cce557d735716218 with parent 98a5a98, future comparison URL.

@rust-timer
Copy link
Collaborator

Finished benchmarking commit (5b3e6dca0007e54ee39f29f8cce557d735716218): comparison url.

Summary: This benchmark run did not return any relevant changes.

If you disagree with this performance assessment, please file an issue in rust-lang/rustc-perf.

Benchmarking 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 led to changes in compiler perf.

@bors rollup=never
@rustbot label: +S-waiting-on-review -S-waiting-on-perf -perf-regression

@rustbot rustbot removed the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Oct 6, 2021
@jkugelman jkugelman marked this pull request as draft October 6, 2021 12:31
@jkugelman jkugelman marked this pull request as ready for review October 6, 2021 16:17
@jkugelman jkugelman force-pushed the optimize-file-read-to-end branch 2 times, most recently from f61aad6 to bea4018 Compare October 6, 2021 16:26
@camelid
Copy link
Member

camelid commented Oct 6, 2021

Summary: This benchmark run did not return any relevant changes.

Note that the benchmark suite consists of running rustc and rustdoc on various files. They are not very Read-heavy, so it's unlikely the results accurately reflect this PR's perf effect.

library/std/src/fs.rs Outdated Show resolved Hide resolved
library/std/src/io/mod.rs Outdated Show resolved Hide resolved
@jkugelman jkugelman force-pushed the optimize-file-read-to-end branch 3 times, most recently from 03be6ca to ae3e5a3 Compare October 7, 2021 22:14
@rust-log-analyzer

This comment has been minimized.

Reading a file into an empty vector or string buffer can incur
unnecessary `read` syscalls and memory re-allocations as the buffer
"warms up" and grows to its final size. This is perhaps a necessary evil
with generic readers, but files can be read in smarter by checking the
file size and reserving that much capacity.

`std::fs::read` and `read_to_string` already perform this optimization:
they open the file, reads its metadata, and call `with_capacity` with
the file size. This ensures that the buffer does not need to be resized
and an initial string of small `read` syscalls.

However, if a user opens the `File` themselves and calls
`file.read_to_end` or `file.read_to_string` they do not get this
optimization.

```rust
let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
```

I searched through this project's codebase and even here are a *lot* of
examples of this. They're found all over in unit tests, which isn't a
big deal, but there are also several real instances in the compiler and
in Cargo. I've documented the ones I found in a comment here:

rust-lang#89516 (comment)

Most telling, the `Read` trait and the `read_to_end` method both show
this exact pattern as examples of how to use readers. What this says to
me is that this shouldn't be solved by simply fixing the instances of it
in this codebase. If it's here it's certain to be prevalent in the wider
Rust ecosystem.

To that end, this commit adds specializations of `read_to_end` and
`read_to_string` directly on `File`. This way it's no longer a minor
footgun to start with an empty buffer when reading a file in.

A nice side effect of this change is that code that accesses a `File` as
a bare `Read` constraint or via a `dyn Read` trait object will benefit.
For example, this code from `compiler/rustc_serialize/src/json.rs`:

```rust
pub fn from_reader(rdr: &mut dyn Read) -> Result<Json, BuilderError> {
    let mut contents = Vec::new();
    match rdr.read_to_end(&mut contents) {
```

Related changes:

- I also added specializations to `BufReader` to delegate to
  `self.inner`'s methods. That way it can call `File`'s optimized
  implementations if the inner reader is a file.

- The private `std::io::append_to_string` function is now marked
  `unsafe`.

- `File::read_to_string` being more efficient means that the performance
  note for `io::read_to_string` can be softened. I've added @camelid's
  suggested wording from:

  rust-lang#80218 (comment)
@jkugelman
Copy link
Contributor Author

@joshtriplett I think this is ready to go.

@joshtriplett
Copy link
Member

@bors r+

@bors
Copy link
Contributor

bors commented Oct 9, 2021

📌 Commit a990c76 has been approved by joshtriplett

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Oct 9, 2021
@joshtriplett joshtriplett added the relnotes Marks issues that should be documented in the release notes of the next release. label Oct 9, 2021
@bors
Copy link
Contributor

bors commented Oct 9, 2021

⌛ Testing commit a990c76 with merge 910692d...

@bors
Copy link
Contributor

bors commented Oct 9, 2021

☀️ Test successful - checks-actions
Approved by: joshtriplett
Pushing 910692d to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Oct 9, 2021
@bors bors merged commit 910692d into rust-lang:master Oct 9, 2021
@rustbot rustbot added this to the 1.57.0 milestone Oct 9, 2021
@rust-timer
Copy link
Collaborator

Finished benchmarking commit (910692d): comparison url.

Summary: This benchmark run did not return any relevant changes.

If you disagree with this performance assessment, please file an issue in rust-lang/rustc-perf.

@rustbot label: -perf-regression

@noproto
Copy link
Contributor

noproto commented Oct 25, 2021

This change (added to Nightly 14 days ago) created a regression in Rust that was able to track by bisecting nightly releases. My previously working project now segfaults on Rust Nightly. I'm working on developing a test case, and I'll create a new issue for it.

wip-sync pushed a commit to NetBSD/pkgsrc-wip that referenced this pull request Dec 6, 2021
Pkgsrc changes:
 * Adapt a couple of patches

Upstream changes:

Version 1.57.0 (2021-12-02)
==========================

Language
--------

- [Macro attributes may follow `#[derive]` and will see the original
  (pre-`cfg`) input.][87220]
- [Accept curly-brace macros in expressions, like `m!{ .. }.method()`
  and `m!{ .. }?`.][88690]
- [Allow panicking in constant evaluation.][89508]

Compiler
--------

- [Create more accurate debuginfo for vtables.][89597]
- [Add `armv6k-nintendo-3ds` at Tier 3\*.][88529]
- [Add `armv7-unknown-linux-uclibceabihf` at Tier 3\*.][88952]
- [Add `m68k-unknown-linux-gnu` at Tier 3\*.][88321]
- [Add SOLID targets at Tier 3\*:][86191] `aarch64-kmc-solid_asp3`,
  `armv7a-kmc-solid_asp3-eabi`, `armv7a-kmc-solid_asp3-eabihf`

\* Refer to Rust's [platform support page][platform-support-doc] for more
   information on Rust's tiered platform support.

Libraries
---------

- [Avoid allocations and copying in `Vec::leak`][89337]
- [Add `#[repr(i8)]` to `Ordering`][89507]
- [Optimize `File::read_to_end` and `read_to_string`][89582]
- [Update to Unicode 14.0][89614]
- [Many more functions are marked `#[must_use]`][89692], producing a warning
  when ignoring their return value. This helps catch mistakes such as expecting
  a function to mutate a value in place rather than return a new value.

Stabilised APIs
---------------

- [`[T; N]::as_mut_slice`][`array::as_mut_slice`]
- [`[T; N]::as_slice`][`array::as_slice`]
- [`collections::TryReserveError`]
- [`HashMap::try_reserve`]
- [`HashSet::try_reserve`]
- [`String::try_reserve`]
- [`String::try_reserve_exact`]
- [`Vec::try_reserve`]
- [`Vec::try_reserve_exact`]
- [`VecDeque::try_reserve`]
- [`VecDeque::try_reserve_exact`]
- [`Iterator::map_while`]
- [`iter::MapWhile`]
- [`proc_macro::is_available`]
- [`Command::get_program`]
- [`Command::get_args`]
- [`Command::get_envs`]
- [`Command::get_current_dir`]
- [`CommandArgs`]
- [`CommandEnvs`]

These APIs are now usable in const contexts:

- [`hint::unreachable_unchecked`]

Cargo
-----

- [Stabilize custom profiles][cargo/9943]

Compatibility notes
-------------------

Internal changes
----------------
These changes provide no direct user facing benefits, but represent significant
improvements to the internals and overall performance of rustc
and related tools.

- [Added an experimental backend for codegen with `libgccjit`.][87260]

[86191]: rust-lang/rust#86191
[87220]: rust-lang/rust#87220
[87260]: rust-lang/rust#87260
[88243]: rust-lang/rust#88243
[88321]: rust-lang/rust#88321
[88529]: rust-lang/rust#88529
[88690]: rust-lang/rust#88690
[88952]: rust-lang/rust#88952
[89337]: rust-lang/rust#89337
[89507]: rust-lang/rust#89507
[89508]: rust-lang/rust#89508
[89582]: rust-lang/rust#89582
[89597]: rust-lang/rust#89597
[89614]: rust-lang/rust#89614
[89692]: rust-lang/rust#89692
[cargo/9943]: rust-lang/cargo#9943
[`array::as_mut_slice`]: https://doc.rust-lang.org/std/primitive.array.html#method.as_mut_slice
[`array::as_slice`]: https://doc.rust-lang.org/std/primitive.array.html#method.as_slice
[`collections::TryReserveError`]: https://doc.rust-lang.org/std/collections/struct.TryReserveError.html
[`HashMap::try_reserve`]: https://doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#method.try_reserve
[`HashSet::try_reserve`]: https://doc.rust-lang.org/std/collections/hash_set/struct.HashSet.html#method.try_reserve
[`String::try_reserve`]: https://doc.rust-lang.org/alloc/string/struct.String.html#method.try_reserve
[`String::try_reserve_exact`]: https://doc.rust-lang.org/alloc/string/struct.String.html#method.try_reserve_exact
[`Vec::try_reserve`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.try_reserve
[`Vec::try_reserve_exact`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.try_reserve_exact
[`VecDeque::try_reserve`]: https://doc.rust-lang.org/std/collections/struct.VecDeque.html#method.try_reserve
[`VecDeque::try_reserve_exact`]: https://doc.rust-lang.org/std/collections/struct.VecDeque.html#method.try_reserve_exact
[`Iterator::map_while`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map_while
[`iter::MapWhile`]: https://doc.rust-lang.org/std/iter/struct.MapWhile.html
[`proc_macro::is_available`]: https://doc.rust-lang.org/proc_macro/fn.is_available.html
[`Command::get_program`]: https://doc.rust-lang.org/std/process/struct.Command.html#method.get_program
[`Command::get_args`]: https://doc.rust-lang.org/std/process/struct.Command.html#method.get_args
[`Command::get_envs`]: https://doc.rust-lang.org/std/process/struct.Command.html#method.get_envs
[`Command::get_current_dir`]: https://doc.rust-lang.org/std/process/struct.Command.html#method.get_current_dir
[`CommandArgs`]: https://doc.rust-lang.org/std/process/struct.CommandArgs.html
[`CommandEnvs`]: https://doc.rust-lang.org/std/process/struct.CommandEnvs.html
netbsd-srcmastr pushed a commit to NetBSD/pkgsrc that referenced this pull request Jan 22, 2022
Pkgsrc changes:
 * Adjust line numbers in a number of patches
 * remove the --disable-dist-src option, so that we produce
   the rust-src rust component, which we upload to LOCALSRC
   to allow the rust-src package to build, which is needed
   for rust-analyzer.
 * Cargo checksum for vendor/cc no longer needs patching;
   checksum for vendor/libc updated

Upstream changes:

Version 1.57.0 (2021-12-02)
==========================

Language
--------

- [Macro attributes may follow `#[derive]` and will see the original
  (pre-`cfg`) input.][87220]
- [Accept curly-brace macros in expressions, like `m!{ .. }.method()`
  and `m!{ .. }?`.][88690]
- [Allow panicking in constant evaluation.][89508]

Compiler
--------

- [Create more accurate debuginfo for vtables.][89597]
- [Add `armv6k-nintendo-3ds` at Tier 3\*.][88529]
- [Add `armv7-unknown-linux-uclibceabihf` at Tier 3\*.][88952]
- [Add `m68k-unknown-linux-gnu` at Tier 3\*.][88321]
- [Add SOLID targets at Tier 3\*:][86191] `aarch64-kmc-solid_asp3`,
  `armv7a-kmc-solid_asp3-eabi`, `armv7a-kmc-solid_asp3-eabihf`

\* Refer to Rust's [platform support page][platform-support-doc] for more
   information on Rust's tiered platform support.

Libraries
---------

- [Avoid allocations and copying in `Vec::leak`][89337]
- [Add `#[repr(i8)]` to `Ordering`][89507]
- [Optimize `File::read_to_end` and `read_to_string`][89582]
- [Update to Unicode 14.0][89614]
- [Many more functions are marked `#[must_use]`][89692], producing a warning
  when ignoring their return value. This helps catch mistakes such as expecting
  a function to mutate a value in place rather than return a new value.

Stabilised APIs
---------------

- [`[T; N]::as_mut_slice`][`array::as_mut_slice`]
- [`[T; N]::as_slice`][`array::as_slice`]
- [`collections::TryReserveError`]
- [`HashMap::try_reserve`]
- [`HashSet::try_reserve`]
- [`String::try_reserve`]
- [`String::try_reserve_exact`]
- [`Vec::try_reserve`]
- [`Vec::try_reserve_exact`]
- [`VecDeque::try_reserve`]
- [`VecDeque::try_reserve_exact`]
- [`Iterator::map_while`]
- [`iter::MapWhile`]
- [`proc_macro::is_available`]
- [`Command::get_program`]
- [`Command::get_args`]
- [`Command::get_envs`]
- [`Command::get_current_dir`]
- [`CommandArgs`]
- [`CommandEnvs`]

These APIs are now usable in const contexts:

- [`hint::unreachable_unchecked`]

Cargo
-----

- [Stabilize custom profiles][cargo/9943]

Compatibility notes
-------------------

Internal changes
----------------
These changes provide no direct user facing benefits, but represent significant
improvements to the internals and overall performance of rustc
and related tools.

- [Added an experimental backend for codegen with `libgccjit`.][87260]

[86191]: rust-lang/rust#86191
[87220]: rust-lang/rust#87220
[87260]: rust-lang/rust#87260
[88243]: rust-lang/rust#88243
[88321]: rust-lang/rust#88321
[88529]: rust-lang/rust#88529
[88690]: rust-lang/rust#88690
[88952]: rust-lang/rust#88952
[89337]: rust-lang/rust#89337
[89507]: rust-lang/rust#89507
[89508]: rust-lang/rust#89508
[89582]: rust-lang/rust#89582
[89597]: rust-lang/rust#89597
[89614]: rust-lang/rust#89614
[89692]: rust-lang/rust#89692
[cargo/9943]: rust-lang/cargo#9943
[`array::as_mut_slice`]: https://doc.rust-lang.org/std/primitive.array.html#method.as_mut_slice
[`array::as_slice`]: https://doc.rust-lang.org/std/primitive.array.html#method.as_slice
[`collections::TryReserveError`]: https://doc.rust-lang.org/std/collections/struct.TryReserveError.html
[`HashMap::try_reserve`]: https://doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#method.try_reserve
[`HashSet::try_reserve`]: https://doc.rust-lang.org/std/collections/hash_set/struct.HashSet.html#method.try_reserve
[`String::try_reserve`]: https://doc.rust-lang.org/alloc/string/struct.String.html#method.try_reserve
[`String::try_reserve_exact`]: https://doc.rust-lang.org/alloc/string/struct.String.html#method.try_reserve_exact
[`Vec::try_reserve`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.try_reserve
[`Vec::try_reserve_exact`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.try_reserve_exact
[`VecDeque::try_reserve`]: https://doc.rust-lang.org/std/collections/struct.VecDeque.html#method.try_reserve
[`VecDeque::try_reserve_exact`]: https://doc.rust-lang.org/std/collections/struct.VecDeque.html#method.try_reserve_exact
[`Iterator::map_while`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map_while
[`iter::MapWhile`]: https://doc.rust-lang.org/std/iter/struct.MapWhile.html
[`proc_macro::is_available`]: https://doc.rust-lang.org/proc_macro/fn.is_available.html
[`Command::get_program`]: https://doc.rust-lang.org/std/process/struct.Command.html#method.get_program
[`Command::get_args`]: https://doc.rust-lang.org/std/process/struct.Command.html#method.get_args
[`Command::get_envs`]: https://doc.rust-lang.org/std/process/struct.Command.html#method.get_envs
[`Command::get_current_dir`]: https://doc.rust-lang.org/std/process/struct.Command.html#method.get_current_dir
[`CommandArgs`]: https://doc.rust-lang.org/std/process/struct.CommandArgs.html
[`CommandEnvs`]: https://doc.rust-lang.org/std/process/struct.CommandEnvs.html
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merged-by-bors This PR was explicitly merged by bors. relnotes Marks issues that should be documented in the release notes of the next release. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.