use bashkit::{Bash, FileSystem, InMemoryFs};
use criterion::{Criterion, Throughput, criterion_group, criterion_main};
use std::path::Path;
use std::sync::Arc;
use tokio::runtime::Runtime;
const INVOCATIONS: u64 = 500;
fn seed(rt: &Runtime) -> Arc<InMemoryFs> {
let fs = Arc::new(InMemoryFs::new());
rt.block_on(async {
let fs_dyn: Arc<dyn FileSystem> = fs.clone();
fs_dyn.mkdir(Path::new("/d"), true).await.expect("mkdir /d");
fs_dyn
.write_file(Path::new("/d/f"), b"abc\n")
.await
.expect("write /d/f");
});
fs
}
fn loop_script(body: &str) -> String {
format!("for ((i=0; i<{INVOCATIONS}; i++)); do {body}; done")
}
fn run(rt: &Runtime, fs: &Arc<InMemoryFs>, script: &str) {
rt.block_on(async {
let mut bash = Bash::builder().fs(fs.clone()).build();
let result = bash.exec(script).await.expect("exec failed");
std::hint::black_box(result);
});
}
fn bench_loop(
g: &mut criterion::BenchmarkGroup<'_, criterion::measurement::WallTime>,
rt: &Runtime,
fs: &Arc<InMemoryFs>,
name: &str,
body: &str,
) {
let script = loop_script(body);
g.bench_function(name, |b| b.iter(|| run(rt, fs, &script)));
}
fn bench_clap_vs_handrolled(c: &mut Criterion) {
let rt = Runtime::new().unwrap();
let fs = seed(&rt);
let mut g = c.benchmark_group("builtin_args/clap_vs_handrolled");
g.throughput(Throughput::Elements(INVOCATIONS));
bench_loop(&mut g, &rt, &fs, "colon_noop", ":");
bench_loop(&mut g, &rt, &fs, "echo_handrolled", "echo x > /dev/null");
bench_loop(
&mut g,
&rt,
&fs,
"printf_handrolled",
"printf x > /dev/null",
);
bench_loop(&mut g, &rt, &fs, "cat_clap", "cat /d/f > /dev/null");
bench_loop(&mut g, &rt, &fs, "ls_clap", "ls /d > /dev/null");
g.finish();
}
fn bench_arg_surface_size(c: &mut Criterion) {
let rt = Runtime::new().unwrap();
let fs = seed(&rt);
let mut g = c.benchmark_group("builtin_args/arg_surface_size");
g.throughput(Throughput::Elements(INVOCATIONS));
bench_loop(&mut g, &rt, &fs, "cat_12_args", "cat /d/f > /dev/null");
bench_loop(&mut g, &rt, &fs, "ls_60_args", "ls /d > /dev/null");
g.finish();
}
fn bench_flag_count(c: &mut Criterion) {
let rt = Runtime::new().unwrap();
let fs = seed(&rt);
let mut g = c.benchmark_group("builtin_args/flag_count");
g.throughput(Throughput::Elements(INVOCATIONS));
bench_loop(&mut g, &rt, &fs, "cat_0_flags", "cat /d/f > /dev/null");
bench_loop(&mut g, &rt, &fs, "cat_1_flag", "cat -n /d/f > /dev/null");
bench_loop(
&mut g,
&rt,
&fs,
"cat_3_flags",
"cat -n -E -T /d/f > /dev/null",
);
g.finish();
}
criterion_group!(
benches,
bench_clap_vs_handrolled,
bench_arg_surface_size,
bench_flag_count
);
criterion_main!(benches);