Skip to content

Commit aba227b

Browse files
Rollup merge of rust-lang#131954 - the8472:bootstrap-parallel-git, r=Kobzol
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.
2 parents 1b24c6f + a269e4d commit aba227b

File tree

3 files changed

+46
-14
lines changed

3 files changed

+46
-14
lines changed

src/bootstrap/src/lib.rs

+12-7
Original file line numberDiff line numberDiff line change
@@ -545,23 +545,28 @@ impl Build {
545545
.args(["--get-regexp", "path"])
546546
.run_capture(self)
547547
.stdout();
548-
for line in output.lines() {
548+
std::thread::scope(|s| {
549549
// Look for `submodule.$name.path = $path`
550550
// Sample output: `submodule.src/rust-installer.path src/tools/rust-installer`
551-
let submodule = line.split_once(' ').unwrap().1;
552-
self.update_existing_submodule(submodule);
553-
}
551+
for line in output.lines() {
552+
let submodule = line.split_once(' ').unwrap().1;
553+
let config = self.config.clone();
554+
s.spawn(move || {
555+
Self::update_existing_submodule(&config, submodule);
556+
});
557+
}
558+
});
554559
}
555560

556561
/// Updates the given submodule only if it's initialized already; nothing happens otherwise.
557-
pub fn update_existing_submodule(&self, submodule: &str) {
562+
pub fn update_existing_submodule(config: &Config, submodule: &str) {
558563
// Avoid running git when there isn't a git checkout.
559-
if !self.config.submodules() {
564+
if !config.submodules() {
560565
return;
561566
}
562567

563568
if GitInfo::new(false, Path::new(submodule)).is_managed_git_subrepository() {
564-
self.config.update_submodule(submodule);
569+
config.update_submodule(submodule);
565570
}
566571
}
567572

src/bootstrap/src/utils/channel.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::path::Path;
1010

1111
use super::helpers;
1212
use crate::Build;
13-
use crate::utils::helpers::{output, t};
13+
use crate::utils::helpers::{start_process, t};
1414

1515
#[derive(Clone, Default)]
1616
pub enum GitInfo {
@@ -56,7 +56,7 @@ impl GitInfo {
5656
}
5757

5858
// Ok, let's scrape some info
59-
let ver_date = output(
59+
let ver_date = start_process(
6060
helpers::git(Some(dir))
6161
.arg("log")
6262
.arg("-1")
@@ -65,14 +65,14 @@ impl GitInfo {
6565
.as_command_mut(),
6666
);
6767
let ver_hash =
68-
output(helpers::git(Some(dir)).arg("rev-parse").arg("HEAD").as_command_mut());
69-
let short_ver_hash = output(
68+
start_process(helpers::git(Some(dir)).arg("rev-parse").arg("HEAD").as_command_mut());
69+
let short_ver_hash = start_process(
7070
helpers::git(Some(dir)).arg("rev-parse").arg("--short=9").arg("HEAD").as_command_mut(),
7171
);
7272
GitInfo::Present(Some(Info {
73-
commit_date: ver_date.trim().to_string(),
74-
sha: ver_hash.trim().to_string(),
75-
short_sha: short_ver_hash.trim().to_string(),
73+
commit_date: ver_date().trim().to_string(),
74+
sha: ver_hash().trim().to_string(),
75+
short_sha: short_ver_hash().trim().to_string(),
7676
}))
7777
}
7878

src/bootstrap/src/utils/helpers.rs

+27
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,33 @@ pub fn output(cmd: &mut Command) -> String {
288288
String::from_utf8(output.stdout).unwrap()
289289
}
290290

291+
/// Spawn a process and return a closure that will wait for the process
292+
/// to finish and then return its output. This allows the spawned process
293+
/// to do work without immediately blocking bootstrap.
294+
#[track_caller]
295+
pub fn start_process(cmd: &mut Command) -> impl FnOnce() -> String {
296+
let child = match cmd.stderr(Stdio::inherit()).stdout(Stdio::piped()).spawn() {
297+
Ok(child) => child,
298+
Err(e) => fail(&format!("failed to execute command: {cmd:?}\nERROR: {e}")),
299+
};
300+
301+
let command = format!("{:?}", cmd);
302+
303+
move || {
304+
let output = child.wait_with_output().unwrap();
305+
306+
if !output.status.success() {
307+
panic!(
308+
"command did not execute successfully: {}\n\
309+
expected success, got: {}",
310+
command, output.status
311+
);
312+
}
313+
314+
String::from_utf8(output.stdout).unwrap()
315+
}
316+
}
317+
291318
/// Returns the last-modified time for `path`, or zero if it doesn't exist.
292319
pub fn mtime(path: &Path) -> SystemTime {
293320
fs::metadata(path).and_then(|f| f.modified()).unwrap_or(UNIX_EPOCH)

0 commit comments

Comments
 (0)