Skip to content

Speed up tidy again - #161201

Open
the8472 wants to merge 4 commits into
rust-lang:mainfrom
the8472:tidy-opts
Open

the8472 wants to merge 4 commits into
rust-lang:mainfrom
the8472:tidy-opts

Conversation

@the8472

@the8472 the8472 commented Aug 16, 2026

Copy link
Copy Markdown
Member

Tidy has slowed down quite a bit over the years (#81833, #105829), so let's recover some perf

OLD

$ hyperfine -w 1 "./x test tidy"
Benchmark #1: ./x test tidy
  Time (mean ± σ):      5.076 s ±  0.057 s    [User: 12.724 s, System: 6.707 s]
  Range (min … max):    4.972 s …  5.148 s    10 runs

$ taskset -c 0-5 hyperfine -w 1 "./x test tidy"
Benchmark #1: ./x test tidy
  Time (mean ± σ):      7.973 s ±  0.099 s    [User: 12.072 s, System: 5.550 s]
  Range (min … max):    7.838 s …  8.184 s    10 runs

NEW

$ hyperfine -w 1 "./x test tidy"
Benchmark #1: ./x test tidy
  Time (mean ± σ):      3.134 s ±  0.055 s    [User: 7.993 s, System: 6.842 s]
  Range (min … max):    3.047 s …  3.204 s    10 runs

$ taskset -c 0-5 hyperfine -w 1 "./x test tidy"
Benchmark #1: ./x test tidy
  Time (mean ± σ):      3.921 s ±  0.129 s    [User: 7.204 s, System: 5.563 s]
  Range (min … max):    3.740 s …  4.069 s    10 runs

After this about half of the remaining time is burned by bootstrap doing git and cargo stuff.

image

Fixes: #141074


LLM disclosure: I used copilot's autocomplete. Most suggestions were trivial (or discarded for being incorrect), with the exception of the Semaphore type which it produced almost wholesale. I compared it to implementations in the wild and they look almost identical, so I hope this too falls under "trivial".

we run it in dev mode by default, so this shaves off a few millis at runtime
@the8472 the8472 added the A-tidy Area: The tidy tool label Aug 16, 2026
@rustbot

rustbot commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator

tidy extra checks were modified.

cc @lolbinarycat

The list of allowed third-party dependencies may have been modified! You must ensure that any new dependencies have compatible licenses before merging.

cc @davidtwco, @BoxyUwU

@rustbot rustbot added A-testsuite Area: The testsuite used to check the correctness of rustc S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) labels Aug 16, 2026
@rustbot

rustbot commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator

r? @Mark-Simulacrum

rustbot has assigned @Mark-Simulacrum.
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: bootstrap
  • bootstrap expanded to 6 candidates
  • Random selection from Mark-Simulacrum, clubby789, jieyouxu

@rust-log-analyzer

This comment has been minimized.

@Kobzol Kobzol left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! Left some comments.

I don't love implementing our own synchronization primitives in tidy, even if they are supposedly simple 😆 I wonder, what do you think about piggybacking upon higher-level sync primitives (a channel) instead?

pub struct Semaphore {
    tx: std::sync::mpsc::SyncSender<()>,
    rx: std::sync::Mutex<std::sync::mpsc::Receiver<()>>,
}

impl Semaphore {
    pub fn new(count: NonZeroUsize) -> Self {
        let (tx, rx) = std::sync::mpsc::sync_channel(count.into());
        for _ in 0..count.into() {
            tx.send(()).unwrap();
        }
        Self { tx, rx: std::sync::Mutex::new(rx) }
    }

    pub fn acquire(&self) -> Guard<'_> {
        let _token = self.rx.lock().unwrap().recv().expect("Cannot read token");
        Guard { _sem: self, tx: self.tx.clone() }
    }
}

pub struct Guard<'a> {
    _sem: &'a Semaphore,
    tx: std::sync::mpsc::SyncSender<()>,
}

impl Drop for Guard<'_> {
    fn drop(&mut self) {
        // Return the token back
        self.tx.send(()).unwrap();
    }
}

View changes since this review

Comment thread src/bootstrap/Cargo.toml
# dependencies, only bootstrap itself.
[profile.dev]
debug = 0
debug-assertions = false

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really measurable? Debug asserts could in theory help find some overflows or similar things. I don't think that we should trade this off.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The asserts showed up in profiles but I haven't measured the change in isolation, will do.

The thing is that we run bootstrap in dev as if it were our release profile... would adding a test profile which reenables them be sufficient?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well we only do that because of compile times, nothing else, really. I guess that we could only use assertions on CI, but I'd like to see if the assertions really make that much of a difference.

}

#[track_caller]
pub fn start(&mut self, exec_ctx: impl AsRef<ExecutionContext>) -> DeferredCommand<'_> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is already the same function called start_capture. I think that you don't want to capture here, though? 🤔

if has_missing_submodule(root, submodules, tidy_ctx.is_running_on_ci()) {
continue;
}
thread::scope(|s| {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this create a new scope when one scope is already passed in?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some leftover from trying different approaches. I'll remove either the local scope or the passed argument.

{
let check = &check;
let checked_runtime_licenses = &checked_runtime_licenses;
let guard = if i > 0 { Some(sem.acquire()) } else { None };

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the semaphore not acquired for the first workspace?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If concurrency==1 then that would hang because the deps module itself already takes up one task slot.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, then please add this as a comment on top of this line.

@Kobzol

Kobzol commented Aug 17, 2026

Copy link
Copy Markdown
Member

Btw, when I run tidy several times in a row, with the disk cache primed, on main the duration is ~9s, while with this PR it is ~8s.

@Kobzol

Kobzol commented Aug 18, 2026

Copy link
Copy Markdown
Member

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-testsuite Area: The testsuite used to check the correctness of rustc A-tidy Area: The tidy tool S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

x test tidy can be faster

5 participants