From 847740e0c83c912c26a73c6e3faf53778f6ba7f2 Mon Sep 17 00:00:00 2001 From: mattsu Date: Sat, 20 Dec 2025 14:31:47 +0900 Subject: [PATCH 1/3] feat(timeout): add benchmarking support with divan Add dev-dependencies for divan and uucore benchmarking features, and configure a new bench target for timeout command performance testing. This enables automated benchmarking to track and optimize execution times. --- Cargo.lock | 1 + src/uu/timeout/Cargo.toml | 8 ++ src/uu/timeout/benches/timeout_bench.rs | 115 ++++++++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 src/uu/timeout/benches/timeout_bench.rs diff --git a/Cargo.lock b/Cargo.lock index fe230f9db57..f2952b2f104 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3976,6 +3976,7 @@ name = "uu_timeout" version = "0.4.0" dependencies = [ "clap", + "codspeed-divan-compat", "fluent", "libc", "nix", diff --git a/src/uu/timeout/Cargo.toml b/src/uu/timeout/Cargo.toml index c6b795628f7..cb87a6b70e9 100644 --- a/src/uu/timeout/Cargo.toml +++ b/src/uu/timeout/Cargo.toml @@ -27,3 +27,11 @@ fluent = { workspace = true } [[bin]] name = "timeout" path = "src/main.rs" + +[dev-dependencies] +divan = { workspace = true } +uucore = { workspace = true, features = ["benchmark"] } + +[[bench]] +name = "timeout_bench" +harness = false diff --git a/src/uu/timeout/benches/timeout_bench.rs b/src/uu/timeout/benches/timeout_bench.rs new file mode 100644 index 00000000000..3ed0e5dc6e9 --- /dev/null +++ b/src/uu/timeout/benches/timeout_bench.rs @@ -0,0 +1,115 @@ +// This file is part of the uutils coreutils package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use std::env; +use std::process; +use std::time::Duration; + +const CHILD_FLAG: &str = "--timeout-bench-child"; + +fn maybe_run_child_mode() { + let mut args = env::args(); + let _ = args.next(); // skip executable path + + while let Some(arg) = args.next() { + if arg == CHILD_FLAG { + let mode = args + .next() + .unwrap_or_else(|| panic!("missing child mode after {CHILD_FLAG}")); + run_child(mode); + } + } +} + +#[cfg(unix)] +fn run_child(mode: String) -> ! { + match mode.as_str() { + "quick-exit" => process::exit(0), + "short-sleep" => { + std::thread::sleep(Duration::from_millis(5)); + process::exit(0); + } + "long-sleep" => { + std::thread::sleep(Duration::from_millis(200)); + process::exit(0); + } + "ignore-term" => { + use nix::sys::signal::{SigHandler, Signal, signal}; + + unsafe { + signal(Signal::SIGTERM, SigHandler::SigIgn) + .expect("failed to ignore SIGTERM in bench child"); + } + + loop { + std::thread::sleep(Duration::from_millis(100)); + } + } + other => { + eprintln!("unknown child mode: {other}"); + process::exit(1); + } + } +} + +#[cfg(not(unix))] +fn run_child(_: String) -> ! { + // The timeout benchmarks are Unix-only, but ensure child invocations still terminate. + process::exit(0); +} + +#[cfg(unix)] +mod unix { + use super::*; + use divan::{Bencher, black_box}; + use uu_timeout::uumain; + use uucore::benchmark::run_util_function; + + fn bench_timeout_with_mode(bencher: Bencher, args: &[&str], child_mode: &str) { + let child_path = env::current_exe() + .expect("failed to locate timeout bench executable") + .into_os_string() + .into_string() + .expect("bench executable path must be valid UTF-8"); + + let mut owned_args: Vec = args.iter().map(|s| (*s).to_string()).collect(); + owned_args.push(child_path); + owned_args.push(CHILD_FLAG.into()); + owned_args.push(child_mode.to_string()); + + let arg_refs: Vec<&str> = owned_args.iter().map(|s| s.as_str()).collect(); + + bencher.bench(|| { + black_box(run_util_function(uumain, &arg_refs)); + }); + } + + /// Benchmark the fast path where the command exits immediately. + #[divan::bench] + fn timeout_quick_exit(bencher: Bencher) { + bench_timeout_with_mode(bencher, &["0.02"], "quick-exit"); + } + + /// Benchmark a command that runs longer than the threshold and receives the default signal. + #[divan::bench] + fn timeout_enforced(bencher: Bencher) { + bench_timeout_with_mode(bencher, &["0.02"], "long-sleep"); + } + + pub fn run() { + divan::main(); + } +} + +#[cfg(unix)] +fn main() { + maybe_run_child_mode(); + unix::run(); +} + +#[cfg(not(unix))] +fn main() { + maybe_run_child_mode(); +} From e3121492bc0bd5110c217ff324bb04e532ca5fb8 Mon Sep 17 00:00:00 2001 From: mattsu Date: Mon, 22 Dec 2025 21:59:48 +0900 Subject: [PATCH 2/3] refactor(timeout/benches): simplify unix-specific benchmark structure Remove the unnecessary `mod unix` block and use `#[cfg(unix)]` attributes on imports, functions, and benchmarks. Adjust `main()` to conditionally call `divan::main()` for Unix platforms. This refactoring improves code readability and reduces module nesting without changing functionality. --- src/uu/timeout/benches/timeout_bench.rs | 77 +++++++++++-------------- 1 file changed, 35 insertions(+), 42 deletions(-) diff --git a/src/uu/timeout/benches/timeout_bench.rs b/src/uu/timeout/benches/timeout_bench.rs index 3ed0e5dc6e9..9e96b1d9593 100644 --- a/src/uu/timeout/benches/timeout_bench.rs +++ b/src/uu/timeout/benches/timeout_bench.rs @@ -61,55 +61,48 @@ fn run_child(_: String) -> ! { } #[cfg(unix)] -mod unix { - use super::*; - use divan::{Bencher, black_box}; - use uu_timeout::uumain; - use uucore::benchmark::run_util_function; - - fn bench_timeout_with_mode(bencher: Bencher, args: &[&str], child_mode: &str) { - let child_path = env::current_exe() - .expect("failed to locate timeout bench executable") - .into_os_string() - .into_string() - .expect("bench executable path must be valid UTF-8"); - - let mut owned_args: Vec = args.iter().map(|s| (*s).to_string()).collect(); - owned_args.push(child_path); - owned_args.push(CHILD_FLAG.into()); - owned_args.push(child_mode.to_string()); - - let arg_refs: Vec<&str> = owned_args.iter().map(|s| s.as_str()).collect(); - - bencher.bench(|| { - black_box(run_util_function(uumain, &arg_refs)); - }); - } - - /// Benchmark the fast path where the command exits immediately. - #[divan::bench] - fn timeout_quick_exit(bencher: Bencher) { - bench_timeout_with_mode(bencher, &["0.02"], "quick-exit"); - } +use divan::{Bencher, black_box}; +#[cfg(unix)] +use uu_timeout::uumain; +#[cfg(unix)] +use uucore::benchmark::run_util_function; - /// Benchmark a command that runs longer than the threshold and receives the default signal. - #[divan::bench] - fn timeout_enforced(bencher: Bencher) { - bench_timeout_with_mode(bencher, &["0.02"], "long-sleep"); - } +#[cfg(unix)] +fn bench_timeout_with_mode(bencher: Bencher, args: &[&str], child_mode: &str) { + let child_path = env::current_exe() + .expect("failed to locate timeout bench executable") + .into_os_string() + .into_string() + .expect("bench executable path must be valid UTF-8"); + + let mut owned_args: Vec = args.iter().map(|s| (*s).to_string()).collect(); + owned_args.push(child_path); + owned_args.push(CHILD_FLAG.into()); + owned_args.push(child_mode.to_string()); + + let arg_refs: Vec<&str> = owned_args.iter().map(|s| s.as_str()).collect(); + + bencher.bench(|| { + black_box(run_util_function(uumain, &arg_refs)); + }); +} - pub fn run() { - divan::main(); - } +/// Benchmark the fast path where the command exits immediately. +#[cfg(unix)] +#[divan::bench] +fn timeout_quick_exit(bencher: Bencher) { + bench_timeout_with_mode(bencher, &["0.02"], "quick-exit"); } +/// Benchmark a command that runs longer than the threshold and receives the default signal. #[cfg(unix)] -fn main() { - maybe_run_child_mode(); - unix::run(); +#[divan::bench] +fn timeout_enforced(bencher: Bencher) { + bench_timeout_with_mode(bencher, &["0.02"], "long-sleep"); } -#[cfg(not(unix))] fn main() { maybe_run_child_mode(); + #[cfg(unix)] + divan::main(); } From 52cd47f7e987f55bf662296e052d9c35b375ec20 Mon Sep 17 00:00:00 2001 From: mattsu Date: Wed, 24 Dec 2025 19:10:59 +0900 Subject: [PATCH 3/3] ci: add uu_timeout to benchmarks Include benchmarking for the uu_timeout utility to ensure performance tracking alongside other tools in the suite. --- .github/workflows/benchmarks.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 205f6c1a24e..ada64746656 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -40,7 +40,8 @@ jobs: - { package: uu_shuf } - { package: uu_sort } - { package: uu_split } - - { package: uu_tsort } + - { package: uu_timeout } + - { package: uu_tsort } - { package: uu_unexpand } - { package: uu_uniq } - { package: uu_wc }