//! Generates `generated.rs` — the dispatch benchmark matrix //! (command length × command count × command-name type × {dispatch_linear, //! dispatch_tree, dispatch_phf}). //! //! The heavy lifting (dispatch generation) happens inside the `bench_cell!` //! proc macro from `mingling_macros` (enabled via its `bench_support` //! feature); this script only writes the invocations and the glue. use std::fmt::Write as _; use std::path::Path; // Full length axis (4/8/16/32) × count {128, 256} × {single, multi, // nested4, nested10}, keeping the agreed max-256 / depth-10 bound so the // whole matrix compiles in a few minutes. const LENGTHS: [usize; 4] = [4, 8, 16, 32]; const COUNTS: [usize; 2] = [128, 256]; // (label, kind, chain_depth) — chain_depth only applies to the "nested" kind. const TYPES: [(&str, &str, usize); 4] = [ ("single", "single", 0), ("multi", "multi", 0), ("nested4", "nested", 4), ("nested10", "nested", 10), ]; const STRATEGIES: [&str; 4] = [ "dispatch_linear", "dispatch_tree", "dispatch_phf", "dispatch_auto", ]; fn zero_pad(i: usize, w: usize) -> String { let s = i.to_string(); let mut out = String::with_capacity(w); for _ in 0..w.saturating_sub(s.len()) { out.push('0'); } out.push_str(&s); out } fn make_names(len: usize, count: usize, kind: &str, depth: usize) -> Vec { let digits = |n: usize| n.to_string().len(); let mut names = Vec::new(); match kind { // Single-word commands with no shared prefixes. "single" => { let w = len.saturating_sub(1).max(3).max(digits(count)); for i in 0..count { names.push(format!("c{}", zero_pad(i, w))); } } // Two-word commands sharing the first word ("cmd ..."), git-style. "multi" => { let w = len.saturating_sub(4).max(3).max(digits(count)); for i in 0..count { names.push(format!("cmd {}", zero_pad(i, w))); } } // Nested-prefix chains of `depth`: base, base w1, base w1 w2, ... // The base word length follows the `len` axis. "nested" => { let groups = count.div_ceil(depth); let bw = len.saturating_sub(1).max(3).max(digits(groups)); 'outer: for g in 0..groups { let base = format!("n{}", zero_pad(g, bw)); for d in 0..depth { let mut name = base.clone(); for k in 1..=d { let _ = write!(name, " w{k}"); } names.push(name); if names.len() == count { break 'outer; } } } } _ => unreachable!(), } names } fn entries_tokens(names: &[String]) -> String { names .iter() .enumerate() .map(|(i, n)| format!("\"{n}\" => D{i}")) .collect::>() .join(", ") } fn main() { let out_dir = std::env::var("OUT_DIR").expect("OUT_DIR is set by cargo"); let mut src = String::from("// @generated by mingling_bench/build.rs — do not edit.\n\n"); let mut meta = String::new(); let mut run_cell = String::new(); let mut run_cell_pick = String::new(); let mut meta_entries: Vec = Vec::new(); let mut cell_id = 0usize; for &len in &LENGTHS { for &count in &COUNTS { for &(ty, kind, depth) in &TYPES { let label = format!("len{len:03}_cnt{count:04}_ty_{ty}"); let names = make_names(len, count, kind, depth); for &strat in &STRATEGIES { let _ = writeln!( src, "mingling_macros::bench_cell!({strat}, cell_{cell_id:03}, [{}]);\n", entries_tokens(&names) ); meta_entries.push(format!( "(\"{label}\", \"{strat}\", cell_{cell_id:03}::NAMES)," )); let _ = writeln!( run_cell, " {cell_id} => cell_{cell_id:03}::measure(hits, misses)," ); let _ = writeln!( run_cell_pick, " {cell_id} => cell_{cell_id:03}::STRATEGY," ); cell_id += 1; } } } } let _ = writeln!( meta, "pub static CELL_META: &[(&str, &str, &'static [&'static str])] = &[\n{}\n];", meta_entries.join("\n") ); let _ = writeln!( src, "{meta}\n\ pub fn run_cell(id: usize, hits: &[Vec], misses: &[Vec]) -> (f64, f64) {{\n\ match id {{\n\ {run_cell} _ => panic!(\"unknown cell {{id}}\"),\n\ }}\n\ }}\n\ pub fn run_cell_pick(id: usize) -> &'static str {{\n\ match id {{\n\ {run_cell_pick} _ => \"\",\n\ }}\n\ }}" ); std::fs::write(Path::new(&out_dir).join("generated.rs"), src) .expect("failed to write generated.rs"); }