use std::time::{Duration, Instant};
use tabnas::Tabnas;
const SRC: &str = "a,b,c\n1,2,3";
const N: usize = 120;
const WANT: u32 = 4;
fn make_csv_parser() -> Tabnas {
tabnas_csv::make()
}
fn time_parses(mut parse_once: impl FnMut()) -> Duration {
let started = Instant::now();
for _ in 0..N {
parse_once();
}
started.elapsed()
}
#[test]
fn reusing_one_instance_is_far_cheaper_than_rebuilding_per_parse() {
for _ in 0..20 {
make_csv_parser().parse(SRC).expect("parses");
}
let shared = make_csv_parser();
for _ in 0..50 {
shared.parse(SRC).expect("parses");
}
tabnas_csv::parse(SRC).expect("parses");
let rebuild = time_parses(|| {
make_csv_parser().parse(SRC).expect("rebuild parse");
});
let reuse = time_parses(|| {
shared.parse(SRC).expect("reuse parse");
});
let convenience = time_parses(|| {
tabnas_csv::parse(SRC).expect("shared parse");
});
assert!(
rebuild > Duration::ZERO,
"{N} rebuild-per-call parses measured as zero elapsed time: this is \
testing the clock, not the code"
);
let reuse_ratio = rebuild.as_secs_f64() / reuse.as_secs_f64().max(f64::EPSILON);
let convenience_ratio = rebuild.as_secs_f64() / convenience.as_secs_f64().max(f64::EPSILON);
eprintln!(
"perf reuses-instance: rebuild-per-call={rebuild:?} reuse={reuse:?} \
ratio={reuse_ratio:.2}x | tabnas_csv::parse={convenience:?} \
ratio={convenience_ratio:.2}x"
);
assert!(
rebuild >= WANT * reuse,
"reusing one csv instance is not meaningfully faster than rebuilding per parse: \
{N} rebuild-per-call parses took {rebuild:?} vs {reuse:?} reusing one instance \
(ratio {reuse_ratio:.1}x, want >={WANT}x). Building the CSV grammar should dominate; \
reuse one tabnas_csv::make() instance (or tabnas_csv::parse) instead of rebuilding \
per parse."
);
assert!(
rebuild >= WANT * convenience,
"tabnas_csv::parse is rebuilding its instance: {rebuild:?} rebuild-per-call vs \
{convenience:?} through tabnas_csv::parse (ratio {convenience_ratio:.1}x, \
want >={WANT}x). `parse` must build the default instance once, behind its OnceLock."
);
}