diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 20e423ab..fc6602cc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,6 +86,7 @@ jobs: env: MY_ENV_VAR: "YES" with: + mode: instrumentation run: cargo codspeed run token: ${{ secrets.CODSPEED_TOKEN }} @@ -113,11 +114,13 @@ jobs: cargo codspeed build -p ${{ matrix.package }} - name: Run the benchmarks - uses: CodSpeedHQ/action@main + uses: CodSpeedHQ/action@chore/runner-branch env: MY_ENV_VAR: "YES" CODSPEED_PERF_ENABLED: true with: + runner-branch: cod-1355-perf-flamegraph-doesnt-match-benchmark + mode: walltime run: cargo codspeed run token: ${{ secrets.CODSPEED_TOKEN }} diff --git a/.gitmodules b/.gitmodules index 03307501..e143b34e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,6 @@ [submodule "crates/divan_compat/examples/src/the_algorithms/Rust"] path = crates/divan_compat/examples/src/the_algorithms/Rust url = https://github.com/TheAlgorithms/Rust.git +[submodule "crates/codspeed/instrument-hooks"] + path = crates/codspeed/instrument-hooks + url = https://github.com/CodSpeedHQ/instrument-hooks diff --git a/Cargo.lock b/Cargo.lock index e35de18c..aebf819c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -395,6 +395,26 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools 0.10.5", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -502,9 +522,22 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.6" +version = "1.2.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] [[package]] name = "cfg-if" @@ -545,6 +578,17 @@ dependencies = [ "half", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.5.17" @@ -592,6 +636,7 @@ version = "3.0.5" dependencies = [ "anyhow", "bincode", + "bindgen", "colored", "glob", "libc", @@ -894,6 +939,12 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "find-msvc-tools" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" + [[package]] name = "float-cmp" version = "0.9.0" @@ -1184,6 +1235,16 @@ version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + [[package]] name = "libmimalloc-sys" version = "0.1.39" @@ -1230,6 +1291,12 @@ dependencies = [ "libmimalloc-sys", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.4" @@ -1251,6 +1318,16 @@ dependencies = [ "libc", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -1433,6 +1510,16 @@ dependencies = [ "termtree", ] +[[package]] +name = "prettyplease" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro-crate" version = "3.2.0" @@ -1569,6 +1656,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustix" version = "0.37.27" @@ -1652,6 +1745,12 @@ dependencies = [ "serde", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -2017,6 +2116,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/crates/codspeed/Cargo.toml b/crates/codspeed/Cargo.toml index 70232db8..424b6dda 100644 --- a/crates/codspeed/Cargo.toml +++ b/crates/codspeed/Cargo.toml @@ -35,3 +35,6 @@ harness = false [dev-dependencies] tempfile = { workspace = true } + +[build-dependencies] +bindgen = "0.72.1" diff --git a/crates/codspeed/build.rs b/crates/codspeed/build.rs new file mode 100644 index 00000000..f2852264 --- /dev/null +++ b/crates/codspeed/build.rs @@ -0,0 +1,17 @@ +use std::{env, path::PathBuf}; + +fn main() { + let bindings = bindgen::Builder::default() + .header("instrument-hooks/includes/core.h") + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .generate() + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/crates/codspeed/instrument-hooks b/crates/codspeed/instrument-hooks new file mode 160000 index 00000000..d094ae4d --- /dev/null +++ b/crates/codspeed/instrument-hooks @@ -0,0 +1 @@ +Subproject commit d094ae4d6aa0be555a1016cfbbea74e34b0ed555 diff --git a/crates/codspeed/src/fifo.rs b/crates/codspeed/src/fifo.rs deleted file mode 100644 index 347cb5e9..00000000 --- a/crates/codspeed/src/fifo.rs +++ /dev/null @@ -1,229 +0,0 @@ -pub use super::shared::*; -use anyhow::bail; -use nix::libc::O_NONBLOCK; -use nix::sys::stat; -use nix::unistd::{self, unlink}; -use std::fs::{File, OpenOptions}; -use std::io::Read; -use std::os::unix::fs::OpenOptionsExt; -use std::path::{Path, PathBuf}; - -pub struct BenchGuard { - ctl_fifo: FifoIpc, - ack_fifo: FifoIpc, -} - -impl BenchGuard { - pub fn new(ctl_fifo: &str, ack_fifo: &str) -> anyhow::Result { - let mut instance = Self { - ctl_fifo: FifoIpc::connect(ctl_fifo)?.with_writer()?, - ack_fifo: FifoIpc::connect(ack_fifo)?.with_reader()?, - }; - - instance.send_cmd(Command::SetIntegration { - name: "codspeed-rust".into(), - version: env!("CARGO_PKG_VERSION").into(), - })?; // FIXME: Just send it once - instance.send_cmd(Command::StartBenchmark)?; - - Ok(instance) - } - - pub fn new_with_runner_fifo() -> anyhow::Result { - Self::new(RUNNER_CTL_FIFO, RUNNER_ACK_FIFO) - } - - fn send_cmd(&mut self, cmd: Command) -> anyhow::Result<()> { - self.ctl_fifo.send_cmd(cmd)?; - self.ack_fifo.wait_for_ack(); - - Ok(()) - } -} - -impl Drop for BenchGuard { - fn drop(&mut self) { - self.send_cmd(Command::StopBenchmark) - .expect("Failed to send stop command"); - } -} - -pub fn send_cmd(cmd: Command) -> anyhow::Result<()> { - let mut writer = FifoIpc::connect(RUNNER_CTL_FIFO)?.with_writer()?; - writer.send_cmd(cmd).unwrap(); - - let mut reader = FifoIpc::connect(RUNNER_ACK_FIFO)?.with_reader()?; - reader.wait_for_ack(); - - Ok(()) -} - -pub struct FifoIpc { - path: PathBuf, - reader: Option, - writer: Option, -} - -impl FifoIpc { - /// Creates a new FIFO at the specified path and connects to it. - /// - /// ```rust - /// use codspeed::fifo::{FifoIpc, Command}; - /// - /// // Create the reader before the writer (required!): - /// let mut read_fifo = FifoIpc::create("/tmp/doctest.fifo").unwrap().with_reader().unwrap(); - /// - /// // Connect to the FIFO and send a command - /// let mut fifo = FifoIpc::connect("/tmp/doctest.fifo").unwrap().with_writer().unwrap(); - /// fifo.send_cmd(Command::StartBenchmark).unwrap(); - /// - /// // Receive the command in the reader - /// let cmd = read_fifo.recv_cmd().unwrap(); - /// assert_eq!(cmd, Command::StartBenchmark); - /// ``` - pub fn create>(path: P) -> anyhow::Result { - // Remove the previous FIFO (if it exists) - let _ = unlink(path.as_ref()); - - // Create the FIFO with RWX permissions for the owner - unistd::mkfifo(path.as_ref(), stat::Mode::S_IRWXU)?; - - Self::connect(path.as_ref()) - } - - pub fn connect>(path: P) -> anyhow::Result { - let path = path.into(); - - if !path.exists() { - bail!("FIFO does not exist: {}", path.display()); - } - - Ok(Self { - path, - reader: None, - writer: None, - }) - } - - pub fn with_reader(mut self) -> anyhow::Result { - self.reader = Some( - OpenOptions::new() - .write(true) - .read(true) - .custom_flags(O_NONBLOCK) - .open(&self.path)?, - ); - Ok(self) - } - - /// WARNING: Writer must be opened _AFTER_ the reader. - pub fn with_writer(mut self) -> anyhow::Result { - self.writer = Some( - OpenOptions::new() - .write(true) - .custom_flags(O_NONBLOCK) - .open(&self.path)?, - ); - Ok(self) - } - - pub fn recv_cmd(&mut self) -> anyhow::Result { - // First read the length (u32 = 4 bytes) - let mut len_buffer = [0u8; 4]; - self.read_exact(&mut len_buffer)?; - let message_len = u32::from_le_bytes(len_buffer) as usize; - - // Try to read the message - let mut buffer = vec![0u8; message_len]; - loop { - if self.read_exact(&mut buffer).is_ok() { - break; - } - } - - let decoded = bincode::deserialize(&buffer)?; - Ok(decoded) - } - - pub fn send_cmd(&mut self, cmd: Command) -> anyhow::Result<()> { - use std::io::Write; - - let encoded = bincode::serialize(&cmd)?; - self.write_all(&(encoded.len() as u32).to_le_bytes())?; - self.write_all(&encoded)?; - Ok(()) - } - - pub fn wait_for_ack(&mut self) { - loop { - if let Ok(Command::Ack) = self.recv_cmd() { - break; - } - } - } -} - -impl std::io::Write for FifoIpc { - fn write(&mut self, buf: &[u8]) -> std::io::Result { - if let Some(writer) = self.writer.as_mut() { - writer.write(buf) - } else { - Err(std::io::Error::new( - std::io::ErrorKind::NotConnected, - "Writer not initialized", - )) - } - } - - fn flush(&mut self) -> std::io::Result<()> { - Ok(()) - } -} - -impl std::io::Read for FifoIpc { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - if let Some(reader) = self.reader.as_mut() { - reader.read(buf) - } else { - Err(std::io::Error::new( - std::io::ErrorKind::NotConnected, - "Reader not initialized", - )) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::io::Write; - - #[test] - fn test_ipc_write_read() { - let mut fifo = FifoIpc::create("/tmp/test1.fifo") - .unwrap() - .with_reader() - .unwrap() - .with_writer() - .unwrap(); - - fifo.write_all(b"Hello").unwrap(); - let mut buffer = [0; 5]; - fifo.read_exact(&mut buffer).unwrap(); - assert_eq!(&buffer, b"Hello"); - } - - #[test] - fn test_ipc_send_recv_cmd() { - let mut fifo = FifoIpc::create("/tmp/test2.fifo") - .unwrap() - .with_reader() - .unwrap() - .with_writer() - .unwrap(); - - fifo.send_cmd(Command::StartBenchmark).unwrap(); - let cmd = fifo.recv_cmd().unwrap(); - assert_eq!(cmd, Command::StartBenchmark); - } -} diff --git a/crates/codspeed/src/instrument_hooks/ffi.rs b/crates/codspeed/src/instrument_hooks/ffi.rs new file mode 100644 index 00000000..428a22c5 --- /dev/null +++ b/crates/codspeed/src/instrument_hooks/ffi.rs @@ -0,0 +1,5 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); \ No newline at end of file diff --git a/crates/codspeed/src/instrument_hooks/mod.rs b/crates/codspeed/src/instrument_hooks/mod.rs new file mode 100644 index 00000000..a01dd58b --- /dev/null +++ b/crates/codspeed/src/instrument_hooks/mod.rs @@ -0,0 +1,116 @@ +use std::ffi::CString; +use std::sync::OnceLock; + +mod ffi; + +pub struct InstrumentHooks(*mut ffi::InstrumentHooks); + +unsafe impl Send for InstrumentHooks {} +unsafe impl Sync for InstrumentHooks {} + +impl InstrumentHooks { + #[inline(always)] + pub fn new() -> Option { + let ptr = unsafe { ffi::instrument_hooks_init() }; + if ptr.is_null() { + None + } else { + Some(InstrumentHooks(ptr)) + } + } + + /// Returns a singleton instance of `InstrumentHooks`. + #[inline(always)] + pub fn instance() -> &'static Self { + static INSTANCE: OnceLock = OnceLock::new(); + INSTANCE.get_or_init(|| { + let instance = InstrumentHooks::new().expect("Failed to initialize InstrumentHooks"); + instance + .set_integration("codspeed-rust", env!("CARGO_PKG_VERSION")) + .expect("Failed to set integration"); + instance + }) + } + + #[inline(always)] + pub fn is_instrumented(&self) -> bool { + unsafe { ffi::instrument_hooks_is_instrumented(self.0) } + } + + #[inline(always)] + pub fn start_benchmark(&self) -> Result<(), i8> { + let result = unsafe { ffi::instrument_hooks_start_benchmark(self.0) }; + if result == 0 { + Ok(()) + } else { + Err(result) + } + } + + #[inline(always)] + pub fn stop_benchmark(&self) -> Result<(), i8> { + let result = unsafe { ffi::instrument_hooks_stop_benchmark(self.0) }; + if result == 0 { + Ok(()) + } else { + Err(result) + } + } + + #[inline(always)] + pub fn set_executed_benchmark(&self, pid: i32, uri: &str) -> Result<(), i8> { + let c_uri = CString::new(uri).map_err(|_| -1i8)?; + let result = + unsafe { ffi::instrument_hooks_set_executed_benchmark(self.0, pid, c_uri.as_ptr()) }; + if result == 0 { + Ok(()) + } else { + Err(result) + } + } + + #[inline(always)] + pub fn set_integration(&self, name: &str, version: &str) -> Result<(), i8> { + let c_name = CString::new(name).map_err(|_| -1i8)?; + let c_version = CString::new(version).map_err(|_| -1i8)?; + let result = unsafe { + ffi::instrument_hooks_set_integration(self.0, c_name.as_ptr(), c_version.as_ptr()) + }; + if result == 0 { + Ok(()) + } else { + Err(result) + } + } + + pub fn start_callgrind_instrumentation() -> Result<(), i8> { + let result = unsafe { ffi::callgrind_start_instrumentation() }; + if result == 0 { + Ok(()) + } else { + Err(result) + } + } + + pub fn stop_callgrind_instrumentation() -> Result<(), i8> { + let result = unsafe { ffi::callgrind_stop_instrumentation() }; + if result == 0 { + Ok(()) + } else { + Err(result) + } + } + + pub fn set_feature(feature: ffi::instrument_hooks_feature_t, enabled: bool) { + unsafe { ffi::instrument_hooks_set_feature(feature, enabled) }; + } +} + +impl Drop for InstrumentHooks { + fn drop(&mut self) { + if !self.0.is_null() { + unsafe { ffi::instrument_hooks_deinit(self.0) }; + self.0 = std::ptr::null_mut(); + } + } +} diff --git a/crates/codspeed/src/lib.rs b/crates/codspeed/src/lib.rs index aa7a51d1..6e7f94ff 100644 --- a/crates/codspeed/src/lib.rs +++ b/crates/codspeed/src/lib.rs @@ -1,11 +1,10 @@ pub mod codspeed; #[cfg(unix)] -pub mod fifo; +pub mod instrument_hooks; mod macros; mod measurement; mod request; -mod shared; pub mod utils; pub mod walltime_results; diff --git a/crates/codspeed/src/shared.rs b/crates/codspeed/src/shared.rs deleted file mode 100644 index 1c8481c0..00000000 --- a/crates/codspeed/src/shared.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! WARNING: Has to be in sync with `runner`. - -#[cfg(unix)] -pub const RUNNER_CTL_FIFO: &str = "/tmp/runner.ctl.fifo"; -#[cfg(unix)] -pub const RUNNER_ACK_FIFO: &str = "/tmp/runner.ack.fifo"; - -#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq)] -pub enum Command { - CurrentBenchmark { pid: u32, uri: String }, - StartBenchmark, - StopBenchmark, - Ack, - PingPerf, - SetIntegration { name: String, version: String }, - Err, -} diff --git a/crates/divan_compat/Cargo.toml b/crates/divan_compat/Cargo.toml index dfcb3a00..8217b970 100644 --- a/crates/divan_compat/Cargo.toml +++ b/crates/divan_compat/Cargo.toml @@ -29,3 +29,11 @@ harness = false [[bench]] name = "sleep_benches" harness = false + +[[bench]] +name = "drop_example" +harness = false + +[[bench]] +name = "thread_example" +harness = false diff --git a/crates/divan_compat/benches/drop_example.rs b/crates/divan_compat/benches/drop_example.rs new file mode 100644 index 00000000..5543796e --- /dev/null +++ b/crates/divan_compat/benches/drop_example.rs @@ -0,0 +1,45 @@ +use codspeed_divan_compat::Bencher; + +struct LargeInput { + data: Vec, +} + +impl Drop for LargeInput { + #[inline(never)] + fn drop(&mut self) { + // Simulate a large drop by performing some computation + let sum: u8 = self.data.iter().copied().sum(); + std::hint::black_box(sum); // Prevent optimization + } +} + +impl LargeInput { + fn new() -> Self { + Self { + data: vec![42; 1024 * 1024 /* 1MiB */], + } + } + + fn process(&self) -> u64 { + // Simulate some work on the data + std::thread::sleep(std::time::Duration::from_millis(50)); + self.data.iter().map(|&x| x as u64).sum() + } +} + +#[codspeed_divan_compat::bench] +fn bench_large_input(bencher: Bencher) { + bencher + .with_inputs(LargeInput::new) + .bench_values(|input| input.process()); +} + +#[codspeed_divan_compat::bench] +fn bench_large_input_local(bencher: Bencher) { + let input = LargeInput::new(); + bencher.bench_local(|| input.process()); +} + +fn main() { + codspeed_divan_compat::main(); +} diff --git a/crates/divan_compat/benches/sleep_benches.rs b/crates/divan_compat/benches/sleep_benches.rs index 7a8931ec..963545b8 100644 --- a/crates/divan_compat/benches/sleep_benches.rs +++ b/crates/divan_compat/benches/sleep_benches.rs @@ -1,25 +1,25 @@ -#[divan::bench] +#[codspeed_divan_compat::bench] fn sleep_1ms() { std::thread::sleep(std::time::Duration::from_millis(1)); } -#[divan::bench] +#[codspeed_divan_compat::bench] fn sleep_10ms() { std::thread::sleep(std::time::Duration::from_millis(10)); } -#[divan::bench] +#[codspeed_divan_compat::bench] fn sleep_50ms() { std::thread::sleep(std::time::Duration::from_millis(50)); } -#[divan::bench] +#[codspeed_divan_compat::bench] fn sleep_100ms() { std::thread::sleep(std::time::Duration::from_millis(100)); } // Tests COD-1044, do not modify the sample size or count! -#[divan::bench(sample_size = 3, sample_count = 6)] +#[codspeed_divan_compat::bench(sample_size = 3, sample_count = 6)] fn sleep_100ms_with_custom_sample() { std::thread::sleep(std::time::Duration::from_millis(100)); } diff --git a/crates/divan_compat/benches/thread_example.rs b/crates/divan_compat/benches/thread_example.rs new file mode 100644 index 00000000..1695033b --- /dev/null +++ b/crates/divan_compat/benches/thread_example.rs @@ -0,0 +1,33 @@ +fn fibo(n: u64) -> u64 { + if n <= 1 { + n + } else { + fibo(n - 1) + fibo(n - 2) + } +} + +#[codspeed_divan_compat::bench(args = [30, 31, 32])] +fn fib_in_thread(n: usize) { + let handle = std::thread::spawn(move || codspeed_divan_compat::black_box(fibo(n as u64))); + handle.join().unwrap(); +} + +#[codspeed_divan_compat::bench(args = [30, 31, 32])] +fn fib_in_thread_bench(bencher: codspeed_divan_compat::Bencher, n: usize) { + bencher.bench(|| { + let handle = std::thread::spawn(move || codspeed_divan_compat::black_box(fibo(n as u64))); + handle.join().unwrap() + }) +} + +#[codspeed_divan_compat::bench(args = [30, 31, 32])] +fn fib_in_thread_bench_local(bencher: codspeed_divan_compat::Bencher, n: usize) { + bencher.bench_local(|| { + let handle = std::thread::spawn(move || codspeed_divan_compat::black_box(fibo(n as u64))); + handle.join().unwrap() + }) +} + +fn main() { + codspeed_divan_compat::main(); +}