Skip to content

Commit

Permalink
Auto merge of #131954 - the8472:bootstrap-parallel-git, r=Kobzol
Browse files Browse the repository at this point in the history
shave 150ms off bootstrap

This starts `git` commands inside `GitInfo`and the submodule updates in parallel. Git should already perform internal locking in cases where it needs to serialize a modification.

```
OLD
Benchmark #1: ./x check core
  Time (mean ± σ):     608.7 ms ±   4.4 ms    [User: 368.3 ms, System: 455.1 ms]
  Range (min … max):   602.3 ms … 618.8 ms    10 runs

NEW
Benchmark #1: ./x check core
  Time (mean ± σ):     462.8 ms ±   2.6 ms    [User: 350.2 ms, System: 485.1 ms]
  Range (min … max):   457.5 ms … 465.6 ms    10 runs
```

This should help with the rust-analyzer setup which issues many individual `./x check` calls. There's more that could be done but these were the lowest-hanging fruits that I saw.
  • Loading branch information
bors committed Oct 21, 2024
2 parents 3ec4308 + a269e4d commit 39f707f
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 14 deletions.
19 changes: 12 additions & 7 deletions src/bootstrap/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -545,23 +545,28 @@ impl Build {
.args(["--get-regexp", "path"])
.run_capture(self)
.stdout();
for line in output.lines() {
std::thread::scope(|s| {
// Look for `submodule.$name.path = $path`
// Sample output: `submodule.src/rust-installer.path src/tools/rust-installer`
let submodule = line.split_once(' ').unwrap().1;
self.update_existing_submodule(submodule);
}
for line in output.lines() {
let submodule = line.split_once(' ').unwrap().1;
let config = self.config.clone();
s.spawn(move || {
Self::update_existing_submodule(&config, submodule);
});
}
});
}

/// Updates the given submodule only if it's initialized already; nothing happens otherwise.
pub fn update_existing_submodule(&self, submodule: &str) {
pub fn update_existing_submodule(config: &Config, submodule: &str) {
// Avoid running git when there isn't a git checkout.
if !self.config.submodules() {
if !config.submodules() {
return;
}

if GitInfo::new(false, Path::new(submodule)).is_managed_git_subrepository() {
self.config.update_submodule(submodule);
config.update_submodule(submodule);
}
}

Expand Down
14 changes: 7 additions & 7 deletions src/bootstrap/src/utils/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::path::Path;

use super::helpers;
use crate::Build;
use crate::utils::helpers::{output, t};
use crate::utils::helpers::{start_process, t};

#[derive(Clone, Default)]
pub enum GitInfo {
Expand Down Expand Up @@ -56,7 +56,7 @@ impl GitInfo {
}

// Ok, let's scrape some info
let ver_date = output(
let ver_date = start_process(
helpers::git(Some(dir))
.arg("log")
.arg("-1")
Expand All @@ -65,14 +65,14 @@ impl GitInfo {
.as_command_mut(),
);
let ver_hash =
output(helpers::git(Some(dir)).arg("rev-parse").arg("HEAD").as_command_mut());
let short_ver_hash = output(
start_process(helpers::git(Some(dir)).arg("rev-parse").arg("HEAD").as_command_mut());
let short_ver_hash = start_process(
helpers::git(Some(dir)).arg("rev-parse").arg("--short=9").arg("HEAD").as_command_mut(),
);
GitInfo::Present(Some(Info {
commit_date: ver_date.trim().to_string(),
sha: ver_hash.trim().to_string(),
short_sha: short_ver_hash.trim().to_string(),
commit_date: ver_date().trim().to_string(),
sha: ver_hash().trim().to_string(),
short_sha: short_ver_hash().trim().to_string(),
}))
}

Expand Down
27 changes: 27 additions & 0 deletions src/bootstrap/src/utils/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,33 @@ pub fn output(cmd: &mut Command) -> String {
String::from_utf8(output.stdout).unwrap()
}

/// Spawn a process and return a closure that will wait for the process
/// to finish and then return its output. This allows the spawned process
/// to do work without immediately blocking bootstrap.
#[track_caller]
pub fn start_process(cmd: &mut Command) -> impl FnOnce() -> String {
let child = match cmd.stderr(Stdio::inherit()).stdout(Stdio::piped()).spawn() {
Ok(child) => child,
Err(e) => fail(&format!("failed to execute command: {cmd:?}\nERROR: {e}")),
};

let command = format!("{:?}", cmd);

move || {
let output = child.wait_with_output().unwrap();

if !output.status.success() {
panic!(
"command did not execute successfully: {}\n\
expected success, got: {}",
command, output.status
);
}

String::from_utf8(output.stdout).unwrap()
}
}

/// Returns the last-modified time for `path`, or zero if it doesn't exist.
pub fn mtime(path: &Path) -> SystemTime {
fs::metadata(path).and_then(|f| f.modified()).unwrap_or(UNIX_EPOCH)
Expand Down

0 comments on commit 39f707f

Please sign in to comment.