bench_pertaro/
bench_pertaro.rs1use pertaro::{
17 xorshift64, PertaroArena121, PertaroExpert243, Sentinel, Word256, ARENA_NODES, FNV1A64_BASIS,
18 FRONTIER_START, ROOT_SENTINEL,
19};
20use std::hint::black_box;
21use std::time::Instant;
22
23fn seeded_expert() -> PertaroExpert243 {
24 let mut raw = Word256 {
25 hash: FNV1A64_BASIS,
26 parent: 0,
27 trits: [17u8; 244],
28 };
29 raw.trits[0] = Sentinel::NullNode as u8;
30 raw.trits[ROOT_SENTINEL] = Sentinel::NullNode as u8;
31 PertaroExpert243(raw)
32}
33
34#[inline(always)]
37fn touch(node: &mut PertaroExpert243, atom: u8) -> u64 {
38 let s = node.reground_seed(atom, Sentinel::BusIndirect);
39 node.0.hash = s;
40 s
41}
42
43fn chase_pass(arena: &mut [PertaroExpert243], passes: usize) -> f64 {
47 let n = arena.len();
48 let mut i = 0usize;
49 let mut acc = 0u64;
50 let t = Instant::now();
51 for _ in 0..passes {
52 for _ in 0..n {
53 let s = touch(&mut arena[i], (acc & 0xFF) as u8 % 243);
54 acc ^= s;
55 i = (s % n as u64) as usize;
56 }
57 }
58 black_box(acc);
59 t.elapsed().as_nanos() as f64 / (passes * n) as f64
60}
61
62fn random_pass(arena: &mut [PertaroExpert243], passes: usize) -> f64 {
63 let n = arena.len();
64 let mut rng = FNV1A64_BASIS;
65 let mut acc = 0u64;
66 let t = Instant::now();
67 for _ in 0..passes {
68 for _ in 0..n {
69 rng = xorshift64(rng);
70 let i = (rng % n as u64) as usize;
71 acc ^= touch(&mut arena[i], (rng >> 32) as u8 % 243);
72 }
73 }
74 black_box(acc);
75 t.elapsed().as_nanos() as f64 / (passes * n) as f64
76}
77
78fn main() {
79 const ITERS: usize = 100_000;
80 let expert = seeded_expert();
81
82 let t0 = Instant::now();
84 for _ in 0..ITERS {
85 let mut e = black_box(expert);
86 black_box(e.prune_frontier());
87 }
88 let ns_single = t0.elapsed().as_nanos() as f64 / ITERS as f64;
89
90 let mut arena = Box::new(PertaroArena121 {
92 nodes: [expert; ARENA_NODES],
93 });
94 let arena_iters = 10_000usize;
95 let mut seed = FNV1A64_BASIS;
96 let t1 = Instant::now();
97 for step in 0..arena_iters {
98 arena.expand_from_root(seed, (step % 243) as u8, Sentinel::BusIndirect);
99 let mut winner = 0u8;
100 for node in arena.frontier_mut() {
101 if let Some(w) = node.prune_frontier().first_winner() {
102 winner = w;
103 }
104 }
105 seed = arena.nodes[ARENA_NODES - 1].reground_seed(winner, Sentinel::BusIndirect);
106 black_box(seed);
107 }
108 let ns_l1_node = t1.elapsed().as_nanos() as f64 / (arena_iters * ARENA_NODES) as f64;
109
110 const TIER6_NODES: usize = 1093;
112 let mut bank = vec![expert; TIER6_NODES];
113 let spill_iters = 2_000usize;
114 let t2 = Instant::now();
115 for step in 0..spill_iters {
116 for (i, node) in bank.iter_mut().enumerate() {
117 node.0.hash = seed.wrapping_add(i as u64);
118 black_box(node.prune_frontier());
119 }
120 seed = seed.wrapping_add(step as u64);
121 }
122 let ns_spill_node = t2.elapsed().as_nanos() as f64 / (spill_iters * TIER6_NODES) as f64;
123
124 let sizes = [121usize, 364, 1093, 3280, 9841, 29524];
126 let mut cliff = Vec::new();
127 for &n in &sizes {
128 let mut a = vec![expert; n];
129 random_pass(&mut a, 2); let passes = (2_000_000 / n).max(20);
131 let independent = random_pass(&mut a, passes);
132 let dependent = chase_pass(&mut a, passes);
133 cliff.push((n, n * 256, independent, dependent));
134 }
135
136 println!("==============================================================");
137 println!(
138 " PERTARO LATENCY (Word256 = 256 B, Arena121 = {} B)",
139 ARENA_NODES * 256
140 );
141 println!("==============================================================");
142 println!("1. one word, materialize + reduct + prune : {ns_single:>8.1} ns");
143 println!("2. tier-4 arena, expand + 81 prunes : {ns_l1_node:>8.1} ns/node");
144 println!("3. tier-6 bank, 1093 prunes (control) : {ns_spill_node:>8.1} ns/node");
145 println!("4. light touch, by working set (tier-4 frontier starts at node {FRONTIER_START}):");
146 println!(" nodes bytes independent ns dependent ns");
147 for (n, bytes, ind, dep) in cliff {
148 println!(" {n:>7} {bytes:>10} {ind:>14.2} {dep:>14.2}");
149 }
150 println!(" independent = random index from an RNG (loads overlap)");
151 println!(" dependent = next index from the word just loaded (loads serialize)");
152 println!("==============================================================");
153}