diff --git a/crates/hashx/bench/benches/hashx_cachegrind.rs b/crates/hashx/bench/benches/hashx_cachegrind.rs index ce0041da2..b352c4af2 100644 --- a/crates/hashx/bench/benches/hashx_cachegrind.rs +++ b/crates/hashx/bench/benches/hashx_cachegrind.rs @@ -11,68 +11,93 @@ use iai::black_box; -fn generate_interp_1000x() { - let mut builder = hashx::HashXBuilder::new(); - builder.runtime(hashx::RuntimeOption::InterpretOnly); - for s in 0_u32..1000_u32 { - let _ = black_box(builder.build(black_box(&s.to_be_bytes()))); +/// Bind Rust `HashBuilder` whose `RuntimeOption` is `$runtime_option` to `$builder` +// +// This and mk_c_equix are macros rather than a function because it avoids us +// having to import RuntimeOption::*, etc. or clutter the calls with a local alias. +macro_rules! mk_rust { { $builder:ident = $runtime_option:ident } => { + let mut $builder = hashx::HashXBuilder::new(); + $builder.runtime(hashx::RuntimeOption::$runtime_option); +} } + +/// Bind a C `HashX` whose `HashXType` is `$hashx_type` to `$ctx` +macro_rules! mk_c_equix { { $ctx:ident = $hashx_type:ident } => { + let mut $ctx = tor_c_equix::HashX::new(tor_c_equix::HashXType::$hashx_type); +} } + +/// Evaluate `$eval` binding `$loopvar` to `0..$max` +/// +/// Applies `black_box` to inputs and outputs. +/// +/// If `$loopvar_map` is supplied, it is a function applied to $loopvar +/// to convert from the integer loop variable, to whatever more useful type is needed. +// +// We expect $loopvar_map:ident even though a closure would be suitable, mostly because +// we expect the actual benchmark cases to want to use a short alias like `u32be` +macro_rules! bench_loop { { + $loopvar:ident, $max:expr $(, $loopvar_map:ident )? => $eval:expr +} => { + for $loopvar in 0..$max { + $( + let $loopvar = $loopvar_map($loopvar); + )? + let $loopvar = black_box($loopvar); + let _ = black_box($eval); } +} } + +/// Convenience alias to reduce clutter in actual benchmarks +const C_HASHX_OK: tor_c_equix::ffi::hashx_result = tor_c_equix::HashXResult::HASHX_OK; + +/// Helper, alias for `u32::to_be_bytes` +// +// Unfortunately, we can't just `use u32::to_be_bytes`. +fn u32be(s: u32) -> [u8; 4] { + s.to_be_bytes() +} + +fn generate_interp_1000x() { + mk_rust!(builder = InterpretOnly); + bench_loop! { s, 1000_u32, u32be => builder.build(&s) } } fn generate_interp_1000x_c() { - let mut ctx = tor_c_equix::HashX::new(tor_c_equix::HashXType::HASHX_TYPE_INTERPRETED); - for s in 0_u32..1000_u32 { - let _ = black_box(ctx.make(black_box(&s.to_be_bytes()))); - } + mk_c_equix!(ctx = HASHX_TYPE_INTERPRETED); + bench_loop! { s, 1000_u32, u32be => ctx.make(&s) } } fn generate_compiled_1000x() { - let mut builder = hashx::HashXBuilder::new(); - builder.runtime(hashx::RuntimeOption::CompileOnly); - for s in 0_u32..1000_u32 { - let _ = black_box(builder.build(black_box(&s.to_be_bytes()))); - } + mk_rust!(builder = CompileOnly); + bench_loop! { s, 1000_u32, u32be => builder.build(&s) } } fn generate_compiled_1000x_c() { - let mut ctx = tor_c_equix::HashX::new(tor_c_equix::HashXType::HASHX_TYPE_COMPILED); - for s in 0_u32..1000_u32 { - let _ = black_box(ctx.make(black_box(&s.to_be_bytes()))); - } + mk_c_equix!(ctx = HASHX_TYPE_COMPILED); + bench_loop! { s, 1000_u32, u32be => ctx.make(&s) } } fn interp_u64_hash_1000x() { - let mut builder = hashx::HashXBuilder::new(); - builder.runtime(hashx::RuntimeOption::InterpretOnly); + mk_rust!(builder = InterpretOnly); let hashx = builder.build(b"abc").unwrap(); - for i in 0_u64..1000_u64 { - let _ = black_box(hashx.hash_to_u64(black_box(i))); - } + bench_loop! { i, 1000_u64 => hashx.hash_to_u64(i) } } fn interp_8b_hash_1000x_c() { - let mut ctx = tor_c_equix::HashX::new(tor_c_equix::HashXType::HASHX_TYPE_INTERPRETED); - assert_eq!(ctx.make(b"abc"), tor_c_equix::HashXResult::HASHX_OK); - for i in 0_u64..1000_u64 { - let _ = black_box(ctx.exec(black_box(i))); - } + mk_c_equix!(ctx = HASHX_TYPE_INTERPRETED); + assert_eq!(ctx.make(b"abc"), C_HASHX_OK); + bench_loop! { i, 1000_u64 => ctx.exec(i) } } fn compiled_u64_hash_100000x() { - let mut builder = hashx::HashXBuilder::new(); - builder.runtime(hashx::RuntimeOption::CompileOnly); + mk_rust!(builder = CompileOnly); let hashx = builder.build(b"abc").unwrap(); - for i in 0_u64..100000_u64 { - let _ = black_box(hashx.hash_to_u64(black_box(i))); - } + bench_loop! { i, 100000_u64 => hashx.hash_to_u64(i) } } fn compiled_8b_hash_100000x_c() { - let mut ctx = tor_c_equix::HashX::new(tor_c_equix::HashXType::HASHX_TYPE_COMPILED); - assert_eq!(ctx.make(b"abc"), tor_c_equix::HashXResult::HASHX_OK); - for i in 0_u64..100000_u64 { - let _ = black_box(ctx.exec(black_box(i))); - } + mk_c_equix!(ctx = HASHX_TYPE_COMPILED); + assert_eq!(ctx.make(b"abc"), C_HASHX_OK); + bench_loop! { i, 100000_u64 => ctx.exec(i) } } iai::main!(