use molt::check_args;
use molt::molt_ok;
use molt::ContextID;
use molt::Interp;
use molt::MoltInt;
use molt::MoltResult;
use molt::Value;
use std::env;
use std::fs;
use std::path::PathBuf;
pub fn benchmark(interp: &mut Interp, args: &[String]) {
if args.is_empty() {
eprintln!("Missing benchmark script.");
write_usage();
return;
}
let mut output_csv = false;
let mut iter = args[1..].iter();
loop {
let opt = iter.next();
if opt.is_none() {
break;
}
let opt = opt.unwrap();
match opt.as_ref() {
"-csv" => {
output_csv = true;
}
_ => {
eprintln!("Unknown option: \"{}\"", opt);
write_usage();
return;
}
}
}
let path = PathBuf::from(&args[0]);
let context_id = interp.save_context(Context::new());
interp.add_command("ident", cmd_ident);
interp.add_context_command("measure", measure_cmd, context_id);
interp.add_command("ok", cmd_ok);
if let Err(exception) = interp.eval(include_str!("bench.tcl")) {
panic!(
"Error in benchmark Tcl library: {}",
exception.value().as_str()
);
}
match fs::read_to_string(&args[0]) {
Ok(script) => {
if let Some(parent) = path.parent() {
let _ = env::set_current_dir(parent);
}
match interp.eval(&script) {
Ok(_) => (),
Err(exception) => {
eprintln!("{}", exception.value());
std::process::exit(1);
}
}
}
Err(e) => println!("{}", e),
}
let ctx = interp.context::<Context>(context_id);
if output_csv {
write_csv(ctx);
} else {
write_formatted_text(ctx);
}
}
fn write_csv(ctx: &Context) {
println!("\"benchmark\",\"description\",\"nanos\",\"norm\"");
let baseline = ctx.baseline();
for record in &ctx.measurements {
println!(
"\"{}\",\"{}\",{},{}",
strip_quotes(&record.name),
strip_quotes(&record.description),
record.nanos,
record.nanos as f64 / (baseline as f64),
);
}
}
fn strip_quotes(string: &str) -> String {
let out: String = string
.chars()
.map(|ch| if ch == '\"' { '\'' } else { ch })
.collect();
out
}
fn write_formatted_text(ctx: &Context) {
write_version();
println!();
println!("{:>8} {:>8} -- Benchmark", "Nanos", "Norm");
let baseline = ctx.baseline();
for record in &ctx.measurements {
println!(
"{:>8} {:>8.2} -- {} {}",
record.nanos,
record.nanos as f64 / (baseline as f64),
record.name,
record.description
);
}
}
fn write_version() {
println!("Molt {} -- Benchmark", env!("CARGO_PKG_VERSION"));
}
fn write_usage() {
write_version();
println!();
println!("Usage: molt bench filename.tcl [-csv]");
}
struct Context {
baseline: Option<MoltInt>,
measurements: Vec<Measurement>,
}
impl Context {
fn new() -> Self {
Self {
baseline: None,
measurements: Vec::new(),
}
}
fn baseline(&self) -> MoltInt {
self.baseline.unwrap_or(1)
}
}
struct Measurement {
name: String,
description: String,
nanos: MoltInt,
}
fn measure_cmd(interp: &mut Interp, context_id: ContextID, argv: &[Value]) -> MoltResult {
molt::check_args(1, argv, 4, 4, "name description nanos")?;
let name = argv[1].to_string();
let description = argv[2].to_string();
let nanos = argv[3].as_int()?;
let ctx = interp.context::<Context>(context_id);
if ctx.baseline.is_none() {
ctx.baseline = Some(nanos);
}
let record = Measurement {
name,
description,
nanos,
};
ctx.measurements.push(record);
molt_ok!()
}
fn cmd_ident(_interp: &mut Interp, _: ContextID, argv: &[Value]) -> MoltResult {
check_args(1, argv, 2, 2, "value")?;
molt_ok!(argv[1].clone())
}
fn cmd_ok(_interp: &mut Interp, _: ContextID, _argv: &[Value]) -> MoltResult {
molt_ok!()
}