use std::fmt::Write as _;
use std::hint::black_box;
use std::io::Write;
use std::time::Instant;
struct Document(std::path::PathBuf);
impl Document {
fn new(name: &str, entries: usize) -> Self {
let mut s = String::from("<?xml version=\"1.0\"?>\n<catalogue>\n");
for i in 0..entries {
let _ = write!(
s,
" <book id=\"b{i}\" lang=\"en\">\n \
<title>Title {i}</title>\n \
<pages>{i}</pages>\n </book>\n"
);
}
s.push_str("</catalogue>\n");
let path = std::env::temp_dir().join(name);
let mut f = std::fs::File::create(&path).expect("a temporary file");
f.write_all(s.as_bytes()).expect("write");
Self(path)
}
fn path(&self) -> String {
self.0.display().to_string()
}
}
impl Drop for Document {
fn drop(&mut self) {
let _ = std::fs::remove_file(&self.0);
}
}
fn fastest(rounds: usize, mut f: impl FnMut()) -> f64 {
let mut best = f64::INFINITY;
for _ in 0..rounds {
let start = Instant::now();
f();
best = best.min(start.elapsed().as_secs_f64());
}
best
}
fn main() {
let small = Document::new("oxml-bench-small.xml", 10);
let large = Document::new("oxml-bench-large.xml", 2_000);
let cases: Vec<(String, Vec<String>)> = vec![
("--help".to_owned(), vec!["--help".to_owned()]),
(
"check (10 entries)".to_owned(),
vec!["check".to_owned(), small.path()],
),
(
"check (2,000 entries)".to_owned(),
vec!["check".to_owned(), large.path()],
),
(
"stats (2,000 entries)".to_owned(),
vec!["stats".to_owned(), large.path()],
),
(
"query //title (2,000)".to_owned(),
vec!["query".to_owned(), "//title".to_owned(), large.path()],
),
(
"query -t //title (2,000)".to_owned(),
vec![
"query".to_owned(),
"-t".to_owned(),
"//title".to_owned(),
large.path(),
],
),
(
"query count() (2,000)".to_owned(),
vec![
"query".to_owned(),
"count(//title)".to_owned(),
large.path(),
],
),
];
println!("per invocation, fastest of 20 rounds, spawn excluded\n");
println!("{:<28} {:>12}", "command", "time");
for (name, argv) in &cases {
let reps = if argv.len() < 2 { 200 } else { 1 };
let seconds = fastest(20, || {
for _ in 0..reps {
let mut out = Vec::with_capacity(1 << 16);
let mut err = Vec::new();
let _ = black_box(oxml_cli::run(
black_box(argv),
&mut out,
&mut err,
));
let _ = black_box(out);
}
}) / f64::from(u16::try_from(reps).unwrap_or(1));
println!("{name:<28} {:>9.1} us", seconds * 1e6);
}
let source = std::fs::read_to_string(large.path()).expect("readable");
let argv = vec!["check".to_owned(), large.path()];
let (mut whole, mut parse) = (f64::INFINITY, f64::INFINITY);
for _ in 0..40 {
let a = Instant::now();
let (mut out, mut err) = (Vec::new(), Vec::new());
let _ = black_box(oxml_cli::run(&argv, &mut out, &mut err));
whole = whole.min(a.elapsed().as_secs_f64());
let b = Instant::now();
let _ = black_box(oxml::parse(black_box(&source)));
parse = parse.min(b.elapsed().as_secs_f64());
}
println!(
"\n`check` against the parse inside it, paired:\n \
{:.0} us vs {:.0} us -- {:+.1}% for argument handling and file \
I/O",
whole * 1e6,
parse * 1e6,
(whole / parse - 1.0) * 100.0
);
}