Skip to content

libtest: never iterate over all tests in --exact mode - #161868

Merged
rust-bors[bot] merged 6 commits into
rust-lang:mainfrom
RalfJung:libtest-less-alloc
Sep 15, 2026
Merged

rust-bors[bot] merged 6 commits into
rust-lang:mainfrom
RalfJung:libtest-less-alloc

Conversation

@RalfJung

@RalfJung RalfJung commented Aug 27, 2026

Copy link
Copy Markdown
Member

View all comments

This should help with rust-lang/miri#5013:

When running cargo miri nextest, nextest spawns one Miri instance for each test of the crate. If the crate has a lot of tests, a non-trivial amount of time is spent in the test harness before it even starts running the test. Turns out almost half that time is spent just making copies of all the TestDescAndFn. That seems silly, we have a perfectly fine static array of those sitting around in the code generated by --test expansion, let's just use references to that array.

So this changes the TestList used to represent the unfiltered list of tests to use borrowed rather than owned types. That changes ripples outwards. The entry points used by the --test harness remain mostly unchanged (except that I renamed them as the old name did not make sense), but rustdoc has been using the old fully-owned API.

Rustdoc has two codepaths, "standalone" and "merged".

  • For "merged", the fix is easy -- like the --test harness, this can just generate a static array full of StaticTestFn rather than populating a Vec at runtime.
  • For "standalone", things are more tricky. We need to construct an &[&TestDescAndFn], which requires filling a new vector with references to the entries of an existing Vec<TestDescAndFn>. This also fundamentally relies on the support for dynamic test functions in libtest. Those must now always be cloneable, so they are now internally stored in Arc and must be Fn, not FnOnce. So compared to before there's now one more big Vec to fill with references to all tests, as well as some Arc::clone. OTOH this mode runs a full separate process for each test so I doubt this extra cost is noticeable.
    The alternative is to keep support for FnOnce dynamic tests in libtest, which is highly non-trivial due to having to plumb multiple layers of Cow-like handling through everything. I don't think it's worth it, given that dynamic tests are only used by "standalone" rustdoc and by the tests testing libtest.

Overall this saves more than 1s when running coretests (which has 2787 tests) with a hot incremental cache with --exact char::test_is_numeric.
Before: 7.606s
After: 6.484s
Given that most of that time is actually spent in rustc, not in the interpreter, that is a very big speedup for the interpreter part.

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Aug 27, 2026
@rustbot rustbot added the T-libs Relevant to the library team, which will review and decide on the PR/issue. label Aug 27, 2026
@rustbot

rustbot commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

r? @JohnTitor

rustbot has assigned @JohnTitor.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: libs
  • libs expanded to 12 candidates
  • Random selection from JohnTitor, Mark-Simulacrum, clarfonthey, joboet, nia-e

@RalfJung
RalfJung force-pushed the libtest-less-alloc branch 3 times, most recently from 33e044a to ffc55d6 Compare August 27, 2026 13:20
@RalfJung RalfJung changed the title libtest: avoid allocating the tests we do not even run libtest: never iterate over all tests in --exact mode Aug 27, 2026
@rust-log-analyzer

This comment has been minimized.

@RalfJung

RalfJung commented Aug 27, 2026

Copy link
Copy Markdown
Member Author

I am not sure what is going on with rustdoc. Even with ./x check --stage 2 src/librustdoc, it seems to not pick up my libtest changes. @rust-lang/rustdoc is that expected?

Also I noticed the test closures rustdoc uses for the "standalone" codepath rely on their FnOnce nature quite a bit. Making them cloneable (in an Arc<Fn>) could be tricky (and it doesn't help that I can't even compile rustdoc against the new libtest to check if this builds). Keeping non-clonable functions supported in libtest is also annoying. Not sure which one is the lesser evil...

@GuillaumeGomez

Copy link
Copy Markdown
Member

Didn't look but isn't it a bootstrap issue?

@RalfJung

RalfJung commented Aug 27, 2026

Copy link
Copy Markdown
Member Author

I don't know. I'm not sure what the expected behavior even is.

It may be a download-rustc problem. I'm now trying with that turned off.
EDIT: Yeah that was it.

@RalfJung
RalfJung force-pushed the libtest-less-alloc branch from 93e6cbb to 70ce450 Compare August 27, 2026 15:15
@RalfJung

Copy link
Copy Markdown
Member Author

Okay, I think rustdoc should compile again. We'll see if I adjusted the auto-generated code correctly, that's harder to test.^^

The "standalone" codepath has extra overhead now, there's some more cloning. My understanding is that the "merged" codepath is the preferred one so I hope that's fine.

@RalfJung
RalfJung force-pushed the libtest-less-alloc branch 4 times, most recently from 842218d to 83fdcbd Compare August 27, 2026 15:47
@rustbot

rustbot commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

rustc-dev-guide is developed in its own repository. If possible, consider making this change to rust-lang/rustc-dev-guide instead.

cc @BoxyUwU, @tshepang

@rustbot rustbot added the A-rustc-dev-guide Area: rustc-dev-guide label Aug 27, 2026
@rust-log-analyzer

This comment has been minimized.

@RalfJung

This comment was marked as resolved.

@RalfJung
RalfJung force-pushed the libtest-less-alloc branch from 83fdcbd to 616df7f Compare August 27, 2026 17:57
@rust-log-analyzer

This comment has been minimized.

@RalfJung

This comment was marked as resolved.

@RalfJung
RalfJung force-pushed the libtest-less-alloc branch from 616df7f to 1277df4 Compare August 27, 2026 20:47
@rustbot

rustbot commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

clippy is developed in its own repository. If possible, consider making this change to rust-lang/rust-clippy instead.

cc @rust-lang/clippy

@rustbot rustbot added the T-clippy Relevant to the Clippy team. label Aug 27, 2026
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

Copy link
Copy Markdown
Collaborator

The job dist-x86_64-msvc failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)

@rust-bors rust-bors Bot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Sep 15, 2026
@rust-bors

rust-bors Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

💔 Test for 29adbb8 failed: CI. Failed job:

@RalfJung

Copy link
Copy Markdown
Member Author

There's no error visible in that log, it just cuts off at some point.

@bors retry

@rust-bors rust-bors Bot 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 Sep 15, 2026
rust-bors Bot pushed a commit that referenced this pull request Sep 15, 2026
…uwer

Rollup of 12 pull requests

Successful merges:

 - #160911 (Remove d32 feature from 32-bit Arm targets)
 - #161868 (libtest: never iterate over all tests in `--exact` mode)
 - #162771 (Filter do_not_recommend impls before handling a single candidate)
 - #162779 (rustdoc: Revert "fix bare urls split text")
 - #161548 (hir_typeck: Don't ICE on closures without drop location in closure capture lint)
 - #161612 (std: make a lot of items crate private)
 - #162204 (Suggest keyword order for `extern "C" const unsafe fn`)
 - #162372 (Clean up `test/rustdoc-html` folder by moving tests where appropriate)
 - #162638 (dont suggest changing the mutability of a borrow that comes from a macro)
 - #162643 (Fix `path_trailing_sep` methods for Windows verbatim paths)
 - #162654 (Improve Armv7-R documentation)
 - #162784 (AGENTS.md: Permit local experimentation, per the online policy.)
@RalfJung

Copy link
Copy Markdown
Member Author

@bors try jobs=dist-x86_64-msvc

@rust-bors

rust-bors Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

⌛ Trying commit cae9fb4 with merge f3337a8

To cancel the try build, run the command @bors try cancel.

Workflow: https://github.com/rust-lang/rust/actions/runs/34948086607

rust-bors Bot pushed a commit that referenced this pull request Sep 15, 2026
libtest: never iterate over all tests in `--exact` mode


try-job: dist-x86_64-msvc
@rust-bors
rust-bors Bot merged commit 7aad439 into rust-lang:main Sep 15, 2026
13 of 15 checks passed
rust-bors Bot pushed a commit that referenced this pull request Sep 15, 2026
Rollup merge of #161868 - RalfJung:libtest-less-alloc, r=Mark-Simulacrum

libtest: never iterate over all tests in `--exact` mode

This should help with rust-lang/miri#5013:

When running `cargo miri nextest`, nextest spawns one Miri instance for each test of the crate. If the crate has a lot of tests, a non-trivial amount of time is spent in the test harness before it even starts running the test. Turns out almost half that time is spent just making copies of all the `TestDescAndFn`. That seems silly, we have a perfectly fine static array of those sitting around in the code generated by `--test` expansion, let's just use references to that array.

So this changes the `TestList` used to represent the unfiltered list of tests to use borrowed rather than owned types. That changes ripples outwards. The entry points used by the `--test` harness remain mostly unchanged (except that I renamed them as the old name did not make sense), but rustdoc has been using the old fully-owned API.

Rustdoc has two codepaths, "standalone" and "merged".
- For "merged", the fix is easy -- like the `--test` harness, this can just generate a static array full of `StaticTestFn` rather than populating a `Vec` at runtime.
- For "standalone", things are more tricky. We need to construct an `&[&TestDescAndFn]`, which requires filling a new vector with references to the entries of an existing `Vec<TestDescAndFn>`. This also fundamentally relies on the support for dynamic test functions in libtest. Those must now always be cloneable, so they are now internally stored in `Arc` and must be `Fn`, not `FnOnce`. So compared to before there's now one more big `Vec` to fill with references to all tests, as well as some `Arc::clone`. OTOH this mode runs a full separate process for each test so I doubt this extra cost is noticeable.
  The alternative is to keep support for `FnOnce` dynamic tests in libtest, which is highly non-trivial due to having to plumb multiple layers of `Cow`-like handling through everything. I don't think it's worth it, given that dynamic tests are only used by "standalone" rustdoc and by the tests testing libtest.

Overall this saves more than 1s when running coretests (which has 2787 tests) with a hot incremental cache with `--exact char::test_is_numeric`.
Before: 7.606s
After: 6.484s
Given that most of that time is actually spent in rustc, not in the interpreter, that is a very big speedup for the interpreter part.
@rustbot rustbot added this to the 1.100.0 milestone Sep 15, 2026
@RalfJung

Copy link
Copy Markdown
Member Author

@bors try cancel

@rust-bors

rust-bors Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Try build cancelled. Cancelled workflows:

Hint: if you want to run another try build, you do not need to manually cancel the previous one. Just run @bors try and bors will cancel the previous build automatically.

@RalfJung
RalfJung deleted the libtest-less-alloc branch September 16, 2026 06:03
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Sep 16, 2026
…oli-obk

libtest: do not early exit from test runners

Suggested by @Mark-Simulacrum in rust-lang#161868. I finally figured out why my earlier attempts did not work.
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Sep 16, 2026
…oli-obk

libtest: do not early exit from test runners

Suggested by @Mark-Simulacrum in rust-lang#161868. I finally figured out why my earlier attempts did not work.
Zalathar added a commit to Zalathar/rust that referenced this pull request Sep 17, 2026
…oli-obk

libtest: do not early exit from test runners

Suggested by @Mark-Simulacrum in rust-lang#161868. I finally figured out why my earlier attempts did not work.
Zalathar added a commit to Zalathar/rust that referenced this pull request Sep 17, 2026
…oli-obk

libtest: do not early exit from test runners

Suggested by @Mark-Simulacrum in rust-lang#161868. I finally figured out why my earlier attempts did not work.
Zalathar added a commit to Zalathar/rust that referenced this pull request Sep 17, 2026
…oli-obk

libtest: do not early exit from test runners

Suggested by @Mark-Simulacrum in rust-lang#161868. I finally figured out why my earlier attempts did not work.
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Sep 17, 2026
…oli-obk

libtest: do not early exit from test runners

Suggested by @Mark-Simulacrum in rust-lang#161868. I finally figured out why my earlier attempts did not work.
flip1995 pushed a commit to flip1995/rust that referenced this pull request Sep 17, 2026
…k-Simulacrum

libtest: never iterate over all tests in `--exact` mode

This should help with rust-lang/miri#5013:

When running `cargo miri nextest`, nextest spawns one Miri instance for each test of the crate. If the crate has a lot of tests, a non-trivial amount of time is spent in the test harness before it even starts running the test. Turns out almost half that time is spent just making copies of all the `TestDescAndFn`. That seems silly, we have a perfectly fine static array of those sitting around in the code generated by `--test` expansion, let's just use references to that array.

So this changes the `TestList` used to represent the unfiltered list of tests to use borrowed rather than owned types. That changes ripples outwards. The entry points used by the `--test` harness remain mostly unchanged (except that I renamed them as the old name did not make sense), but rustdoc has been using the old fully-owned API.

Rustdoc has two codepaths, "standalone" and "merged".
- For "merged", the fix is easy -- like the `--test` harness, this can just generate a static array full of `StaticTestFn` rather than populating a `Vec` at runtime.
- For "standalone", things are more tricky. We need to construct an `&[&TestDescAndFn]`, which requires filling a new vector with references to the entries of an existing `Vec<TestDescAndFn>`. This also fundamentally relies on the support for dynamic test functions in libtest. Those must now always be cloneable, so they are now internally stored in `Arc` and must be `Fn`, not `FnOnce`. So compared to before there's now one more big `Vec` to fill with references to all tests, as well as some `Arc::clone`. OTOH this mode runs a full separate process for each test so I doubt this extra cost is noticeable.
  The alternative is to keep support for `FnOnce` dynamic tests in libtest, which is highly non-trivial due to having to plumb multiple layers of `Cow`-like handling through everything. I don't think it's worth it, given that dynamic tests are only used by "standalone" rustdoc and by the tests testing libtest.

Overall this saves more than 1s when running coretests (which has 2787 tests) with a hot incremental cache with `--exact char::test_is_numeric`.
Before: 7.606s
After: 6.484s
Given that most of that time is actually spent in rustc, not in the interpreter, that is a very big speedup for the interpreter part.
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Sep 17, 2026
…oli-obk

libtest: do not early exit from test runners

Suggested by @Mark-Simulacrum in rust-lang#161868. I finally figured out why my earlier attempts did not work.
github-actions Bot pushed a commit to rust-lang/rustc-dev-guide that referenced this pull request Sep 17, 2026
…uwer

Rollup of 12 pull requests

Successful merges:

 - rust-lang/rust#160911 (Remove d32 feature from 32-bit Arm targets)
 - rust-lang/rust#161868 (libtest: never iterate over all tests in `--exact` mode)
 - rust-lang/rust#162771 (Filter do_not_recommend impls before handling a single candidate)
 - rust-lang/rust#162779 (rustdoc: Revert "fix bare urls split text")
 - rust-lang/rust#161548 (hir_typeck: Don't ICE on closures without drop location in closure capture lint)
 - rust-lang/rust#161612 (std: make a lot of items crate private)
 - rust-lang/rust#162204 (Suggest keyword order for `extern "C" const unsafe fn`)
 - rust-lang/rust#162372 (Clean up `test/rustdoc-html` folder by moving tests where appropriate)
 - rust-lang/rust#162638 (dont suggest changing the mutability of a borrow that comes from a macro)
 - rust-lang/rust#162643 (Fix `path_trailing_sep` methods for Windows verbatim paths)
 - rust-lang/rust#162654 (Improve Armv7-R documentation)
 - rust-lang/rust#162784 (AGENTS.md: Permit local experimentation, per the online policy.)
rust-bors Bot pushed a commit that referenced this pull request Sep 17, 2026
Rollup merge of #162796 - RalfJung:libtest-no-early-exit, r=oli-obk

libtest: do not early exit from test runners

Suggested by @Mark-Simulacrum in #161868. I finally figured out why my earlier attempts did not work.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-rustc-dev-guide Area: rustc-dev-guide S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-clippy Relevant to the Clippy team. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants