diff --git a/constants.env b/constants.env index 8b291bc3d..b57c76d35 100644 --- a/constants.env +++ b/constants.env @@ -35,5 +35,6 @@ CARGO_UDEPS_VERSION=0.1.60 CARGO_WORKSPACES_VERSION=0.4.2 # Other tools +GUNGRAUN_RUNNER_VERSION=0.19.0 JUST_VERSION=1.46.0 SCCACHE_VERSION=v0.13.0 diff --git a/crates/multitude/Cargo.toml b/crates/multitude/Cargo.toml index 6d4351c74..9346e9379 100644 --- a/crates/multitude/Cargo.toml +++ b/crates/multitude/Cargo.toml @@ -63,6 +63,12 @@ zerocopy = { workspace = true, optional = true } [target.'cfg(loom)'.dependencies] loom = { workspace = true } +# Workspace dep declares `default-features = false`; we re-enable `default` here +# (which pulls in the `benchmark` feature with `library_benchmark`, +# `library_benchmark_group!`, and `gungraun::main!`) only on Linux. +[target.'cfg(target_os = "linux")'.dev-dependencies] +gungraun = { workspace = true, features = ["default"] } + [dev-dependencies] allocator-api2 = { workspace = true, features = ["alloc"] } bolero = { workspace = true, features = ["std"] } @@ -71,7 +77,6 @@ bolero = { workspace = true, features = ["std"] } # but does not itself activate that feature on `bolero-engine`. bumpalo = { workspace = true, features = ["collections"] } criterion.workspace = true -gungraun = { workspace = true, features = ["default"] } mutants.workspace = true serde_json.workspace = true @@ -86,6 +91,9 @@ harness = false name = "criterion_drop" harness = false +# Callgrind benches require Linux (Valgrind). The bench files are gated to compile +# to a no-op on non-Linux targets, but the [[bench]] entry itself cannot be +# cfg-gated, so it is unconditional here. [[bench]] name = "gungraun_alloc" harness = false diff --git a/crates/multitude/benches/gungraun_alloc.rs b/crates/multitude/benches/gungraun_alloc/linux.rs similarity index 95% rename from crates/multitude/benches/gungraun_alloc.rs rename to crates/multitude/benches/gungraun_alloc/linux.rs index 0aed367b6..991d1fe3d 100644 --- a/crates/multitude/benches/gungraun_alloc.rs +++ b/crates/multitude/benches/gungraun_alloc/linux.rs @@ -1,26 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -//! Instruction-precise allocation benchmarks for multitude. -//! -//! Mirrors `benches/criterion_alloc.rs` 1:1: each gungraun function -//! `_` corresponds to a criterion benchmark -//! `/`. -//! -//! Run with `cargo bench --bench gungraun_alloc` on a Linux host with Valgrind. - -#![expect(missing_docs, reason = "Benchmark")] -#![allow(unused_results, reason = "black_box of bench input is intentional")] -#![allow( - clippy::needless_pass_by_value, - reason = "gungraun bench inputs are passed by value by the framework" -)] -#![allow(clippy::ref_as_ptr, reason = "trivial pointer cast in bench plumbing")] -#![allow(clippy::too_many_lines, reason = "benchmark file")] - use core::hint::black_box; -use gungraun::{Callgrind, LibraryBenchmarkConfig, library_benchmark, library_benchmark_group, main}; +use gungraun::{library_benchmark, library_benchmark_group}; use multitude::Arena; const N: usize = 1_000; @@ -687,9 +670,3 @@ library_benchmark_group!( alloc_vec, alloc_vec_with_capacity, vec_builder_bumpalo_grow, vec_builder_bumpalo_with_cap ); - -main!( - config = LibraryBenchmarkConfig::default() - .tool(Callgrind::with_args(["--branch-sim=yes"])); - library_benchmark_groups = alloc_group -); diff --git a/crates/multitude/benches/gungraun_alloc/main.rs b/crates/multitude/benches/gungraun_alloc/main.rs new file mode 100644 index 000000000..e98723fd3 --- /dev/null +++ b/crates/multitude/benches/gungraun_alloc/main.rs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Instruction-precise allocation benchmarks for multitude. +//! +//! Mirrors `benches/criterion_alloc.rs` 1:1: each gungraun function +//! `_` corresponds to a criterion benchmark +//! `/`. +//! +//! Run with `cargo bench --bench gungraun_alloc` on a Linux host with Valgrind. + +#![allow(missing_docs, reason = "Benchmark")] +#![allow(unused_results, reason = "black_box of bench input is intentional")] +#![allow( + clippy::needless_pass_by_value, + reason = "gungraun bench inputs are passed by value by the framework" +)] +#![allow(clippy::ref_as_ptr, reason = "trivial pointer cast in bench plumbing")] +#![allow(clippy::too_many_lines, reason = "benchmark file")] +#![cfg_attr( + target_os = "linux", + expect( + clippy::exit, + clippy::missing_docs_in_private_items, + unused_qualifications, + reason = "Triggered by Gungraun macro expansion. Upstream tracking issues are pending." + ) +)] + +// Gungraun requires Valgrind, which is Linux-only. On other platforms this +// bench target compiles to a no-op so `cargo build --all-targets` still works. +#[cfg(not(target_os = "linux"))] +fn main() {} + +#[cfg(target_os = "linux")] +mod linux; + +#[cfg(target_os = "linux")] +use linux::*; + +#[cfg(target_os = "linux")] +gungraun::main!( + config = gungraun::LibraryBenchmarkConfig::default() + .tool(gungraun::Callgrind::with_args(["--branch-sim=yes"])); + library_benchmark_groups = alloc_group +); diff --git a/crates/multitude/benches/gungraun_drop.rs b/crates/multitude/benches/gungraun_drop/linux.rs similarity index 90% rename from crates/multitude/benches/gungraun_drop.rs rename to crates/multitude/benches/gungraun_drop/linux.rs index e67563321..b8c08274f 100644 --- a/crates/multitude/benches/gungraun_drop.rs +++ b/crates/multitude/benches/gungraun_drop/linux.rs @@ -1,21 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -//! Instruction-precise drop benchmarks for multitude. -//! -//! Mirrors `benches/criterion_drop.rs` 1:1: each gungraun function -//! `drop_` corresponds to a criterion benchmark `drop/`. -//! Each setup pre-fills an arena with N handles; the bench body drops them -//! (handle vec + arena), measuring per-handle smart-pointer drop plus chunk -//! teardown at arena drop. - -#![expect(missing_docs, reason = "Benchmark")] -#![allow(unused_results, reason = "black_box of bench input is intentional")] -#![allow(clippy::too_many_lines, reason = "benchmark file")] - use core::hint::black_box; -use gungraun::{Callgrind, LibraryBenchmarkConfig, library_benchmark, library_benchmark_group, main}; +use gungraun::{library_benchmark, library_benchmark_group}; use multitude::strings::{ArcStr, BoxStr, RcStr}; use multitude::{Arc, Arena, Box, Rc}; @@ -290,9 +278,3 @@ library_benchmark_group!( drop_slice_box_droppy, drop_slice_rc_droppy, drop_slice_arc_droppy, drop_alloc ); - -main!( - config = LibraryBenchmarkConfig::default() - .tool(Callgrind::with_args(["--branch-sim=yes"])); - library_benchmark_groups = drop_group -); diff --git a/crates/multitude/benches/gungraun_drop/main.rs b/crates/multitude/benches/gungraun_drop/main.rs new file mode 100644 index 000000000..49dc4026f --- /dev/null +++ b/crates/multitude/benches/gungraun_drop/main.rs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Instruction-precise drop benchmarks for multitude. +//! +//! Mirrors `benches/criterion_drop.rs` 1:1: each gungraun function +//! `drop_` corresponds to a criterion benchmark `drop/`. +//! Each setup pre-fills an arena with N handles; the bench body drops them +//! (handle vec + arena), measuring per-handle smart-pointer drop plus chunk +//! teardown at arena drop. + +#![allow(missing_docs, reason = "Benchmark")] +#![allow(unused_results, reason = "black_box of bench input is intentional")] +#![allow(clippy::too_many_lines, reason = "benchmark file")] +#![cfg_attr( + target_os = "linux", + expect( + clippy::exit, + clippy::missing_docs_in_private_items, + unused_qualifications, + reason = "Triggered by Gungraun macro expansion. Upstream tracking issues are pending." + ) +)] + +// Gungraun requires Valgrind, which is Linux-only. On other platforms this +// bench target compiles to a no-op so `cargo build --all-targets` still works. +#[cfg(not(target_os = "linux"))] +fn main() {} + +#[cfg(target_os = "linux")] +mod linux; + +#[cfg(target_os = "linux")] +use linux::*; + +#[cfg(target_os = "linux")] +gungraun::main!( + config = gungraun::LibraryBenchmarkConfig::default() + .tool(gungraun::Callgrind::with_args(["--branch-sim=yes"])); + library_benchmark_groups = drop_group +); diff --git a/crates/multitude/src/arena/tests.rs b/crates/multitude/src/arena/tests.rs index 93895ec26..3885482d7 100644 --- a/crates/multitude/src/arena/tests.rs +++ b/crates/multitude/src/arena/tests.rs @@ -329,6 +329,27 @@ fn alloc_slice_local_with_or_panic_no_drop_fn_does_not_reserve_drop_entry() { ); } +/// Kills `inner_slice.rs:975:47 && -> ||` mutant in `try_alloc_slice_shared_with`. +/// With the mutation `drop_fn.is_some() || len != 0`, an empty slice of a Drop +/// type would erroneously reserve a drop entry. Verify `drop_back` stays put. +#[test] +fn alloc_slice_shared_with_or_panic_empty_drop_type_does_not_reserve_drop_entry() { + let arena = Arena::::new(); + let _ = arena.alloc_arc(0_u32); + let drop_back_before = arena.current_shared.drop_back.get(); + let raw = arena.alloc_slice_shared_with_or_panic::(0, Some(noop_drop_shim), |_, slot| { + slot.write(0); + }); + let drop_back_after = arena.current_shared.drop_back.get(); + // SAFETY: `raw` was just returned by the shared slice allocator which + // bumped the chunk's smart-pointer refcount by 1 for us. + let _arc: crate::Arc<[u8], Global> = unsafe { crate::Arc::from_value_ptr(raw) }; + assert_eq!( + drop_back_before, drop_back_after, + "drop_back must not retreat for empty slice even with drop_fn" + ); +} + /// Kills `arena.rs:3603:47 && -> ||` mutant — shared-flavor sibling of /// the previous test. #[test] diff --git a/justfiles/setup.just b/justfiles/setup.just index 7118eee61..8959303f8 100644 --- a/justfiles/setup.just +++ b/justfiles/setup.just @@ -38,3 +38,6 @@ install-tools: # This tool is not well maintained and fails to actually build if using locked dependencies. Okay then. cargo install cargo-spellcheck@$env:CARGO_SPELLCHECK_VERSION + + # Install platform-specific benchmark toolchain (Callgrind, Linux-only). + & (Join-Path $PSScriptRoot ".." "scripts" "install-callgrind-tools.ps1") diff --git a/scripts/install-callgrind-tools.ps1 b/scripts/install-callgrind-tools.ps1 new file mode 100644 index 000000000..201e3f56b --- /dev/null +++ b/scripts/install-callgrind-tools.ps1 @@ -0,0 +1,24 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +# Installs the Callgrind benchmark toolchain (gungraun-runner). Linux-only: +# Callgrind requires Valgrind, which is Linux-only. The runner is the helper +# binary that Callgrind bench binaries (built with `harness = false`) hand +# their work off to via an encoded payload. +# +# Keep the version in lockstep with the `gungraun` workspace dep in +# Cargo.toml and the constants.env file. +# `gungraun-runner` enforces strict string equality on the version +# (`gungraun-runner::runner::compare_versions`), so any patch-level drift +# between the library and the runner causes `*_cg` benches to fail at runtime +# with VersionMismatch. + +$ErrorActionPreference = "Stop" +$PSNativeCommandUseErrorActionPreference = $true + +if (-not $IsLinux) { + Write-Host "Callgrind toolchain is Linux-only; skipping." + return +} + +cargo install --locked gungraun-runner --version $env:GUNGRAUN_RUNNER_VERSION