Skip to content

Commit 01550d2

Browse files
committed
Sort tests at compile time, not at startup
Recently, another Miri user was trying to run `cargo miri test` on the crate `iced-x86` with `--features=code_asm,mvex`. This configuration has a startup time of ~18 minutes. That's ~18 minutes before any tests even start to run. The fact that this crate has over 26,000 tests and Miri is slow makes a lot of code which is otherwise a bit sloppy but fine into a huge runtime issue. Sorting the tests when the test harness is created instead of at startup time knocks just under 4 minutes out of those ~18 minutes. I have ways to remove most of the rest of the startup time, but this change requires coordinating changes of both the compiler and libtest, so I'm sending it separately.
1 parent c233ee6 commit 01550d2

File tree

3 files changed

+8
-38
lines changed

3 files changed

+8
-38
lines changed

test/src/console.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ pub fn list_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Res
147147
let mut ntest = 0;
148148
let mut nbench = 0;
149149

150-
for test in filter_tests(&opts, tests) {
150+
for test in filter_tests(&opts, tests).into_iter() {
151151
use crate::TestFn::*;
152152

153153
let TestDescAndFn { desc: TestDesc { name, .. }, testfn } = test;

test/src/lib.rs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -242,20 +242,20 @@ where
242242
let event = TestEvent::TeFiltered(filtered_descs, shuffle_seed);
243243
notify_about_test_event(event)?;
244244

245-
let (filtered_tests, filtered_benchs): (Vec<_>, _) = filtered_tests
245+
let (mut filtered_tests, filtered_benchs): (Vec<_>, _) = filtered_tests
246246
.into_iter()
247247
.enumerate()
248248
.map(|(i, e)| (TestId(i), e))
249249
.partition(|(_, e)| matches!(e.testfn, StaticTestFn(_) | DynTestFn(_)));
250250

251251
let concurrency = opts.test_threads.unwrap_or_else(get_concurrency);
252252

253-
let mut remaining = filtered_tests;
254253
if let Some(shuffle_seed) = shuffle_seed {
255-
shuffle_tests(shuffle_seed, &mut remaining);
256-
} else {
257-
remaining.reverse();
254+
shuffle_tests(shuffle_seed, &mut filtered_tests);
258255
}
256+
// Store the tests in a VecDeque so we can efficiently remove the first element to run the
257+
// tests in the order they were passed (unless shuffled).
258+
let mut remaining = VecDeque::from(filtered_tests);
259259
let mut pending = 0;
260260

261261
let (tx, rx) = channel::<CompletedTest>();
@@ -295,7 +295,7 @@ where
295295

296296
if concurrency == 1 {
297297
while !remaining.is_empty() {
298-
let (id, test) = remaining.pop().unwrap();
298+
let (id, test) = remaining.pop_front().unwrap();
299299
let event = TestEvent::TeWait(test.desc.clone());
300300
notify_about_test_event(event)?;
301301
let join_handle =
@@ -309,7 +309,7 @@ where
309309
} else {
310310
while pending > 0 || !remaining.is_empty() {
311311
while pending < concurrency && !remaining.is_empty() {
312-
let (id, test) = remaining.pop().unwrap();
312+
let (id, test) = remaining.pop_front().unwrap();
313313
let timeout = time::get_default_test_timeout();
314314
let desc = test.desc.clone();
315315

@@ -421,9 +421,6 @@ pub fn filter_tests(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> Vec<TestDescA
421421
RunIgnored::No => {}
422422
}
423423

424-
// Sort the tests alphabetically
425-
filtered.sort_by(|t1, t2| t1.desc.name.as_slice().cmp(t2.desc.name.as_slice()));
426-
427424
filtered
428425
}
429426

test/src/tests.rs

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -600,33 +600,6 @@ fn sample_tests() -> Vec<TestDescAndFn> {
600600
tests
601601
}
602602

603-
#[test]
604-
pub fn sort_tests() {
605-
let mut opts = TestOpts::new();
606-
opts.run_tests = true;
607-
608-
let tests = sample_tests();
609-
let filtered = filter_tests(&opts, tests);
610-
611-
let expected = vec![
612-
"isize::test_pow".to_string(),
613-
"isize::test_to_str".to_string(),
614-
"sha1::test".to_string(),
615-
"test::do_not_run_ignored_tests".to_string(),
616-
"test::filter_for_ignored_option".to_string(),
617-
"test::first_free_arg_should_be_a_filter".to_string(),
618-
"test::ignored_tests_result_in_ignored".to_string(),
619-
"test::parse_ignored_flag".to_string(),
620-
"test::parse_include_ignored_flag".to_string(),
621-
"test::run_include_ignored_option".to_string(),
622-
"test::sort_tests".to_string(),
623-
];
624-
625-
for (a, b) in expected.iter().zip(filtered) {
626-
assert_eq!(*a, b.desc.name.to_string());
627-
}
628-
}
629-
630603
#[test]
631604
pub fn shuffle_tests() {
632605
let mut opts = TestOpts::new();

0 commit comments

Comments
 (0)