Skip to content

Commit 8d4283f

Browse files
authored
Rollup merge of rust-lang#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 42bc414 + 09e36ce commit 8d4283f

File tree

3 files changed

+61
-38
lines changed

3 files changed

+61
-38
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",

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

@@ -641,6 +642,18 @@ struct TestCollector {
641642
poisoned: bool,
642643
}
643644

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

661674
let cx = TestCollectorCx { config, cache, common_inputs_stamp, modified_tests };
662-
let mut collector =
663-
TestCollector { tests: vec![], found_path_stems: HashSet::new(), poisoned: false };
664-
665-
collect_tests_from_dir(&cx, &mut collector, &cx.config.src_test_suite_root, Utf8Path::new(""))
675+
let collector = collect_tests_from_dir(&cx, &cx.config.src_test_suite_root, Utf8Path::new(""))
666676
.unwrap_or_else(|reason| {
667677
panic!("Could not read tests from {}: {reason}", cx.config.src_test_suite_root)
668678
});
@@ -768,25 +778,25 @@ fn modified_tests(config: &Config, dir: &Utf8Path) -> Result<Vec<Utf8PathBuf>, S
768778
/// that will be handed over to libtest.
769779
fn collect_tests_from_dir(
770780
cx: &TestCollectorCx,
771-
collector: &mut TestCollector,
772781
dir: &Utf8Path,
773782
relative_dir_path: &Utf8Path,
774-
) -> io::Result<()> {
783+
) -> io::Result<TestCollector> {
775784
// Ignore directories that contain a file named `compiletest-ignore-dir`.
776785
if dir.join("compiletest-ignore-dir").exists() {
777-
return Ok(());
786+
return Ok(TestCollector::new());
778787
}
779788

780789
// For run-make tests, a "test file" is actually a directory that contains an `rmake.rs`.
781790
if cx.config.mode == Mode::RunMake {
791+
let mut collector = TestCollector::new();
782792
if dir.join("rmake.rs").exists() {
783793
let paths = TestPaths {
784794
file: dir.to_path_buf(),
785795
relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
786796
};
787-
make_test(cx, collector, &paths);
797+
make_test(cx, &mut collector, &paths);
788798
// This directory is a test, so don't try to find other tests inside it.
789-
return Ok(());
799+
return Ok(collector);
790800
}
791801
}
792802

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

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

0 commit comments

Comments
 (0)