Skip to content

Commit 21ceaa2

Browse files
authored
Introduce (experimental, unstable) parallel runner for proof harnesses (rust-lang#1726)
1 parent 5539c0d commit 21ceaa2

File tree

10 files changed

+188
-64
lines changed

10 files changed

+188
-64
lines changed

Cargo.lock

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,51 @@ dependencies = [
242242
"cfg-if",
243243
]
244244

245+
[[package]]
246+
name = "crossbeam-channel"
247+
version = "0.5.6"
248+
source = "registry+https://github.com/rust-lang/crates.io-index"
249+
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
250+
dependencies = [
251+
"cfg-if",
252+
"crossbeam-utils",
253+
]
254+
255+
[[package]]
256+
name = "crossbeam-deque"
257+
version = "0.8.2"
258+
source = "registry+https://github.com/rust-lang/crates.io-index"
259+
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
260+
dependencies = [
261+
"cfg-if",
262+
"crossbeam-epoch",
263+
"crossbeam-utils",
264+
]
265+
266+
[[package]]
267+
name = "crossbeam-epoch"
268+
version = "0.9.10"
269+
source = "registry+https://github.com/rust-lang/crates.io-index"
270+
checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1"
271+
dependencies = [
272+
"autocfg",
273+
"cfg-if",
274+
"crossbeam-utils",
275+
"memoffset",
276+
"once_cell",
277+
"scopeguard",
278+
]
279+
280+
[[package]]
281+
name = "crossbeam-utils"
282+
version = "0.8.11"
283+
source = "registry+https://github.com/rust-lang/crates.io-index"
284+
checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
285+
dependencies = [
286+
"cfg-if",
287+
"once_cell",
288+
]
289+
245290
[[package]]
246291
name = "either"
247292
version = "1.8.0"
@@ -392,6 +437,7 @@ dependencies = [
392437
"kani_metadata",
393438
"once_cell",
394439
"pathdiff",
440+
"rayon",
395441
"regex",
396442
"rustc-demangle",
397443
"serde",
@@ -490,6 +536,15 @@ version = "2.5.0"
490536
source = "registry+https://github.com/rust-lang/crates.io-index"
491537
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
492538

539+
[[package]]
540+
name = "memoffset"
541+
version = "0.6.5"
542+
source = "registry+https://github.com/rust-lang/crates.io-index"
543+
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
544+
dependencies = [
545+
"autocfg",
546+
]
547+
493548
[[package]]
494549
name = "num"
495550
version = "0.4.0"
@@ -566,6 +621,16 @@ dependencies = [
566621
"autocfg",
567622
]
568623

624+
[[package]]
625+
name = "num_cpus"
626+
version = "1.13.1"
627+
source = "registry+https://github.com/rust-lang/crates.io-index"
628+
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
629+
dependencies = [
630+
"hermit-abi",
631+
"libc",
632+
]
633+
569634
[[package]]
570635
name = "object"
571636
version = "0.29.0"
@@ -688,6 +753,30 @@ dependencies = [
688753
"proc-macro2",
689754
]
690755

756+
[[package]]
757+
name = "rayon"
758+
version = "1.5.3"
759+
source = "registry+https://github.com/rust-lang/crates.io-index"
760+
checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
761+
dependencies = [
762+
"autocfg",
763+
"crossbeam-deque",
764+
"either",
765+
"rayon-core",
766+
]
767+
768+
[[package]]
769+
name = "rayon-core"
770+
version = "1.9.3"
771+
source = "registry+https://github.com/rust-lang/crates.io-index"
772+
checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
773+
dependencies = [
774+
"crossbeam-channel",
775+
"crossbeam-deque",
776+
"crossbeam-utils",
777+
"num_cpus",
778+
]
779+
691780
[[package]]
692781
name = "redox_syscall"
693782
version = "0.2.16"

kani-driver/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ toml = "0.5"
3030
regex = "1.6"
3131
rustc-demangle = "0.1.21"
3232
pathdiff = "0.2.1"
33+
rayon = "1.5.3"
3334

3435
# A good set of suggested dependencies can be found in rustup:
3536
# https://github.com/rust-lang/rustup/blob/master/Cargo.toml

kani-driver/src/args.rs

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@ pub struct KaniArgs {
148148
// consumes everything
149149
pub cbmc_args: Vec<OsString>,
150150

151+
/// Number of parallel jobs, defaults to 1
152+
#[structopt(short, long, hidden = true, requires("enable-unstable"))]
153+
pub jobs: Option<Option<usize>>,
154+
151155
// Hide option till https://github.com/model-checking/kani/issues/697 is
152156
// fixed.
153157
/// Use abstractions for the standard library.
@@ -226,6 +230,15 @@ impl KaniArgs {
226230
Some(DEFAULT_OBJECT_BITS)
227231
}
228232
}
233+
234+
/// Computes how many threads should be used to verify harnesses.
235+
pub fn jobs(&self) -> Option<usize> {
236+
match self.jobs {
237+
None => Some(1), // no argument, default 1
238+
Some(None) => None, // -j
239+
Some(Some(x)) => Some(x), // -j=x
240+
}
241+
}
229242
}
230243

231244
arg_enum! {
@@ -365,7 +378,6 @@ impl KaniArgs {
365378
String::new()
366379
};
367380

368-
// `tracing` is not a dependency of `kani-driver`, so we println! here instead.
369381
println!(
370382
"Using concrete playback with --randomize-layout.\n\
371383
The produced tests will have to be played with the same rustc arguments:\n\
@@ -377,29 +389,47 @@ impl KaniArgs {
377389
// TODO: these conflicting flags reflect what's necessary to pass current tests unmodified.
378390
// We should consider improving the error messages slightly in a later pull request.
379391
if natives_unwind && extra_unwind {
380-
Err(Error::with_description(
392+
return Err(Error::with_description(
381393
"Conflicting flags: unwind flags provided to kani and in --cbmc-args.",
382394
ErrorKind::ArgumentConflict,
383-
))
384-
} else if self.cbmc_args.contains(&OsString::from("--function")) {
385-
Err(Error::with_description(
395+
));
396+
}
397+
if self.cbmc_args.contains(&OsString::from("--function")) {
398+
return Err(Error::with_description(
386399
"Invalid flag: --function should be provided to Kani directly, not via --cbmc-args.",
387400
ErrorKind::ArgumentConflict,
388-
))
389-
} else if self.quiet && self.concrete_playback == Some(ConcretePlaybackMode::Print) {
390-
Err(Error::with_description(
401+
));
402+
}
403+
if self.quiet && self.concrete_playback == Some(ConcretePlaybackMode::Print) {
404+
return Err(Error::with_description(
391405
"Conflicting options: --concrete-playback=print and --quiet.",
392406
ErrorKind::ArgumentConflict,
393-
))
394-
} else if self.concrete_playback.is_some() && self.output_format == OutputFormat::Old {
395-
Err(Error::with_description(
407+
));
408+
}
409+
if self.concrete_playback.is_some() && self.output_format == OutputFormat::Old {
410+
return Err(Error::with_description(
396411
"Conflicting options: --concrete-playback isn't compatible with \
397412
--output-format=old.",
398413
ErrorKind::ArgumentConflict,
399-
))
400-
} else {
401-
Ok(())
414+
));
415+
}
416+
if self.concrete_playback.is_some() && self.jobs() != Some(1) {
417+
// Concrete playback currently embeds a lot of assumptions about the order in which harnesses get called.
418+
return Err(Error::with_description(
419+
"Conflicting options: --concrete-playback isn't compatible with --jobs.",
420+
ErrorKind::ArgumentConflict,
421+
));
402422
}
423+
if self.jobs.is_some() && self.output_format != OutputFormat::Terse {
424+
// More verbose output formats make it hard to interpret output right now when run in parallel.
425+
// This can be removed when we change up how results are printed.
426+
return Err(Error::with_description(
427+
"Conflicting options: --jobs requires `--output-format=terse`",
428+
ErrorKind::ArgumentConflict,
429+
));
430+
}
431+
432+
Ok(())
403433
}
404434
}
405435

kani-driver/src/call_cbmc_viewer.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,7 @@ impl KaniSession {
2323
let coverage_filename = alter_extension(file, "coverage.xml");
2424
let property_filename = alter_extension(file, "property.xml");
2525

26-
{
27-
let mut temps = self.temporaries.borrow_mut();
28-
temps.push(results_filename.clone());
29-
temps.push(coverage_filename.clone());
30-
temps.push(property_filename.clone());
31-
}
26+
self.record_temporary_files(&[&results_filename, &coverage_filename, &property_filename]);
3227

3328
self.cbmc_variant(file, &["--xml-ui", "--trace"], &results_filename, harness_metadata)?;
3429
self.cbmc_variant(

kani-driver/src/call_goto_instrument.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,7 @@ impl KaniSession {
6464
pub fn apply_vtable_restrictions(&self, file: &Path, source: &Path) -> Result<()> {
6565
let linked_restrictions = alter_extension(file, "linked-restrictions.json");
6666

67-
{
68-
let mut temps = self.temporaries.borrow_mut();
69-
temps.push(linked_restrictions.clone());
70-
}
67+
self.record_temporary_files(&[&linked_restrictions]);
7168

7269
collect_and_link_function_pointer_restrictions(source, &linked_restrictions)?;
7370

kani-driver/src/call_single_file.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,14 @@ impl KaniSession {
3333
let restrictions_filename = alter_extension(file, "restrictions.json");
3434
let rlib_filename = guess_rlib_name(file);
3535

36-
{
37-
let mut temps = self.temporaries.borrow_mut();
38-
temps.push(rlib_filename);
39-
temps.push(output_filename.clone());
40-
temps.push(typemap_filename);
41-
temps.push(metadata_filename.clone());
42-
if self.args.restrict_vtable() {
43-
temps.push(restrictions_filename.clone());
44-
}
36+
self.record_temporary_files(&[
37+
&rlib_filename,
38+
&output_filename,
39+
&typemap_filename,
40+
&metadata_filename,
41+
]);
42+
if self.args.restrict_vtable() {
43+
self.record_temporary_files(&[&restrictions_filename]);
4544
}
4645

4746
let mut kani_args = self.kani_specific_flags();

kani-driver/src/call_symtab.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,7 @@ impl KaniSession {
1212
pub fn symbol_table_to_gotoc(&self, file: &Path) -> Result<PathBuf> {
1313
let output_filename = file.with_extension("out");
1414

15-
{
16-
let mut temps = self.temporaries.borrow_mut();
17-
temps.push(output_filename.clone());
18-
}
15+
self.record_temporary_files(&[&output_filename]);
1916

2017
let args = vec![
2118
file.to_owned().into_os_string(),

kani-driver/src/harness_runner.rs

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
use anyhow::Result;
55
use kani_metadata::HarnessMetadata;
6+
use rayon::prelude::*;
67
use std::path::{Path, PathBuf};
78

89
use crate::call_cbmc::VerificationStatus;
@@ -45,26 +46,37 @@ impl<'sess> HarnessRunner<'sess> {
4546
) -> Result<Vec<HarnessResult<'a>>> {
4647
let sorted_harnesses = crate::metadata::sort_harnesses_by_loc(harnesses);
4748

48-
let mut results = vec![];
49-
50-
for harness in &sorted_harnesses {
51-
let harness_filename = harness.pretty_name.replace("::", "-");
52-
let report_dir = self.report_base.join(format!("report-{}", harness_filename));
53-
let specialized_obj = specialized_harness_name(self.linked_obj, &harness_filename);
54-
if !self.retain_specialized_harnesses {
55-
let mut temps = self.sess.temporaries.borrow_mut();
56-
temps.push(specialized_obj.to_owned());
49+
let pool = {
50+
let mut builder = rayon::ThreadPoolBuilder::new();
51+
if let Some(x) = self.sess.args.jobs() {
52+
builder = builder.num_threads(x);
5753
}
58-
self.sess.run_goto_instrument(
59-
self.linked_obj,
60-
&specialized_obj,
61-
self.symtabs,
62-
&harness.mangled_name,
63-
)?;
64-
65-
let result = self.sess.check_harness(&specialized_obj, &report_dir, harness)?;
66-
results.push(HarnessResult { harness, result });
67-
}
54+
builder.build()?
55+
};
56+
57+
let results = pool.install(|| -> Result<Vec<HarnessResult<'a>>> {
58+
sorted_harnesses
59+
.par_iter()
60+
.map(|harness| -> Result<HarnessResult<'a>> {
61+
let harness_filename = harness.pretty_name.replace("::", "-");
62+
let report_dir = self.report_base.join(format!("report-{}", harness_filename));
63+
let specialized_obj =
64+
specialized_harness_name(self.linked_obj, &harness_filename);
65+
if !self.retain_specialized_harnesses {
66+
self.sess.record_temporary_files(&[&specialized_obj]);
67+
}
68+
self.sess.run_goto_instrument(
69+
self.linked_obj,
70+
&specialized_obj,
71+
self.symtabs,
72+
&harness.mangled_name,
73+
)?;
74+
75+
let result = self.sess.check_harness(&specialized_obj, &report_dir, harness)?;
76+
Ok(HarnessResult { harness, result })
77+
})
78+
.collect::<Result<Vec<_>>>()
79+
})?;
6880

6981
Ok(results)
7082
}

kani-driver/src/main.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,7 @@ fn standalone_main() -> Result<()> {
8888
}
8989

9090
let linked_obj = util::alter_extension(&args.input, "out");
91-
{
92-
let mut temps = ctx.temporaries.borrow_mut();
93-
temps.push(linked_obj.to_owned());
94-
}
91+
ctx.record_temporary_files(&[&linked_obj]);
9592
ctx.link_goto_binary(&[goto_obj], &linked_obj)?;
9693
if let Some(restriction) = outputs.restrictions {
9794
ctx.apply_vtable_restrictions(&linked_obj, &restriction)?;

0 commit comments

Comments
 (0)