Skip to content

Commit ea7b516

Browse files
authored
Merge pull request #822 from xldenis/proof-stats
Add utility to compute proof time statistics
2 parents f5d0e47 + f483131 commit ea7b516

File tree

3 files changed

+136
-0
lines changed

3 files changed

+136
-0
lines changed

Cargo.lock

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

why3tests/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,14 @@ assert_cmd = "1.0"
1010
termcolor = "1.1"
1111
git2 = "0.14.4"
1212
clap = { version = "4.2", features = ["env", "derive"]}
13+
roxmltree = "0.18.0"
1314

1415
[[test]]
1516
test = false
1617
name = "why3"
1718
harness = false
19+
20+
[[test]]
21+
test = false
22+
name = "session_stats"
23+
harness = false

why3tests/tests/session_stats.rs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
use clap::Parser;
2+
use git2::{build::CheckoutBuilder, Repository};
3+
use roxmltree::Document;
4+
use std::{
5+
collections::HashMap,
6+
path::{Path, PathBuf},
7+
};
8+
9+
#[derive(Parser, Debug)]
10+
struct Args {
11+
/// Only check mlcfg files that differ from the provided source in the git history (useful for small PRs)
12+
#[clap(long = "diff-from")]
13+
diff_from: Option<String>,
14+
/// Only run tests which contain this string
15+
filter: Option<String>,
16+
}
17+
18+
fn main() {
19+
let args = Args::parse();
20+
21+
if let Some(from) = &args.diff_from {
22+
let old = collect_stats_at_rev(from, &args).unwrap();
23+
24+
let mut stats: Vec<_> = collect_stats(&args)
25+
.filter_map(|(f, new)| {
26+
let old = old.get(&f)?;
27+
if old.time > 0.0 {
28+
let d_t = new.time / old.time - 1.;
29+
Some((f, old, new, d_t))
30+
} else {
31+
None
32+
}
33+
})
34+
.collect();
35+
36+
let mean_d_t = stats.iter().map(|s| s.3).sum::<f64>() / (stats.len() as f64);
37+
38+
stats.sort_by_key(|(_, _, _, c)| (c * 10000.) as i64);
39+
40+
for (file, old, new, d_t) in stats {
41+
let file = file.strip_prefix("../creusot/tests").unwrap().parent().unwrap();
42+
let d_n = new.steps as f64 / old.steps as f64 - 1.;
43+
println!(
44+
"{}: t_old={:.2} t_new={:.2} ({:+.1}%) n_old={} n_new={} ({:+.1}%)",
45+
file.display(),
46+
old.time,
47+
new.time,
48+
d_t * 100.,
49+
old.steps,
50+
new.steps,
51+
d_n * 100.,
52+
);
53+
}
54+
55+
println!("mean change in proof time: {:+.1}%", mean_d_t * 100.)
56+
} else {
57+
for (file, stats) in collect_stats(&args) {
58+
println!(
59+
"{}: time={:.2} steps={}",
60+
file.parent().unwrap().display(),
61+
stats.time,
62+
stats.steps
63+
);
64+
}
65+
}
66+
}
67+
68+
fn collect_stats_at_rev(rev: &str, args: &Args) -> Result<HashMap<PathBuf, Stats>, git2::Error> {
69+
let repo = Repository::open("..")?;
70+
71+
let rev = repo.revparse_single(rev)?;
72+
repo.checkout_tree(&rev, None)?;
73+
let old_stats = collect_stats(&args).collect();
74+
75+
repo.checkout_head(Some(CheckoutBuilder::new().force()))?;
76+
Ok(old_stats)
77+
}
78+
79+
#[derive(Copy, Clone, Default, Debug)]
80+
struct Stats {
81+
time: f64,
82+
steps: u64,
83+
}
84+
85+
fn collect_stats(args: &Args) -> impl Iterator<Item = (PathBuf, Stats)> + '_ {
86+
let filter = args.filter.clone();
87+
glob::glob("../creusot/tests/**/why3session.xml")
88+
.unwrap()
89+
.flatten()
90+
.filter(move |f| match (&filter, f.to_str()) {
91+
(Some(filter), Some(f)) => f.contains(filter),
92+
_ => true,
93+
})
94+
.map(|f| {
95+
let stats = stats_for_session(&f);
96+
(f, stats)
97+
})
98+
}
99+
100+
fn stats_for_session(sess: &Path) -> Stats {
101+
let session = std::fs::read_to_string(sess).unwrap();
102+
let doc = Document::parse(&session).unwrap();
103+
doc.descendants()
104+
.filter(|n| {
105+
n.is_element()
106+
&& n.tag_name().name() == "result"
107+
&& n.attribute("status") == Some("valid")
108+
})
109+
.fold(Stats::default(), |mut stats, n| {
110+
stats.time += n.attribute("time").unwrap().parse::<f64>().unwrap();
111+
stats.steps += n.attribute("steps").unwrap().parse::<u64>().unwrap();
112+
stats
113+
})
114+
}

0 commit comments

Comments
 (0)