about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorJubilee <workingjubilee@gmail.com>2024-10-21 20:32:02 -0700
committerGitHub <noreply@github.com>2024-10-21 20:32:02 -0700
commitaba227b3a92a894d571b428796ad86f411716214 (patch)
tree5811ab74b2f3d16825c4425a872d7fd9c207a831 /src
parent1b24c6fc1442a00f6edb8f4381ea64c2ba017843 (diff)
parenta269e4da72ed1e2dc71eca2871acab6b9f1423c5 (diff)
downloadrust-aba227b3a92a894d571b428796ad86f411716214.tar.gz
rust-aba227b3a92a894d571b428796ad86f411716214.zip
Rollup merge of #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.
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/src/lib.rs19
-rw-r--r--src/bootstrap/src/utils/channel.rs14
-rw-r--r--src/bootstrap/src/utils/helpers.rs27
3 files changed, 46 insertions, 14 deletions
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index 3924a6d714e..a9db0377a50 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -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);
         }
     }
 
diff --git a/src/bootstrap/src/utils/channel.rs b/src/bootstrap/src/utils/channel.rs
index c361abb9c9e..4a9ecc7a4f8 100644
--- a/src/bootstrap/src/utils/channel.rs
+++ b/src/bootstrap/src/utils/channel.rs
@@ -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 {
@@ -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")
@@ -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(),
         }))
     }
 
diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs
index 2519ace92b9..7162007e9f0 100644
--- a/src/bootstrap/src/utils/helpers.rs
+++ b/src/bootstrap/src/utils/helpers.rs
@@ -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)