From 0ee5d865a1eaf8b409c2b310ab16ee5f42a0f14a Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Thu, 20 Aug 2026 03:38:20 +0800 Subject: feat: add dispatch benchmark subproject --- dev/bench/dispatch/build.rs | 153 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 dev/bench/dispatch/build.rs (limited to 'dev/bench/dispatch/build.rs') diff --git a/dev/bench/dispatch/build.rs b/dev/bench/dispatch/build.rs new file mode 100644 index 0000000..09eefaa --- /dev/null +++ b/dev/bench/dispatch/build.rs @@ -0,0 +1,153 @@ +//! 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"); +} -- cgit