perf_construction/
perf_construction.rs1use std::hint::black_box;
14
15use nexus_rt::{IntoCallback, IntoHandler, Local, PipelineStart, Res, ResMut, WorldBuilder};
16
17const ITERATIONS: usize = 100_000;
22const WARMUP: usize = 10_000;
23const BATCH: u64 = 100;
24
25#[inline(always)]
26#[cfg(target_arch = "x86_64")]
27fn rdtsc_start() -> u64 {
28 unsafe {
29 core::arch::x86_64::_mm_lfence();
30 core::arch::x86_64::_rdtsc()
31 }
32}
33
34#[inline(always)]
35#[cfg(target_arch = "x86_64")]
36fn rdtsc_end() -> u64 {
37 unsafe {
38 let mut aux = 0u32;
39 let tsc = core::arch::x86_64::__rdtscp(&raw mut aux);
40 core::arch::x86_64::_mm_lfence();
41 tsc
42 }
43}
44
45fn percentile(sorted: &[u64], p: f64) -> u64 {
46 let idx = ((sorted.len() as f64) * p / 100.0) as usize;
47 sorted[idx.min(sorted.len() - 1)]
48}
49
50fn bench_batched<F: FnMut() -> u64>(name: &str, mut f: F) -> (u64, u64, u64) {
51 for _ in 0..WARMUP {
52 black_box(f());
53 }
54 let mut samples = Vec::with_capacity(ITERATIONS);
55 for _ in 0..ITERATIONS {
56 let start = rdtsc_start();
57 for _ in 0..BATCH {
58 black_box(f());
59 }
60 let end = rdtsc_end();
61 samples.push(end.wrapping_sub(start) / BATCH);
62 }
63 samples.sort_unstable();
64 let p50 = percentile(&samples, 50.0);
65 let p99 = percentile(&samples, 99.0);
66 let p999 = percentile(&samples, 99.9);
67 println!("{:<50} {:>8} {:>8} {:>8}", name, p50, p99, p999);
68 (p50, p99, p999)
69}
70
71fn print_header(title: &str) {
72 println!("=== {} ===\n", title);
73 println!(
74 "{:<50} {:>8} {:>8} {:>8}",
75 "Operation", "p50", "p99", "p999"
76 );
77 println!("{}", "-".repeat(78));
78}
79
80fn sys_1p(_a: Res<u64>, _e: ()) {}
85fn sys_2p(_a: Res<u64>, _b: ResMut<u32>, _e: ()) {}
86fn sys_4p(_a: Res<u64>, _b: ResMut<u32>, _c: Res<bool>, _d: Res<f64>, _e: ()) {}
87
88#[allow(clippy::too_many_arguments)]
89fn sys_8p(
90 _a: Res<u64>,
91 _b: ResMut<u32>,
92 _c: Res<bool>,
93 _d: Res<f64>,
94 _e2: Res<i64>,
95 _f: Res<i32>,
96 _g: Res<u8>,
97 _h: ResMut<u16>,
98 _e: (),
99) {
100}
101
102fn sys_local(_a: Local<u64>, _b: ResMut<u32>, _e: ()) {}
104
105fn sys_option(_a: Option<Res<u64>>, _b: ResMut<u32>, _e: ()) {}
107
108fn cb_2p(_ctx: &mut u64, _a: Res<u64>, _b: ResMut<u32>, _e: ()) {}
113fn cb_4p(_ctx: &mut u64, _a: Res<u64>, _b: ResMut<u32>, _c: Res<bool>, _d: Res<f64>, _e: ()) {}
114
115fn stage_2p(_a: Res<u64>, _b: ResMut<u32>, _x: u32) -> u32 {
120 0
121}
122fn stage_4p(_a: Res<u64>, _b: ResMut<u32>, _c: Res<bool>, _d: Res<f64>, _x: u32) -> u32 {
123 0
124}
125
126fn main() {
131 let mut wb = WorldBuilder::new();
133 wb.register::<u64>(0);
134 wb.register::<u32>(0);
135 wb.register::<bool>(false);
136 wb.register::<f64>(0.0);
137 wb.register::<i64>(0);
138 wb.register::<i32>(0);
139 wb.register::<u8>(0);
140 wb.register::<u16>(0);
141 let mut world = wb.build();
142 let r = world.registry_mut();
143
144 print_header("into_handler Construction (cycles)");
145
146 bench_batched("into_handler 1-param (Res<u64>)", || {
147 let _ = black_box(sys_1p.into_handler(r));
148 0
149 });
150
151 bench_batched("into_handler 2-param (Res + ResMut)", || {
152 let _ = black_box(sys_2p.into_handler(r));
153 0
154 });
155
156 bench_batched("into_handler 4-param", || {
157 let _ = black_box(sys_4p.into_handler(r));
158 0
159 });
160
161 bench_batched("into_handler 8-param", || {
162 let _ = black_box(sys_8p.into_handler(r));
163 0
164 });
165
166 bench_batched("into_handler 2-param (Local + ResMut)", || {
167 let _ = black_box(sys_local.into_handler(r));
168 0
169 });
170
171 bench_batched("into_handler 2-param (Option<Res> + ResMut)", || {
172 let _ = black_box(sys_option.into_handler(r));
173 0
174 });
175
176 println!();
177 print_header("into_callback Construction (cycles)");
178
179 bench_batched("into_callback 2-param (Res + ResMut)", || {
180 let _ = black_box(cb_2p.into_callback(0u64, r));
181 0
182 });
183
184 bench_batched("into_callback 4-param", || {
185 let _ = black_box(cb_4p.into_callback(0u64, r));
186 0
187 });
188
189 println!();
190 print_header("into_stage Construction (cycles)");
191
192 bench_batched(".stage() 2-param (Res + ResMut)", || {
193 let _ = black_box(PipelineStart::<u32>::new().stage(stage_2p, r));
194 0
195 });
196
197 bench_batched(".stage() 4-param", || {
198 let _ = black_box(PipelineStart::<u32>::new().stage(stage_4p, r));
199 0
200 });
201
202 println!();
203}