aboutsummaryrefslogtreecommitdiff
path: root/dev/bench/dispatch/build.rs
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-08-20 03:38:20 +0800
committer魏曹先生 <1992414357@qq.com>2026-08-20 03:42:06 +0800
commit0ee5d865a1eaf8b409c2b310ab16ee5f42a0f14a (patch)
treecc409abf22f6e9d5fd5313da1f5e89488a5b2713 /dev/bench/dispatch/build.rs
parent5678e0ba37ce2c5c8161f0d48505e7993967e30c (diff)
feat: add dispatch benchmark subproject
Diffstat (limited to 'dev/bench/dispatch/build.rs')
-rw-r--r--dev/bench/dispatch/build.rs153
1 files changed, 153 insertions, 0 deletions
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<String> {
+ 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::<Vec<_>>()
+ .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<String> = 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<String>], misses: &[Vec<String>]) -> (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");
+}