Skip to content

Commit d3508a8

Browse files
committed
Auto merge of #140177 - tmandry:compiletest-par, r=jieyouxu
[compiletest] Parallelize test discovery Certain filesystems are slow to service individual read requests, but can service many in parallel. This change brings down the time to run a single cached test on one of those filesystems from 40s to about 8s.
2 parents 555e1d0 + f673c9b commit d3508a8

File tree

4 files changed

+62
-40
lines changed

4 files changed

+62
-40
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,7 @@ dependencies = [
737737
"libc",
738738
"miow",
739739
"miropt-test-tools",
740+
"rayon",
740741
"regex",
741742
"rustfix",
742743
"semver",

compiler/rustc_mir_transform/src/coverage/mod.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
pub(super) mod query;
2-
31
mod counters;
42
mod graph;
53
mod mappings;
4+
pub(super) mod query;
65
mod spans;
76
#[cfg(test)]
87
mod tests;

src/tools/compiletest/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ glob = "0.3.0"
1818
home = "0.5.5"
1919
indexmap = "2.0.0"
2020
miropt-test-tools = { path = "../miropt-test-tools" }
21+
rayon = "1.10.0"
2122
regex = "1.0"
2223
rustfix = "0.8.1"
2324
semver = { version = "1.0.23", features = ["serde"] }

src/tools/compiletest/src/lib.rs

+59-38
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use std::{env, fs, vec};
3333
use build_helper::git::{get_git_modified_files, get_git_untracked_files};
3434
use camino::{Utf8Path, Utf8PathBuf};
3535
use getopts::Options;
36+
use rayon::iter::{ParallelBridge, ParallelIterator};
3637
use tracing::*;
3738
use walkdir::WalkDir;
3839

@@ -638,6 +639,18 @@ struct TestCollector {
638639
poisoned: bool,
639640
}
640641

642+
impl TestCollector {
643+
fn new() -> Self {
644+
TestCollector { tests: vec![], found_path_stems: HashSet::new(), poisoned: false }
645+
}
646+
647+
fn merge(&mut self, mut other: Self) {
648+
self.tests.append(&mut other.tests);
649+
self.found_path_stems.extend(other.found_path_stems);
650+
self.poisoned |= other.poisoned;
651+
}
652+
}
653+
641654
/// Creates test structures for every test/revision in the test suite directory.
642655
///
643656
/// This always inspects _all_ test files in the suite (e.g. all 17k+ ui tests),
@@ -656,10 +669,7 @@ pub(crate) fn collect_and_make_tests(config: Arc<Config>) -> Vec<CollectedTest>
656669
let cache = HeadersCache::load(&config);
657670

658671
let cx = TestCollectorCx { config, cache, common_inputs_stamp, modified_tests };
659-
let mut collector =
660-
TestCollector { tests: vec![], found_path_stems: HashSet::new(), poisoned: false };
661-
662-
collect_tests_from_dir(&cx, &mut collector, &cx.config.src_test_suite_root, Utf8Path::new(""))
672+
let collector = collect_tests_from_dir(&cx, &cx.config.src_test_suite_root, Utf8Path::new(""))
663673
.unwrap_or_else(|reason| {
664674
panic!("Could not read tests from {}: {reason}", cx.config.src_test_suite_root)
665675
});
@@ -765,25 +775,25 @@ fn modified_tests(config: &Config, dir: &Utf8Path) -> Result<Vec<Utf8PathBuf>, S
765775
/// that will be handed over to libtest.
766776
fn collect_tests_from_dir(
767777
cx: &TestCollectorCx,
768-
collector: &mut TestCollector,
769778
dir: &Utf8Path,
770779
relative_dir_path: &Utf8Path,
771-
) -> io::Result<()> {
780+
) -> io::Result<TestCollector> {
772781
// Ignore directories that contain a file named `compiletest-ignore-dir`.
773782
if dir.join("compiletest-ignore-dir").exists() {
774-
return Ok(());
783+
return Ok(TestCollector::new());
775784
}
776785

777786
// For run-make tests, a "test file" is actually a directory that contains an `rmake.rs`.
778787
if cx.config.mode == Mode::RunMake {
788+
let mut collector = TestCollector::new();
779789
if dir.join("rmake.rs").exists() {
780790
let paths = TestPaths {
781791
file: dir.to_path_buf(),
782792
relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
783793
};
784-
make_test(cx, collector, &paths);
794+
make_test(cx, &mut collector, &paths);
785795
// This directory is a test, so don't try to find other tests inside it.
786-
return Ok(());
796+
return Ok(collector);
787797
}
788798
}
789799

@@ -800,36 +810,47 @@ fn collect_tests_from_dir(
800810
// subdirectories we find, except for `auxiliary` directories.
801811
// FIXME: this walks full tests tree, even if we have something to ignore
802812
// use walkdir/ignore like in tidy?
803-
for file in fs::read_dir(dir.as_std_path())? {
804-
let file = file?;
805-
let file_path = Utf8PathBuf::try_from(file.path()).unwrap();
806-
let file_name = file_path.file_name().unwrap();
807-
808-
if is_test(file_name)
809-
&& (!cx.config.only_modified || cx.modified_tests.contains(&file_path))
810-
{
811-
// We found a test file, so create the corresponding libtest structures.
812-
debug!(%file_path, "found test file");
813-
814-
// Record the stem of the test file, to check for overlaps later.
815-
let rel_test_path = relative_dir_path.join(file_path.file_stem().unwrap());
816-
collector.found_path_stems.insert(rel_test_path);
817-
818-
let paths =
819-
TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() };
820-
make_test(cx, collector, &paths);
821-
} else if file_path.is_dir() {
822-
// Recurse to find more tests in a subdirectory.
823-
let relative_file_path = relative_dir_path.join(file_name);
824-
if file_name != "auxiliary" {
825-
debug!(%file_path, "found directory");
826-
collect_tests_from_dir(cx, collector, &file_path, &relative_file_path)?;
813+
fs::read_dir(dir.as_std_path())?
814+
.par_bridge()
815+
.map(|file| {
816+
let mut collector = TestCollector::new();
817+
let file = file?;
818+
let file_path = Utf8PathBuf::try_from(file.path()).unwrap();
819+
let file_name = file_path.file_name().unwrap();
820+
821+
if is_test(file_name)
822+
&& (!cx.config.only_modified || cx.modified_tests.contains(&file_path))
823+
{
824+
// We found a test file, so create the corresponding libtest structures.
825+
debug!(%file_path, "found test file");
826+
827+
// Record the stem of the test file, to check for overlaps later.
828+
let rel_test_path = relative_dir_path.join(file_path.file_stem().unwrap());
829+
collector.found_path_stems.insert(rel_test_path);
830+
831+
let paths =
832+
TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() };
833+
make_test(cx, &mut collector, &paths);
834+
} else if file_path.is_dir() {
835+
// Recurse to find more tests in a subdirectory.
836+
let relative_file_path = relative_dir_path.join(file_name);
837+
if file_name != "auxiliary" {
838+
debug!(%file_path, "found directory");
839+
collector.merge(collect_tests_from_dir(cx, &file_path, &relative_file_path)?);
840+
}
841+
} else {
842+
debug!(%file_path, "found other file/directory");
827843
}
828-
} else {
829-
debug!(%file_path, "found other file/directory");
830-
}
831-
}
832-
Ok(())
844+
Ok(collector)
845+
})
846+
.reduce(
847+
|| Ok(TestCollector::new()),
848+
|a, b| {
849+
let mut a = a?;
850+
a.merge(b?);
851+
Ok(a)
852+
},
853+
)
833854
}
834855

835856
/// Returns true if `file_name` looks like a proper test file name.

0 commit comments

Comments
 (0)